Include this in scope analysis

This commit is contained in:
Andrew Morris
2023-07-21 15:27:02 +10:00
parent fcdebb7b9e
commit fd116d06a2
4 changed files with 146 additions and 48 deletions

View File

@@ -0,0 +1,20 @@
pub struct Ident {
pub sym: swc_atoms::JsWord,
pub span: swc_common::Span,
}
impl Ident {
pub fn from_swc_ident(ident: &swc_ecma_ast::Ident) -> Self {
Ident {
sym: ident.sym.clone(),
span: ident.span,
}
}
pub fn this(span: swc_common::Span) -> Self {
Ident {
sym: swc_atoms::JsWord::from("this"),
span,
}
}
}

View File

@@ -8,6 +8,7 @@ mod diagnostic;
mod expression_compiler;
mod function_compiler;
mod gather_modules;
mod ident;
mod import_pattern;
mod instruction;
mod link_module;

View File

@@ -9,7 +9,8 @@ use crate::{asm::Builtin, constants::CONSTANTS};
#[derive(Hash, PartialEq, Eq, Clone, Debug, PartialOrd, Ord)]
pub enum NameId {
Span(swc_common::Span),
Span(swc_common::Span), // Original identifier
This(swc_common::Span), // owner id span
Builtin(Builtin),
Constant(&'static str),
}
@@ -17,7 +18,7 @@ pub enum NameId {
impl Spanned for NameId {
fn span(&self) -> swc_common::Span {
match self {
NameId::Span(span) => *span,
NameId::Span(span) | NameId::This(span) => *span,
NameId::Builtin(_) => swc_common::DUMMY_SP,
NameId::Constant(_) => swc_common::DUMMY_SP,
}
@@ -73,18 +74,21 @@ impl ScopeTrait for Scope {
if old_mapping.is_some() {
diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
message: "Scope overwrite occurred (TODO: being permissive about this)".to_string(),
message: format!(
"Scope overwrite of `{}` occurred (TODO: being permissive about this)",
name
),
span,
});
}
}
fn nest(&self, name_owner_location: Option<OwnerId>) -> Rc<RefCell<ScopeData>> {
return Rc::new(RefCell::new(ScopeData {
Rc::new(RefCell::new(ScopeData {
owner_id: name_owner_location.unwrap_or(self.borrow().owner_id.clone()),
name_map: Default::default(),
parent: Some(self.clone()),
}));
}))
}
}

View File

@@ -9,6 +9,7 @@ use valuescript_common::BUILTIN_NAMES;
use crate::{
asm::{Builtin, Register, Value},
constants::CONSTANTS,
ident::Ident,
name_allocator::{PointerAllocator, RegAllocator},
scope::{init_std_scope, NameId, OwnerId, Scope, ScopeTrait},
};
@@ -30,6 +31,7 @@ pub enum NameType {
Var,
Let,
Const,
This,
Param,
Function,
Class,
@@ -177,16 +179,19 @@ impl ScopeAnalysis {
scope: &Scope,
type_: NameType,
value: Value,
origin_ident: &swc_ecma_ast::Ident,
origin_ident: &Ident,
tdz_end: Option<swc_common::BytePos>,
) {
let name = Name {
id: NameId::Span(origin_ident.span),
id: match type_ {
NameType::This => NameId::This(origin_ident.span),
_ => NameId::Span(origin_ident.span),
},
owner_id: scope.borrow().owner_id.clone(),
sym: origin_ident.sym.clone(),
type_,
effectively_const: match type_ {
NameType::Var | NameType::Let | NameType::Param => false,
NameType::Var | NameType::Let | NameType::Param | NameType::This => false,
NameType::Const
| NameType::Enum
| NameType::Function
@@ -202,14 +207,17 @@ impl ScopeAnalysis {
};
self.names.insert(name.id.clone(), name.clone());
self.refs.insert(
origin_ident.span,
Ref {
name_id: name.id.clone(),
owner_id: scope.borrow().owner_id.clone(),
span: origin_ident.span,
},
);
if type_ != NameType::This {
self.refs.insert(
origin_ident.span,
Ref {
name_id: name.id.clone(),
owner_id: scope.borrow().owner_id.clone(),
span: origin_ident.span,
},
);
}
self
.owners
@@ -232,21 +240,31 @@ impl ScopeAnalysis {
origin_ident: &swc_ecma_ast::Ident,
) {
match scope.get(&origin_ident.sym) {
None => {
let pointer = Value::Pointer(self.pointer_allocator.allocate(&origin_ident.sym));
self.insert_name(scope, type_, pointer, origin_ident, None);
}
None => {}
Some(name_id) => {
let name = self.names.get(&name_id).expect("Name not found");
match &name.value {
Value::Pointer(_) => {}
_ => {
panic!("Expected pointer value for name: {:?}", name);
if name.type_ != NameType::This {
match &name.value {
// Already inserted, skip
Value::Pointer(_) => return,
_ => {
panic!("Expected pointer value for name: {:?}", name);
}
}
}
}
}
let pointer = Value::Pointer(self.pointer_allocator.allocate(&origin_ident.sym));
self.insert_name(
scope,
type_,
pointer,
&Ident::from_swc_ident(origin_ident),
None,
);
}
fn insert_reg_name(
@@ -257,7 +275,24 @@ impl ScopeAnalysis {
tdz_end: Option<swc_common::BytePos>,
) {
let reg = Value::Register(self.allocate_reg(&scope.borrow().owner_id, &origin_ident.sym));
self.insert_name(scope, type_, reg, origin_ident, tdz_end);
self.insert_name(
scope,
type_,
reg,
&Ident::from_swc_ident(origin_ident),
tdz_end,
);
}
fn insert_this_name(&mut self, scope: &Scope, owner_span: swc_common::Span) {
self.insert_name(
scope,
NameType::This,
Value::Register(Register::this()),
&Ident::this(owner_span),
None,
);
}
fn insert_capture(&mut self, captor_id: &OwnerId, name_id: &NameId, ref_: swc_common::Span) {
@@ -392,7 +427,7 @@ impl ScopeAnalysis {
}
match &named_specifier.orig {
ModuleExportName::Ident(ident) => self.ident(scope, ident),
ModuleExportName::Ident(ident) => self.ident(scope, &Ident::from_swc_ident(ident)),
ModuleExportName::Str(_) => self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::InternalError,
message: "TODO: ModuleExportName::Str".to_string(),
@@ -400,9 +435,11 @@ impl ScopeAnalysis {
}),
}
}
Default(default_specifier) => self.ident(scope, &default_specifier.exported),
Default(default_specifier) => {
self.ident(scope, &Ident::from_swc_ident(&default_specifier.exported))
}
Namespace(namespace_specifier) => match &namespace_specifier.name {
ModuleExportName::Ident(ident) => self.ident(scope, ident),
ModuleExportName::Ident(ident) => self.ident(scope, &Ident::from_swc_ident(ident)),
ModuleExportName::Str(_) => self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::InternalError,
message: "TODO: ModuleExportName::Str".to_string(),
@@ -463,13 +500,20 @@ impl ScopeAnalysis {
name: &Option<swc_ecma_ast::Ident>,
function: &swc_ecma_ast::Function,
) {
let child_scope = scope.nest(Some(fn_to_owner_id(name, function)));
let owner_span = fn_owner_span(name, function);
let child_scope = scope.nest(Some(OwnerId::Span(owner_span)));
self.insert_this_name(&child_scope, owner_span);
if let Some(name) = name {
self.insert_pointer_name(&child_scope, NameType::Function, name);
}
for param in &function.params {
if is_this_pat(&param.pat) {
continue;
}
self.param_pat(&child_scope, &param.pat);
}
@@ -880,6 +924,7 @@ impl ScopeAnalysis {
class_: &swc_ecma_ast::Class,
) {
let child_scope = scope.nest(Some(OwnerId::Span(class_.span)));
self.insert_this_name(&child_scope, class_.span);
if let Some(ident) = ident {
self.insert_pointer_name(&child_scope, NameType::Class, ident);
@@ -963,9 +1008,11 @@ impl ScopeAnalysis {
use swc_ecma_ast::Expr;
match expr {
Expr::This(_) => {}
Expr::This(this) => {
self.ident(scope, &Ident::this(this.span));
}
Expr::Ident(ident) => {
self.ident(scope, ident);
self.ident(scope, &Ident::from_swc_ident(ident));
}
Expr::Lit(_) => {}
Expr::Array(array) => {
@@ -1175,7 +1222,7 @@ impl ScopeAnalysis {
match pat {
Pat::Ident(ident) => {
self.ident(scope, &ident.id);
self.ident(scope, &Ident::from_swc_ident(&ident.id));
}
Pat::Array(array) => {
for elem in array.elems.iter().flatten() {
@@ -1193,7 +1240,7 @@ impl ScopeAnalysis {
self.pat(scope, &key_value.value);
}
swc_ecma_ast::ObjectPatProp::Assign(assign) => {
self.ident(scope, &assign.key);
self.ident(scope, &Ident::from_swc_ident(&assign.key));
if let Some(value) = &assign.value {
self.expr(scope, value);
@@ -1223,7 +1270,7 @@ impl ScopeAnalysis {
match expr {
Expr::Ident(ident) => {
self.mutate_ident(scope, ident, optional);
self.mutate_ident(scope, &Ident::from_swc_ident(ident), optional);
}
Expr::Member(member) => {
self.mutate_expr(scope, &member.obj, optional);
@@ -1307,8 +1354,8 @@ impl ScopeAnalysis {
});
}
Expr::This(_) => {
// TODO: Add capture+mutation analysis for `this`.
Expr::This(this) => {
self.mutate_ident(scope, &Ident::this(this.span), optional);
}
Expr::Array(array) => {
diagnostic = Some(Diagnostic {
@@ -1464,7 +1511,7 @@ impl ScopeAnalysis {
match pat {
Pat::Ident(ident) => {
self.mutate_ident(scope, &ident.id, false);
self.mutate_ident(scope, &Ident::from_swc_ident(&ident.id), false);
}
Pat::Array(array_pat) => {
for elem in array_pat.elems.iter().flatten() {
@@ -1483,7 +1530,7 @@ impl ScopeAnalysis {
swc_ecma_ast::ObjectPatProp::Assign(assign) => {
// TODO: How is `({ y: x = 3 } = { y: 4 })` handled?
self.mutate_ident(scope, &assign.key, false);
self.mutate_ident(scope, &Ident::from_swc_ident(&assign.key), false);
if let Some(value) = &assign.value {
// Note: Generally mutate_* only processes the mutation aspect
@@ -1517,7 +1564,7 @@ impl ScopeAnalysis {
}
}
fn mutate_ident(&mut self, scope: &Scope, ident: &swc_ecma_ast::Ident, optional: bool) {
fn mutate_ident(&mut self, scope: &Scope, ident: &Ident, optional: bool) {
let name_id = match scope.get(&ident.sym) {
Some(name_id) => name_id,
None => {
@@ -1559,7 +1606,7 @@ impl ScopeAnalysis {
);
}
fn ident(&mut self, scope: &Scope, ident: &swc_ecma_ast::Ident) {
fn ident(&mut self, scope: &Scope, ident: &Ident) {
let name_id = match scope.get(&ident.sym) {
Some(name_id) => name_id,
None => {
@@ -1618,7 +1665,7 @@ impl ScopeAnalysis {
match prop_or_spread {
PropOrSpread::Prop(prop) => match &**prop {
swc_ecma_ast::Prop::Shorthand(ident) => {
self.ident(scope, ident);
self.ident(scope, &Ident::from_swc_ident(ident));
}
swc_ecma_ast::Prop::KeyValue(key_value) => {
self.prop_key(scope, &key_value.key);
@@ -1891,10 +1938,10 @@ impl ScopeAnalysis {
span: *span,
});
}
NameId::Builtin(_) | NameId::Constant(_) => {
NameId::This(_) | NameId::Builtin(_) | NameId::Constant(_) => {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::InternalError,
message: "Builtin/constant should not have type_ let".to_string(),
message: "Builtin/constant/this should not have type_ let".to_string(),
span: swc_common::DUMMY_SP,
});
}
@@ -1930,6 +1977,15 @@ impl ScopeAnalysis {
if name.type_ == NameType::Function {
match name_id {
NameId::Span(span) => Some(OwnerId::Span(*span)),
NameId::This(_) => {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::InternalError,
message: "NameId::This should not be associated with NameType::Function".to_string(),
span: swc_common::DUMMY_SP,
});
None
}
NameId::Builtin(_) | NameId::Constant(_) => None,
}
} else {
@@ -2070,7 +2126,7 @@ impl ScopeAnalysis {
}
let name_span = match &ref_.name_id {
NameId::Span(span) => *span,
NameId::Span(span) | NameId::This(span) => *span,
NameId::Builtin(_) | NameId::Constant(_) => continue,
};
@@ -2145,12 +2201,29 @@ fn is_declare(decl: &swc_ecma_ast::Decl) -> bool {
}
}
pub fn fn_owner_span(
name: &Option<swc_ecma_ast::Ident>,
function: &swc_ecma_ast::Function,
) -> swc_common::Span {
match name {
Some(name) => name.span,
None => function.span,
}
}
pub fn fn_to_owner_id(
name: &Option<swc_ecma_ast::Ident>,
function: &swc_ecma_ast::Function,
) -> OwnerId {
OwnerId::Span(match name {
Some(name) => name.span,
None => function.span,
})
OwnerId::Span(fn_owner_span(name, function))
}
fn is_this_pat(pat: &swc_ecma_ast::Pat) -> bool {
if let swc_ecma_ast::Pat::Ident(bi) = pat {
if bi.id.sym == *"this" {
return true;
}
}
false
}