Fix capturing for function declarations

This commit is contained in:
Andrew Morris
2023-03-24 16:25:27 +11:00
parent a7ced2eb76
commit 36e0b0d2cc
5 changed files with 146 additions and 57 deletions

View File

@@ -0,0 +1,11 @@
// test_output! 3
export default function () {
const x = 3;
function foo() {
return x;
}
return foo();
}

View File

@@ -4,10 +4,10 @@ use queues::*;
use swc_common::Spanned;
use crate::asm::{Array, Instruction, Label, Object, Pointer, Register, Value};
use crate::asm::{Array, Instruction, Label, Object, Register, Value};
use crate::diagnostic::{Diagnostic, DiagnosticLevel};
use crate::function_compiler::{FunctionCompiler, Functionish, QueuedFunction};
use crate::scope_analysis::{NameId, OwnerId};
use crate::scope_analysis::{fn_to_owner_id, NameId, NameType, OwnerId};
pub struct CompiledExpression {
/** It is usually better to access this via functionCompiler.use_ */
@@ -318,7 +318,7 @@ impl<'a> ExpressionCompiler<'a> {
}
fn get_register_for_ident_mutation(&mut self, ident: &swc_ecma_ast::Ident) -> Register {
let (reg, err_msg) = match self.fnc.lookup(ident) {
let (reg, err_msg) = match self.fnc.lookup_value(ident) {
Some(Value::Register(reg)) => (Some(reg), None),
lookup_result => (
None,
@@ -1031,7 +1031,7 @@ impl<'a> ExpressionCompiler<'a> {
.fnc
.scope_analysis
.captures
.get(&OwnerId::Span(fn_.function.span))
.get(&fn_to_owner_id(&fn_.ident, &fn_.function))
.cloned();
self
@@ -1040,7 +1040,7 @@ impl<'a> ExpressionCompiler<'a> {
.add(QueuedFunction {
definition_pointer: definition_pointer.clone(),
fn_name: fn_name.clone(),
functionish: Functionish::Fn(fn_.function.clone()),
functionish: Functionish::Fn(fn_.ident.clone(), fn_.function.clone()),
})
.expect("Failed to queue function");
@@ -1048,7 +1048,7 @@ impl<'a> ExpressionCompiler<'a> {
None => self.inline(Value::Pointer(definition_pointer), target_register),
Some(capture_params) => self.capturing_fn_ref(
fn_name,
&definition_pointer,
&Value::Pointer(definition_pointer),
&capture_params,
target_register,
),
@@ -1081,16 +1081,19 @@ impl<'a> ExpressionCompiler<'a> {
match capture_params {
None => self.inline(Value::Pointer(definition_pointer), target_register),
Some(capture_params) => {
self.capturing_fn_ref(None, &definition_pointer, &capture_params, target_register)
}
Some(capture_params) => self.capturing_fn_ref(
None,
&Value::Pointer(definition_pointer),
&capture_params,
target_register,
),
}
}
pub fn capturing_fn_ref(
&mut self,
fn_name: Option<String>,
definition_pointer: &Pointer,
fn_value: &Value,
captures: &HashSet<NameId>,
target_register: Option<Register>,
) -> CompiledExpression {
@@ -1113,25 +1116,27 @@ impl<'a> ExpressionCompiler<'a> {
let mut bind_values = Array::default();
for cap in captures {
bind_values.values.push(match self.fnc.lookup_name_id(cap) {
Some(v) => match v {
Value::Register(_) => v,
_ => continue,
},
None => {
self.fnc.diagnostics.push(Diagnostic {
level: DiagnosticLevel::InternalError,
message: format!("Failed to find capture {:?}", cap),
span: cap.span(),
});
bind_values
.values
.push(match self.fnc.lookup_by_name_id(cap) {
Some(v) => match v {
Value::Register(_) => v,
_ => continue,
},
None => {
self.fnc.diagnostics.push(Diagnostic {
level: DiagnosticLevel::InternalError,
message: format!("Failed to find capture {:?}", cap),
span: cap.span(),
});
continue;
}
});
continue;
}
});
}
self.fnc.push(Instruction::Bind(
Value::Pointer(definition_pointer.clone()),
fn_value.clone(),
Value::Array(Box::new(bind_values)),
reg.clone(),
));
@@ -1237,7 +1242,29 @@ impl<'a> ExpressionCompiler<'a> {
return self.inline(Value::Undefined, target_register);
}
let value = match self.fnc.lookup(ident) {
let fn_as_owner_id = match self.fnc.scope_analysis.lookup(ident) {
Some(name) => match name.type_ == NameType::Function {
true => match name.id {
// TODO: This is a bit of a hack, it might break...
// functions have an owner id, and the name id should
// have the same span... at least it does now
NameId::Span(span) => Some(OwnerId::Span(span)),
_ => None, // Internal error?
},
false => None,
},
_ => {
self.fnc.diagnostics.push(Diagnostic {
level: DiagnosticLevel::InternalError,
message: format!("Failed to lookup identifier `{}`", ident.sym),
span: ident.span,
});
None
}
};
let value = match self.fnc.lookup_value(ident) {
Some(v) => v, // TODO: Capturing functions
None => {
self.fnc.diagnostics.push(Diagnostic {
@@ -1254,7 +1281,22 @@ impl<'a> ExpressionCompiler<'a> {
}
};
self.inline(value, target_register)
match fn_as_owner_id {
Some(owner_id) => {
let capture_params = self.fnc.scope_analysis.captures.get(&owner_id).cloned();
match capture_params {
Some(capture_params) => self.capturing_fn_ref(
Some(ident.sym.to_string()),
&value,
&capture_params,
target_register,
),
None => self.inline(value, target_register),
}
}
None => self.inline(value, target_register),
}
}
pub fn compile_literal(&mut self, lit: &swc_ecma_ast::Lit) -> Value {
@@ -1532,7 +1574,7 @@ impl TargetAccessor {
use swc_ecma_ast::Expr::*;
return match expr {
Ident(ident) => match ec.fnc.lookup(ident) {
Ident(ident) => match ec.fnc.lookup_value(ident) {
Some(Value::Register(_)) => true,
_ => false,
},

View File

@@ -13,11 +13,11 @@ use crate::diagnostic::{Diagnostic, DiagnosticLevel};
use crate::expression_compiler::CompiledExpression;
use crate::expression_compiler::ExpressionCompiler;
use crate::name_allocator::{NameAllocator, RegAllocator};
use crate::scope_analysis::{NameId, OwnerId, ScopeAnalysis};
use crate::scope_analysis::{fn_to_owner_id, NameId, OwnerId, ScopeAnalysis};
#[derive(Clone, Debug)]
pub enum Functionish {
Fn(swc_ecma_ast::Function),
Fn(Option<swc_ecma_ast::Ident>, swc_ecma_ast::Function),
Arrow(swc_ecma_ast::ArrowExpr),
Constructor(
Vec<InstructionOrLabel>,
@@ -29,7 +29,7 @@ pub enum Functionish {
impl Spanned for Functionish {
fn span(&self) -> swc_common::Span {
match self {
Functionish::Fn(fn_) => fn_.span,
Functionish::Fn(_, fn_) => fn_.span,
Functionish::Arrow(arrow) => arrow.span,
Functionish::Constructor(_, class_span, _) => *class_span,
}
@@ -38,7 +38,10 @@ impl Spanned for Functionish {
impl Functionish {
pub fn owner_id(&self) -> OwnerId {
OwnerId::Span(self.span().clone())
match self {
Functionish::Fn(ident, fn_) => fn_to_owner_id(ident, fn_),
_ => OwnerId::Span(self.span().clone()),
}
}
}
@@ -117,12 +120,14 @@ impl FunctionCompiler {
self.current.body.push(InstructionOrLabel::Label(label));
}
pub fn lookup(&self, ident: &swc_ecma_ast::Ident) -> Option<Value> {
self.scope_analysis.lookup(&self.owner_id, ident)
pub fn lookup_value(&self, ident: &swc_ecma_ast::Ident) -> Option<Value> {
self.scope_analysis.lookup_value(&self.owner_id, ident)
}
pub fn lookup_name_id(&self, name_id: &NameId) -> Option<Value> {
self.scope_analysis.lookup_name_id(&self.owner_id, name_id)
pub fn lookup_by_name_id(&self, name_id: &NameId) -> Option<Value> {
self
.scope_analysis
.lookup_by_name_id(&self.owner_id, name_id)
}
pub fn todo(&mut self, span: swc_common::Span, message: &str) {
@@ -256,7 +261,7 @@ impl FunctionCompiler {
self.add_param_code(functionish, &param_registers);
match functionish {
Functionish::Fn(fn_) => {
Functionish::Fn(_, fn_) => {
match &fn_.body {
Some(block) => {
self.handle_block_body(block);
@@ -316,7 +321,7 @@ impl FunctionCompiler {
let mut param_registers = Vec::<Register>::new();
match functionish {
Functionish::Fn(fn_) => {
Functionish::Fn(_, fn_) => {
for p in &fn_.params {
param_registers.push(self.get_pattern_register(&p.pat));
}
@@ -359,7 +364,7 @@ impl FunctionCompiler {
}
pub fn get_variable_register(&mut self, ident: &swc_ecma_ast::Ident) -> Register {
match self.scope_analysis.lookup(&self.owner_id, ident) {
match self.scope_analysis.lookup_value(&self.owner_id, ident) {
Some(Value::Register(reg)) => reg,
lookup_result => {
self.diagnostics.push(Diagnostic {
@@ -379,7 +384,7 @@ impl FunctionCompiler {
fn add_param_code(&mut self, functionish: &Functionish, param_registers: &Vec<Register>) {
match functionish {
Functionish::Fn(fn_) => {
Functionish::Fn(_, fn_) => {
for (i, p) in fn_.params.iter().enumerate() {
let mut ec = ExpressionCompiler { fnc: self };
ec.pat(&p.pat, &param_registers[i], false);
@@ -1043,7 +1048,7 @@ impl FunctionCompiler {
self
.queue
.add(QueuedFunction {
definition_pointer: match self.lookup(&fn_decl.ident) {
definition_pointer: match self.lookup_value(&fn_decl.ident) {
Some(Value::Pointer(p)) => p,
_ => {
self.diagnostics.push(Diagnostic {
@@ -1051,7 +1056,7 @@ impl FunctionCompiler {
message: format!(
"Lookup of function {} was not a pointer, lookup_result: {:?}",
fn_decl.ident.sym,
self.lookup(&fn_decl.ident)
self.lookup_value(&fn_decl.ident)
),
span: fn_decl.ident.span,
});
@@ -1060,7 +1065,7 @@ impl FunctionCompiler {
}
},
fn_name: Some(fn_decl.ident.sym.to_string()),
functionish: Functionish::Fn(fn_decl.function.clone()),
functionish: Functionish::Fn(Some(fn_decl.ident.clone()), fn_decl.function.clone()),
})
.expect("Failed to add function to queue");
}

View File

@@ -254,7 +254,10 @@ impl ModuleCompiler {
fn compile_fn_decl(&mut self, export: bool, fn_: &swc_ecma_ast::FnDecl) {
let fn_name = fn_.ident.sym.to_string();
let pointer = match self.scope_analysis.lookup(&OwnerId::Module, &fn_.ident) {
let pointer = match self
.scope_analysis
.lookup_value(&OwnerId::Module, &fn_.ident)
{
Some(Value::Pointer(p)) => p,
_ => {
self.diagnostics.push(Diagnostic {
@@ -277,7 +280,7 @@ impl ModuleCompiler {
let mut fn_defns = self.compile_fn(
pointer,
Some(fn_name),
Functionish::Fn(fn_.function.clone()),
Functionish::Fn(Some(fn_.ident.clone()), fn_.function.clone()),
);
self.module.definitions.append(&mut fn_defns);
@@ -296,7 +299,7 @@ impl ModuleCompiler {
Some(ident) => {
let fn_name = ident.sym.to_string();
let defn = match self.scope_analysis.lookup(&OwnerId::Module, ident) {
let defn = match self.scope_analysis.lookup_value(&OwnerId::Module, ident) {
Some(Value::Pointer(p)) => p,
_ => {
self.diagnostics.push(Diagnostic {
@@ -316,7 +319,11 @@ impl ModuleCompiler {
self.module.export_default = Value::Pointer(defn.clone());
let mut fn_defns = self.compile_fn(defn, fn_name, Functionish::Fn(fn_.function.clone()));
let mut fn_defns = self.compile_fn(
defn,
fn_name,
Functionish::Fn(fn_.ident.clone(), fn_.function.clone()),
);
self.module.definitions.append(&mut fn_defns);
}
@@ -408,7 +415,10 @@ impl ModuleCompiler {
Some(defn)
}
None => match self.scope_analysis.lookup(&OwnerId::Module, &orig_name) {
None => match self
.scope_analysis
.lookup_value(&OwnerId::Module, &orig_name)
{
Some(Value::Pointer(p)) => Some(p),
lookup_result => {
self.diagnostics.push(Diagnostic {
@@ -522,7 +532,10 @@ impl ModuleCompiler {
None => local_name.clone(),
};
let pointer = match self.scope_analysis.lookup(&OwnerId::Module, &named.local) {
let pointer = match self
.scope_analysis
.lookup_value(&OwnerId::Module, &named.local)
{
Some(Value::Pointer(p)) => p,
_ => {
self.diagnostics.push(Diagnostic {
@@ -555,7 +568,10 @@ impl ModuleCompiler {
Default(default) => {
let local_name = default.local.sym.to_string();
let pointer = match self.scope_analysis.lookup(&OwnerId::Module, &default.local) {
let pointer = match self
.scope_analysis
.lookup_value(&OwnerId::Module, &default.local)
{
Some(Value::Pointer(p)) => p,
_ => {
self.diagnostics.push(Diagnostic {
@@ -583,7 +599,7 @@ impl ModuleCompiler {
let pointer = match self
.scope_analysis
.lookup(&OwnerId::Module, &namespace.local)
.lookup_value(&OwnerId::Module, &namespace.local)
{
Some(Value::Pointer(p)) => p,
_ => {
@@ -641,7 +657,7 @@ impl ModuleCompiler {
let mut dependent_definitions: Vec<Definition>;
let defn_name = match ident {
Some(ident) => match self.scope_analysis.lookup(&OwnerId::Module, ident) {
Some(ident) => match self.scope_analysis.lookup_value(&OwnerId::Module, ident) {
Some(Value::Pointer(p)) => p,
_ => {
self.diagnostics.push(Diagnostic {
@@ -766,7 +782,7 @@ impl ModuleCompiler {
dependent_definitions.append(&mut self.compile_fn(
method_defn_name.clone(),
None,
Functionish::Fn(method.function.clone()),
Functionish::Fn(None, method.function.clone()),
));
methods

View File

@@ -164,12 +164,17 @@ impl ScopeAnalysis {
return sa;
}
pub fn lookup(&self, scope: &OwnerId, ident: &swc_ecma_ast::Ident) -> Option<Value> {
pub fn lookup(&self, ident: &swc_ecma_ast::Ident) -> Option<&Name> {
let name_id = self.refs.get(&ident.span)?;
self.lookup_name_id(scope, name_id)
self.names.get(name_id)
}
pub fn lookup_name_id(&self, scope: &OwnerId, name_id: &NameId) -> Option<Value> {
pub fn lookup_value(&self, scope: &OwnerId, ident: &swc_ecma_ast::Ident) -> Option<Value> {
let name_id = self.refs.get(&ident.span)?;
self.lookup_by_name_id(scope, name_id)
}
pub fn lookup_by_name_id(&self, scope: &OwnerId, name_id: &NameId) -> Option<Value> {
let name = self.names.get(name_id)?;
match &name.value {
@@ -450,7 +455,7 @@ impl ScopeAnalysis {
name: &Option<swc_ecma_ast::Ident>,
function: &swc_ecma_ast::Function,
) {
let child_scope = scope.nest(Some(OwnerId::Span(function.span.clone())));
let child_scope = scope.nest(Some(fn_to_owner_id(name, function)));
if let Some(name) = name {
self.insert_pointer_name(&child_scope, NameType::Function, name);
@@ -1909,3 +1914,13 @@ fn is_declare(decl: &swc_ecma_ast::Decl) -> bool {
swc_ecma_ast::Decl::TsModule(ts_module_decl) => ts_module_decl.declare,
}
}
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,
})
}