From 383fcddfa11465bdab9344d00b4fb11df9dc7b70 Mon Sep 17 00:00:00 2001 From: Andrew Morris Date: Wed, 31 May 2023 10:03:22 +1000 Subject: [PATCH] Split out TargetAccessor --- .../src/expression_compiler.rs | 237 +---------------- valuescript_compiler/src/lib.rs | 1 + valuescript_compiler/src/target_accessor.rs | 240 ++++++++++++++++++ 3 files changed, 243 insertions(+), 235 deletions(-) create mode 100644 valuescript_compiler/src/target_accessor.rs diff --git a/valuescript_compiler/src/expression_compiler.rs b/valuescript_compiler/src/expression_compiler.rs index 2bfcce2..16e936f 100644 --- a/valuescript_compiler/src/expression_compiler.rs +++ b/valuescript_compiler/src/expression_compiler.rs @@ -10,6 +10,7 @@ use crate::diagnostic::{Diagnostic, DiagnosticLevel}; use crate::function_compiler::{FunctionCompiler, Functionish, QueuedFunction}; use crate::scope::{NameId, OwnerId}; use crate::scope_analysis::{fn_to_owner_id, NameType}; +use crate::target_accessor::TargetAccessor; pub struct CompiledExpression { /** It is usually better to access this via functionCompiler.use_ */ @@ -319,7 +320,7 @@ impl<'a> ExpressionCompiler<'a> { return CompiledExpression::new(Value::Register(target), nested_registers); } - fn get_register_for_ident_mutation(&mut self, ident: &swc_ecma_ast::Ident) -> Register { + pub fn get_register_for_ident_mutation(&mut self, ident: &swc_ecma_ast::Ident) -> Register { let (reg, err_msg) = match self.fnc.lookup_value(ident) { Some(Value::Register(reg)) => (Some(reg), None), lookup_result => ( @@ -1706,237 +1707,3 @@ pub fn make_update_op(op: swc_ecma_ast::UpdateOp, register: Register) -> Instruc MinusMinus => Instruction::OpDec(register), } } - -struct NestedTargetAccess { - obj: Box, - subscript: CompiledExpression, - register: Register, -} - -enum TargetAccessor { - Register(Register), - Nested(NestedTargetAccess), -} - -impl TargetAccessor { - fn is_eligible_expr(ec: &mut ExpressionCompiler, expr: &swc_ecma_ast::Expr) -> bool { - use swc_ecma_ast::Expr::*; - - return match expr { - Ident(ident) => match ec.fnc.lookup(ident) { - Some(name) => !name.effectively_const, - _ => false, // TODO: InternalError? - }, - This(_) => true, - Member(member) => TargetAccessor::is_eligible_expr(ec, &member.obj), - _ => false, // TODO: Others may be eligible but not implemented? - }; - } - - fn compile( - ec: &mut ExpressionCompiler, - expr: &swc_ecma_ast::Expr, - is_outermost: bool, - ) -> TargetAccessor { - use swc_ecma_ast::Expr::*; - - return match expr { - Ident(ident) => TargetAccessor::compile_ident(ec, ident), - This(_) => TargetAccessor::Register(Register::This), - Member(member) => { - let obj = TargetAccessor::compile(ec, &member.obj, false); - let subscript = ec.member_prop(&member.prop, None); - - let register = ec.fnc.allocate_tmp(); - - if !is_outermost { - ec.fnc.push(Instruction::Sub( - Value::Register(obj.register()), - subscript.value.clone(), - register.clone(), - )); - } - - TargetAccessor::Nested(NestedTargetAccess { - obj: Box::new(obj), - subscript, - register, - }) - } - SuperProp(super_prop) => { - ec.fnc.todo(super_prop.span, "SuperProp expressions"); - TargetAccessor::make_todo(ec) - } - _ => { - ec.fnc.diagnostics.push(Diagnostic { - level: DiagnosticLevel::Error, - span: expr.span(), - message: format!("Invalid target {}", get_expr_type_str(expr)), - }); - - TargetAccessor::make_bad(ec) - } - }; - } - - fn compile_ident(ec: &mut ExpressionCompiler, ident: &swc_ecma_ast::Ident) -> TargetAccessor { - return TargetAccessor::Register(ec.get_register_for_ident_mutation(ident)); - } - - fn make_bad(ec: &mut ExpressionCompiler) -> TargetAccessor { - return TargetAccessor::Register(ec.fnc.allocate_numbered_reg(&"_bad_lvalue".to_string())); - } - - fn make_todo(ec: &mut ExpressionCompiler) -> TargetAccessor { - return TargetAccessor::Register(ec.fnc.allocate_numbered_reg(&"_todo_lvalue".to_string())); - } - - fn assign_and_packup( - &mut self, - ec: &mut ExpressionCompiler, - value: &Value, - uses_this_subcall: bool, - ) { - use TargetAccessor::*; - - match self { - Register(reg) => { - // TODO: Should value just derive from Eq? - if value.to_string() != reg.to_string() { - ec.fnc.push(Instruction::Mov(value.clone(), reg.clone())); - } - } - Nested(nta) => { - let submov_instr = Instruction::SubMov( - ec.fnc.use_ref(&mut nta.subscript), - value.clone(), - nta.obj.register(), - ); - - if uses_this_subcall { - // This avoids require_mutable_this when packing up a this_subcall. Technically it will - // still assign to %this, but we've protected against the actual mutation because if %this - // is const, then this_subcall won't allow its mutation. - ec.fnc.push_raw(submov_instr); - } else { - ec.fnc.push(submov_instr); - } - - ec.fnc.release_reg(&nta.register); - - nta.obj.packup(ec, uses_this_subcall); - } - } - } - - fn read(&self, ec: &mut ExpressionCompiler) -> Register { - use TargetAccessor::*; - - return match self { - Register(reg) => reg.clone(), - Nested(nta) => { - ec.fnc.push(Instruction::Sub( - Value::Register(nta.obj.register()), - nta.subscript.value.clone(), - nta.register.clone(), - )); - - nta.register.clone() - } - }; - } - - fn register(&self) -> Register { - use TargetAccessor::*; - - return match self { - Register(reg) => reg.clone(), - Nested(nta) => nta.register.clone(), - }; - } - - fn direct_register(&self) -> Option { - use TargetAccessor::*; - - return match self { - Register(reg) => Some(reg.clone()), - Nested(_) => None, - }; - } - - fn packup(&mut self, ec: &mut ExpressionCompiler, uses_this_subcall: bool) { - use TargetAccessor::*; - - match self { - Register(_) => {} - Nested(nta) => { - let submov_instr = Instruction::SubMov( - ec.fnc.use_ref(&mut nta.subscript), - Value::Register(nta.register.clone()), - nta.obj.register(), - ); - - if uses_this_subcall { - ec.fnc.push_raw(submov_instr); - } else { - ec.fnc.push(submov_instr); - } - - ec.fnc.release_reg(&nta.register); - - nta.obj.packup(ec, uses_this_subcall); - } - } - } - - fn targets_this(&self) -> bool { - return match self { - TargetAccessor::Register(reg) => reg == &Register::This, - TargetAccessor::Nested(nta) => nta.obj.targets_this(), - }; - } -} - -fn get_expr_type_str(expr: &swc_ecma_ast::Expr) -> &'static str { - use swc_ecma_ast::Expr::*; - - return match expr { - This(_) => "This", - Ident(_) => "Ident", - Array(_) => "Array", - Object(_) => "Object", - Fn(_) => "Fn", - Unary(_) => "Unary", - Update(_) => "Update", - Bin(_) => "Bin", - Assign(_) => "Assign", - Seq(_) => "Seq", - Cond(_) => "Cond", - Call(_) => "Call", - Member(_) => "Member", - New(_) => "New", - Paren(_) => "Paren", - Arrow(_) => "Arrow", - Yield(_) => "Yield", - Await(_) => "Await", - Lit(_) => "Lit", - Tpl(_) => "Tpl", - TaggedTpl(_) => "TaggedTpl", - Class(_) => "Class", - MetaProp(_) => "MetaProp", - Invalid(_) => "Invalid", - TsTypeAssertion(_) => "TsTypeAssertion", - TsConstAssertion(_) => "TsConstAssertion", - TsNonNull(_) => "TsNonNull", - TsAs(_) => "TsAs", - OptChain(_) => "OptChain", - PrivateName(_) => "PrivateName", - SuperProp(_) => "SuperProp", - JSXMember(_) => "JSXMember", - JSXNamespacedName(_) => "JSXNamespacedName", - JSXEmpty(_) => "JSXEmpty", - JSXElement(_) => "JSXElement", - JSXFragment(_) => "JSXFragment", - TsInstantiation(_) => "TsInstantiation", - }; -} diff --git a/valuescript_compiler/src/lib.rs b/valuescript_compiler/src/lib.rs index dc217cf..771d6a9 100644 --- a/valuescript_compiler/src/lib.rs +++ b/valuescript_compiler/src/lib.rs @@ -16,6 +16,7 @@ mod resolve_path; mod scope; mod scope_analysis; mod static_eval_expr; +mod target_accessor; pub use assembler::assemble; pub use assembly_parser::parse_module; diff --git a/valuescript_compiler/src/target_accessor.rs b/valuescript_compiler/src/target_accessor.rs new file mode 100644 index 0000000..6314bc3 --- /dev/null +++ b/valuescript_compiler/src/target_accessor.rs @@ -0,0 +1,240 @@ +use crate::{ + asm::{Instruction, Register, Value}, + expression_compiler::{CompiledExpression, ExpressionCompiler}, + Diagnostic, DiagnosticLevel, +}; +use swc_common::Spanned; + +pub struct NestedTargetAccess { + pub obj: Box, + pub subscript: CompiledExpression, + pub register: Register, +} + +pub enum TargetAccessor { + Register(Register), + Nested(NestedTargetAccess), +} + +impl TargetAccessor { + pub fn is_eligible_expr(ec: &mut ExpressionCompiler, expr: &swc_ecma_ast::Expr) -> bool { + use swc_ecma_ast::Expr::*; + + return match expr { + Ident(ident) => match ec.fnc.lookup(ident) { + Some(name) => !name.effectively_const, + _ => false, // TODO: InternalError? + }, + This(_) => true, + Member(member) => TargetAccessor::is_eligible_expr(ec, &member.obj), + _ => false, // TODO: Others may be eligible but not implemented? + }; + } + + pub fn compile( + ec: &mut ExpressionCompiler, + expr: &swc_ecma_ast::Expr, + is_outermost: bool, + ) -> TargetAccessor { + use swc_ecma_ast::Expr::*; + + return match expr { + Ident(ident) => TargetAccessor::compile_ident(ec, ident), + This(_) => TargetAccessor::Register(Register::This), + Member(member) => { + let obj = TargetAccessor::compile(ec, &member.obj, false); + let subscript = ec.member_prop(&member.prop, None); + + let register = ec.fnc.allocate_tmp(); + + if !is_outermost { + ec.fnc.push(Instruction::Sub( + Value::Register(obj.register()), + subscript.value.clone(), + register.clone(), + )); + } + + TargetAccessor::Nested(NestedTargetAccess { + obj: Box::new(obj), + subscript, + register, + }) + } + SuperProp(super_prop) => { + ec.fnc.todo(super_prop.span, "SuperProp expressions"); + TargetAccessor::make_todo(ec) + } + _ => { + ec.fnc.diagnostics.push(Diagnostic { + level: DiagnosticLevel::Error, + span: expr.span(), + message: format!("Invalid target {}", get_expr_type_str(expr)), + }); + + TargetAccessor::make_bad(ec) + } + }; + } + + pub fn compile_ident(ec: &mut ExpressionCompiler, ident: &swc_ecma_ast::Ident) -> TargetAccessor { + return TargetAccessor::Register(ec.get_register_for_ident_mutation(ident)); + } + + pub fn make_bad(ec: &mut ExpressionCompiler) -> TargetAccessor { + return TargetAccessor::Register(ec.fnc.allocate_numbered_reg(&"_bad_lvalue".to_string())); + } + + pub fn make_todo(ec: &mut ExpressionCompiler) -> TargetAccessor { + return TargetAccessor::Register(ec.fnc.allocate_numbered_reg(&"_todo_lvalue".to_string())); + } + + pub fn assign_and_packup( + &mut self, + ec: &mut ExpressionCompiler, + value: &Value, + uses_this_subcall: bool, + ) { + use TargetAccessor::*; + + match self { + Register(reg) => { + // TODO: Should value just derive from Eq? + if value.to_string() != reg.to_string() { + ec.fnc.push(Instruction::Mov(value.clone(), reg.clone())); + } + } + Nested(nta) => { + let submov_instr = Instruction::SubMov( + ec.fnc.use_ref(&mut nta.subscript), + value.clone(), + nta.obj.register(), + ); + + if uses_this_subcall { + // This avoids require_mutable_this when packing up a this_subcall. Technically it will + // still assign to %this, but we've protected against the actual mutation because if %this + // is const, then this_subcall won't allow its mutation. + ec.fnc.push_raw(submov_instr); + } else { + ec.fnc.push(submov_instr); + } + + ec.fnc.release_reg(&nta.register); + + nta.obj.packup(ec, uses_this_subcall); + } + } + } + + pub fn read(&self, ec: &mut ExpressionCompiler) -> Register { + use TargetAccessor::*; + + return match self { + Register(reg) => reg.clone(), + Nested(nta) => { + ec.fnc.push(Instruction::Sub( + Value::Register(nta.obj.register()), + nta.subscript.value.clone(), + nta.register.clone(), + )); + + nta.register.clone() + } + }; + } + + pub fn register(&self) -> Register { + use TargetAccessor::*; + + return match self { + Register(reg) => reg.clone(), + Nested(nta) => nta.register.clone(), + }; + } + + pub fn direct_register(&self) -> Option { + use TargetAccessor::*; + + return match self { + Register(reg) => Some(reg.clone()), + Nested(_) => None, + }; + } + + pub fn packup(&mut self, ec: &mut ExpressionCompiler, uses_this_subcall: bool) { + use TargetAccessor::*; + + match self { + Register(_) => {} + Nested(nta) => { + let submov_instr = Instruction::SubMov( + ec.fnc.use_ref(&mut nta.subscript), + Value::Register(nta.register.clone()), + nta.obj.register(), + ); + + if uses_this_subcall { + ec.fnc.push_raw(submov_instr); + } else { + ec.fnc.push(submov_instr); + } + + ec.fnc.release_reg(&nta.register); + + nta.obj.packup(ec, uses_this_subcall); + } + } + } + + pub fn targets_this(&self) -> bool { + return match self { + TargetAccessor::Register(reg) => reg == &Register::This, + TargetAccessor::Nested(nta) => nta.obj.targets_this(), + }; + } +} + +pub fn get_expr_type_str(expr: &swc_ecma_ast::Expr) -> &'static str { + use swc_ecma_ast::Expr::*; + + return match expr { + This(_) => "This", + Ident(_) => "Ident", + Array(_) => "Array", + Object(_) => "Object", + Fn(_) => "Fn", + Unary(_) => "Unary", + Update(_) => "Update", + Bin(_) => "Bin", + Assign(_) => "Assign", + Seq(_) => "Seq", + Cond(_) => "Cond", + Call(_) => "Call", + Member(_) => "Member", + New(_) => "New", + Paren(_) => "Paren", + Arrow(_) => "Arrow", + Yield(_) => "Yield", + Await(_) => "Await", + Lit(_) => "Lit", + Tpl(_) => "Tpl", + TaggedTpl(_) => "TaggedTpl", + Class(_) => "Class", + MetaProp(_) => "MetaProp", + Invalid(_) => "Invalid", + TsTypeAssertion(_) => "TsTypeAssertion", + TsConstAssertion(_) => "TsConstAssertion", + TsNonNull(_) => "TsNonNull", + TsAs(_) => "TsAs", + OptChain(_) => "OptChain", + PrivateName(_) => "PrivateName", + SuperProp(_) => "SuperProp", + JSXMember(_) => "JSXMember", + JSXNamespacedName(_) => "JSXNamespacedName", + JSXEmpty(_) => "JSXEmpty", + JSXElement(_) => "JSXElement", + JSXFragment(_) => "JSXFragment", + TsInstantiation(_) => "TsInstantiation", + }; +}