diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..e605eae --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,16 @@ +### Pre-req + +* Rust - `curl https://sh.rustup.rs -sSf | sh` + +### Once + +``` +> yarn default +> rustup target add wasm32-wasi +``` + +### Each Change + +``` +> yarn compile +``` diff --git a/src/closure_decorator.rs b/src/closure_decorator.rs index 1bad4c6..3bc1ace 100644 --- a/src/closure_decorator.rs +++ b/src/closure_decorator.rs @@ -5,7 +5,7 @@ use swc_core::ast::*; use swc_core::common::util::take::Take; use swc_core::common::DUMMY_SP; use swc_core::plugin::proxies::PluginSourceMapProxy; -use swc_core::utils::{prepend_stmts, private_ident, quote_ident}; +use swc_core::utils::{prepend_stmts, private_ident, quote_ident, quote_str}; use swc_core::visit::*; use crate::class_like::ClassLike; @@ -17,53 +17,39 @@ use crate::span::{concat_span, get_prop_name_span}; use crate::virtual_machine::VirtualMachine; /** - * Name of the `register` function that is injected into all compiled source files. + * Flags used to identify the Functionless AST association sequences. * - * ```ts - * function register_8269d1a8(func, ast) { - * func[Symbol.for("functionless:AST")] = ast; - * return func; - * } - * ``` - * - * All Function Declarations, Expressions and Arrow Expressions are decorated with - * the `register` function which attaches its AST as a property. + * These can be used by downstream programs to discover and manipulate the injects + * functionless assignment sequences. */ -const REGISTER_FUNCTION_NAME: &str = "register_8269d1a8"; +// (stash=REGISTER,stash=func,stash[]=ast,stash) +const REGISTER_FLAG: &str = "REGISTER"; +// (stash=REGISTER_REF,stash[]=ast) +const REGISTER_REF_FLAG: &str = "REGISTER_REF"; +// // (stash=BIND, ...) +const BIND_FLAG: &str = "BIND"; +// // (stash=PROXY, ...) +const PROXY_FLAG: &str = "PROXY"; + +const GLOBAL_THIS_NAME: &str = "global_8269d1a8"; /** - * Name of the `bind` function that is injected into all compiled source files. + * A unique variable used to story temporary values like registering functions with their AST. * + * Example: * ```ts - * function bind_8269d1a8(func, self, ...args) { - * const tmp = func.bind(self, ...args); - * if (typeof func === "function") { - * func[Symbol.for("functionless:BoundThis")] = self; - * func[Symbol.for("functionless:BoundArgs")] = args; - * func[Symbol.for("functionless:TargetFunction")] = func; - * } - * return tmp; - * } + * const func = () => {}; * ``` * - * All CallExpressions with the shape .bind(...) are re-written as calls - * to this special function which intercepts the call. - * ```ts - * .bind(...) - * // => - * bind_8269d1a8(, ...) - * ``` + * becomes: * - * If `` is a Function, then the values of BoundThis, BoundArgs and TargetFunction - * are added to the bound Function. + * ```ts + * let stash; * - * If `` is not a Function, then the call is proxied without modification. + * const func = (stash = () => {}, stash[Symbol.for(...)] = ast, stash); + * ``` */ -const BIND_FUNCTION_NAME: &str = "bind_8269d1a8"; - -const GLOBAL_THIS_NAME: &str = "global_8269d1a8"; - -const PROXY_FUNCTION_NAME: &str = "proxy_8269d1a8"; +const STASH_NAME: &str = "stash_8269d1a8"; const UTIL_FUNCTION_NAME: &str = "util_8269d1a8"; @@ -81,17 +67,9 @@ pub struct ClosureDecorator<'a> { */ pub global: Ident, /** - * An Identifier referencing the `register` interceptor function injected into all processed modules. - */ - pub register: Ident, - /** - * An Identifier referencing the `bind` interceptor function injected into all processed modules. - */ - pub bind: Ident, - /** - * An Identifier referencing the `proxy` interceptor function injected into all processed modules. + * A unique identifier functionless uses to story temporary values for registration. */ - pub proxy: Ident, + pub stash: Ident, /** * An Identifier referencing NodeJS's `util` module. */ @@ -113,9 +91,7 @@ impl<'a> ClosureDecorator<'a> { source_map, vm: VirtualMachine::new(), global: private_ident!(GLOBAL_THIS_NAME), - register: private_ident!(REGISTER_FUNCTION_NAME), - bind: private_ident!(BIND_FUNCTION_NAME), - proxy: private_ident!(PROXY_FUNCTION_NAME), + stash: private_ident!(STASH_NAME), util: private_ident!(UTIL_FUNCTION_NAME), ids: 0, } @@ -125,16 +101,27 @@ impl<'a> ClosureDecorator<'a> { impl<'a> VisitMut for ClosureDecorator<'a> { fn visit_mut_module_items(&mut self, items: &mut Vec) { let new_stmts: Vec = [ + ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl { + span: DUMMY_SP, + declare: false, + decls: vec![VarDeclarator { + definite: false, + name: Pat::Ident(BindingIdent { + id: self.stash.clone(), + type_ann: None, + }), + init: None, + span: DUMMY_SP, + }], + kind: VarDeclKind::Let, + }))), ModuleItem::Stmt(Stmt::Decl(self.create_global_this())), ModuleItem::Stmt(Stmt::Decl(self.create_import_util())), - ModuleItem::Stmt(Stmt::Decl(self.create_register_interceptor())), - ModuleItem::Stmt(Stmt::Decl(self.create_bind_interceptor())), - ModuleItem::Stmt(Stmt::Decl(self.create_proxy_interceptor())), ] .into_iter() .chain( - // discover all function declarations in the module and create a - // CallExpr to `register` that decorates each function with its AST. + // discover all function declarations in the module and + // add the expressions to `register` function with its AST. items .iter() .filter_map(|item| match item { @@ -163,7 +150,7 @@ impl<'a> VisitMut for ClosureDecorator<'a> { fn visit_mut_stmts(&mut self, stmts: &mut Vec) { // discover all function declarations in the block and create a - // CallExpr to `register` that decorates each function with its AST. + // add the expressions to `register` function with its AST. let register_stmts: Vec = stmts .iter() .filter_map(|stmt| self.register_stmt_if_func_decl(stmt)) @@ -186,49 +173,6 @@ impl<'a> VisitMut for ClosureDecorator<'a> { self.visit_mut_class_like(class_expr); } - fn visit_mut_call_expr(&mut self, call: &mut CallExpr) { - // detect if this looks like a call to Function.bind - // e.g. `foo.bind(self)` - // in general: `.bind(...)` - let maybe_bind_expr = match &mut call.callee { - Callee::Expr(expr) => match expr.as_mut() { - Expr::Member(member) => match &member.prop { - MemberProp::Ident(ident) if &ident.sym == "bind" => Some(member.obj.as_mut()), - _ => None, - }, - _ => None, - }, - _ => None, - }; - - if maybe_bind_expr.is_some() { - let expr = maybe_bind_expr.unwrap(); - - let args = iter::once(ExprOrSpread { - expr: Box::new(expr.take()), - spread: None, - }) - .chain(call.args.iter_mut().map(|arg| ExprOrSpread { - spread: None, - expr: arg.expr.take(), - })) - .collect(); - - // replace the CallExpr with a call to the `bind` interceptor function - // foo.bind(bar) - // => - // bind(foo, bar); - *call = CallExpr { - callee: Callee::Expr(Box::new(Expr::Ident(self.bind.clone()))), - span: call.span.clone(), - type_args: None, - args, - } - } - - call.visit_mut_children_with(self); - } - fn visit_mut_prop(&mut self, prop: &mut Prop) { match prop { // { method() { } } @@ -253,7 +197,7 @@ impl<'a> VisitMut for ClosureDecorator<'a> { *prop = Prop::KeyValue(KeyValueProp { key: method.key.take(), - value: self.register_ast(func, ast), + value: self.register_ast_seq(func, ast), }); } _ => { @@ -269,14 +213,10 @@ impl<'a> VisitMut for ClosureDecorator<'a> { arrow.visit_mut_children_with(self); - // replace the arrow function with a call to the `register` interceptor function - // that decorates the function with its AST + // replace the arrow function with the expressions which associate the function to it's AST // () => {..} // => - // register( - // () => {..}, // original arrow - // () => [..], // function that produces the arrow's AST - // ) + // ("REGISTER", stash = () => {...}, stash[Symbol.for("AST")] = ast, stash) *expr = *self.register_mut_ast(&mut Expr::Arrow(arrow.take()), ast); } Expr::Fn(func) if func.function.body.is_some() => { @@ -288,53 +228,75 @@ impl<'a> VisitMut for ClosureDecorator<'a> { // that decorates the function with its AST // function foo() {..} // => - // register( - // function foo() {..}, // original function expression - // () => [..], // function that produces the function expression's AST - // ) + // ("REGISTER", stash = function foo() {..}, stash[Symbol.for("AST")] = ast, stash) *expr = *self.register_mut_ast(&mut Expr::Fn(func.take()), ast); } - Expr::New(new) if new.args.is_some() => { + Expr::New(NewExpr { args, callee, .. }) if args.is_some() => { // detect if this looks like a new Proxy // e.g. `new Proxy({}, {})` - let maybe_proxy_class = match new.callee.as_mut() { - Expr::Ident(ident) if &ident.sym == "Proxy" => Some(ident), - _ => None, - }; + let maybe_proxy_class = callee.as_mut_ident().filter(|ident| &ident.sym == "Proxy"); if maybe_proxy_class.is_some() { let proxy_class = maybe_proxy_class.unwrap(); - let args: Vec = iter::once(ExprOrSpread { - expr: Box::new(Expr::Ident(proxy_class.take())), - spread: None, - }) - .chain( - new - .args - .as_mut() - .unwrap() - .iter_mut() - .map(|arg| ExprOrSpread { - spread: None, - expr: arg.expr.take(), - }), - ) - .collect(); + args + .iter_mut() + .for_each(|arg| arg.visit_mut_children_with(self)); + + proxy_class.visit_mut_children_with(self); // replace the NewExpr with a call to the `bind` interceptor function - // foo.bind(bar) + // new Proxy({}, {}) // => - // bind(foo, bar); - *expr = Expr::Call(CallExpr { - callee: Callee::Expr(Box::new(Expr::Ident(self.proxy.clone()))), - span: new.span.clone(), - type_args: None, - args: args, - }); + // ("PROXY", ...) + *expr = self.wrap_proxy( + Box::new(proxy_class.take()), + args.take().unwrap_or_default(), + ); + } else { + expr.visit_mut_children_with(self) } + } + Expr::Call(call) => { + let maybe_bind_expr = match &mut call.callee { + Callee::Expr(expr) => match expr.as_mut() { + Expr::Member(member) => match member { + MemberExpr { obj, prop, .. } + if prop + .as_ident() + .filter(|ident| &ident.sym == "bind") + .is_some() => + { + Some(obj) + } + _ => None, + }, + _ => None, + }, + _ => None, + }; - expr.visit_mut_children_with(self); + if maybe_bind_expr.is_some() { + let func = maybe_bind_expr.unwrap(); + call + .args + .iter_mut() + .for_each(|arg| arg.visit_mut_children_with(self)); + + func.visit_mut_children_with(self); + + // replace the CallExpr with a call to the `bind` interceptor function + // foo.bind(bar) + // => + // ("BIND", ...); + *expr = self.wrap_bind( + func.take(), + call.args[0].expr.clone(), + call.args[1..].to_vec(), + ) + } else { + expr.visit_mut_children_with(self); + } } _ => { expr.visit_mut_children_with(self); @@ -514,40 +476,13 @@ impl<'a> ClosureDecorator<'a> { fn register_ast_stmt(&self, expr: Box, ast: Box) -> Stmt { Stmt::Expr(ExprStmt { - expr: self.register_ast(expr, ast), + expr: self.register_ref_ast_seq(expr, ast), span: DUMMY_SP, }) } fn register_mut_ast(&self, func: &mut Expr, ast: Box) -> Box { - self.register_ast(Box::new(func.take()), ast) - } - - fn register_ast(&self, func: Box, ast: Box) -> Box { - Box::new(Expr::Call(CallExpr { - span: DUMMY_SP, - type_args: None, - // register(() => { .. }, () => new FunctionDecl([..])) - callee: Callee::Expr(Box::new(Expr::Ident(self.register.clone()))), - args: vec![ - ExprOrSpread { - expr: func, - spread: None, - }, - ExprOrSpread { - expr: Box::new(Expr::Arrow(ArrowExpr { - is_async: false, - is_generator: false, - return_type: None, - params: vec![], - span: DUMMY_SP, - type_params: None, - body: BlockStmtOrExpr::Expr(ast), - })), - spread: None, - }, - ], - })) + self.register_ast_seq(Box::new(func.take()), ast) } /** @@ -573,7 +508,18 @@ impl<'a> ClosureDecorator<'a> { span: DUMMY_SP, type_args: None, callee: Callee::Expr(prop_access_expr( - ident_expr(self.register.clone()), + Box::new(Expr::Arrow(ArrowExpr { + is_async: false, + is_generator: false, + return_type: None, + params: vec![], + span: DUMMY_SP, + type_params: None, + body: BlockStmtOrExpr::BlockStmt(BlockStmt { + span: DUMMY_SP, + stmts: vec![], + }), + })), quote_ident!("constructor"), )), args: vec![ExprOrSpread { @@ -612,135 +558,267 @@ impl<'a> ClosureDecorator<'a> { }) } - fn create_register_interceptor(&self) -> Decl { - let func = quote_ident!("func"); - let ast = quote_ident!("ast"); - Decl::Fn(FnDecl { - declare: false, - ident: self.register.clone(), - function: Function { - params: vec![param(func.clone(), false), param(ast.clone(), false)], - decorators: vec![], - span: DUMMY_SP, - body: Some(BlockStmt { + /** + * Registers a function or object with it's AST and returns a reference to it's value using the stash variable. + * + * ```ts + * const x = () => {} + * ``` + * + * becomes + * + * ```ts + * ("REGISTER", stash = () => {}, stash[Symbol.from(functionless::AST)] = ast, stash) + * ``` + */ + fn register_ast_seq(&self, func: Box, ast: Box) -> Box { + return Box::new(Expr::Seq(SeqExpr { + span: DUMMY_SP, + exprs: vec![ + // "REGISTER" + Box::new(Expr::Assign(AssignExpr { + left: PatOrExpr::Expr(Box::new(Expr::Ident(self.stash.clone()))), + op: AssignOp::Assign, + right: string_expr(REGISTER_FLAG), span: DUMMY_SP, - stmts: vec![ - self.set_symbol(func.clone(), "functionless:AST", ast.clone()), - Stmt::Return(ReturnStmt { - span: DUMMY_SP, - arg: Some(Box::new(Expr::Ident(func.clone()))), - }), - ], - }), - is_generator: false, - is_async: false, - type_params: None, - return_type: None, - }, - }) + })), + // stash=func + Box::new(Expr::Assign(AssignExpr { + left: PatOrExpr::Expr(Box::new(Expr::Ident(self.stash.clone()))), + op: AssignOp::Assign, + right: func, + span: DUMMY_SP, + })), + // stash[Symbol.from(functionless::AST)] = ast + Box::new(self.set_symbol_expr( + Box::new(Expr::Ident(self.stash.clone())), + "functionless:AST", + // is this right? + Box::new(Expr::Arrow(ArrowExpr { + is_async: false, + is_generator: false, + return_type: None, + params: vec![], + span: DUMMY_SP, + type_params: None, + body: BlockStmtOrExpr::Expr(ast), + })), + )), + // stash + Box::new(Expr::Ident(self.stash.clone())), + ], + })); } - fn create_bind_interceptor(&self) -> Decl { - let func = quote_ident!("func"); - let this = quote_ident!("self"); - let args = quote_ident!("args"); - let f = quote_ident!("f"); - Decl::Fn(FnDecl { - declare: false, - ident: self.bind.clone(), - function: Function { - params: vec![ - param(func.clone(), false), - param(this.clone(), false), - param(args.clone(), true), - ], - decorators: vec![], - span: DUMMY_SP, - body: Some(BlockStmt { + /** + * Registers an identifier with it's AST. + * + * (stash=REGISTER_REF,x[Symbol.for(AST)]=ast) + * function x() { + * .... + * } + */ + fn register_ref_ast_seq(&self, expr: Box, ast: Box) -> Box { + return Box::new(Expr::Seq(SeqExpr { + span: DUMMY_SP, + exprs: vec![ + // "REGISTER" + Box::new(Expr::Assign(AssignExpr { + left: PatOrExpr::Expr(Box::new(Expr::Ident(self.stash.clone()))), + op: AssignOp::Assign, + right: string_expr(REGISTER_REF_FLAG), span: DUMMY_SP, - stmts: vec![ - // const f = func.bind(self, ...args); - Stmt::Decl(Decl::Var(VarDecl { - declare: false, - kind: VarDeclKind::Const, - decls: vec![VarDeclarator { - definite: false, - span: DUMMY_SP, - name: Pat::Ident(BindingIdent { - id: f.clone(), - type_ann: None, - }), - init: Some(Box::new(Expr::Call(CallExpr { + })), + // stash[Symbol.from(functionless::AST)] = ast + Box::new(self.set_symbol_expr( + expr, + "functionless:AST", + // is this right? + Box::new(Expr::Arrow(ArrowExpr { + is_async: false, + is_generator: false, + return_type: None, + params: vec![], + span: DUMMY_SP, + type_params: None, + body: BlockStmtOrExpr::Expr(ast), + })), + )), + ], + })); + } + + /** + * ("BIND", + * stash={ args: args, self: this, func: func }, + * stash={ f: stash.func.bind(stash.self, ...stash.args), ...stash }, + * typeof stash.f === "function" && ( + * stash.f[Symbol.for("functionless:BoundThis")] = stash.self, + * stash.f[Symbol.for("functionless:BoundArgs")] = stash.args, + * stash.f[Symbol.for("functionless:TargetFunction")] = stash.func + * ), + * stash + * ) + */ + fn wrap_bind(&self, func: Box, this: Box, args: Vec) -> Expr { + let member_f = Box::new(Expr::Member(MemberExpr { + span: DUMMY_SP, + obj: Box::new(Expr::Ident(self.stash.clone())), + prop: MemberProp::Ident(quote_ident!("f")), + })); + // the original function stored in stash + let member_func = Box::new(Expr::Member(MemberExpr { + span: DUMMY_SP, + obj: Box::new(Expr::Ident(self.stash.clone())), + prop: MemberProp::Ident(quote_ident!("func")), + })); + let member_args = Box::new(Expr::Member(MemberExpr { + span: DUMMY_SP, + obj: Box::new(Expr::Ident(self.stash.clone())), + prop: MemberProp::Ident(quote_ident!("args")), + })); + // + let member_this = Box::new(Expr::Member(MemberExpr { + span: DUMMY_SP, + obj: Box::new(Expr::Ident(self.stash.clone())), + prop: MemberProp::Ident(quote_ident!("this")), + })); + + return Expr::Seq(SeqExpr { + span: DUMMY_SP, + exprs: vec![ + Box::new(Expr::Assign(AssignExpr { + left: PatOrExpr::Expr(Box::new(Expr::Ident(self.stash.clone()))), + op: AssignOp::Assign, + right: string_expr(BIND_FLAG), + span: DUMMY_SP, + })), + // stash={ args, self, func} + Box::new(Expr::Assign(AssignExpr { + span: DUMMY_SP, + left: PatOrExpr::Expr(Box::new(Expr::Ident(self.stash.clone()))), + op: AssignOp::Assign, + right: Box::new(Expr::Object(ObjectLit { + span: DUMMY_SP, + props: vec![ + // args = [args] + PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp { + key: PropName::Ident(quote_ident!("args")), + value: Box::new(Expr::Array(ArrayLit { + span: DUMMY_SP, + elems: args.into_iter().map(Some).collect(), + })), + }))), + // this = this + PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp { + key: PropName::Ident(quote_ident!("this")), + value: this, + }))), + // func = func + PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp { + key: PropName::Ident(quote_ident!("func")), + value: func, + }))), + ], + })), + })), + // stash = { f: stash.func.bind(stash.this, stash.args), ...stash } + Box::new(Expr::Assign(AssignExpr { + span: DUMMY_SP, + left: PatOrExpr::Expr(Box::new(Expr::Ident(self.stash.clone()))), + op: AssignOp::Assign, + right: Box::new(Expr::Object(ObjectLit { + span: DUMMY_SP, + props: vec![ + PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp { + key: PropName::Ident(quote_ident!("f")), + value: Box::new(Expr::Call(CallExpr { span: DUMMY_SP, type_args: None, callee: Callee::Expr(Box::new(Expr::Member(MemberExpr { - obj: Box::new(Expr::Ident(func.clone())), + obj: member_func.clone(), prop: MemberProp::Ident(quote_ident!("bind")), span: DUMMY_SP, }))), args: vec![ ExprOrSpread { - expr: Box::new(Expr::Ident(this.clone())), + expr: member_this.clone(), spread: None, }, + // ...stash.args ExprOrSpread { - expr: Box::new(Expr::Ident(args.clone())), + expr: member_args.clone(), spread: Some(DUMMY_SP), }, ], - }))), - }], - span: DUMMY_SP, - })), - // if(typeof func === "function") - Stmt::If(IfStmt { - span: DUMMY_SP, - test: Box::new(Expr::Bin(BinExpr { - span: DUMMY_SP, - left: Box::new(Expr::Unary(UnaryExpr { - arg: Box::new(Expr::Ident(func.clone())), - span: DUMMY_SP, - op: UnaryOp::TypeOf, })), - op: BinaryOp::EqEqEq, - right: Box::new(Expr::Lit(Lit::Str(Str { - raw: None, - span: DUMMY_SP, - value: JsWord::from("function"), - }))), - })), - alt: None, - cons: Box::new(Stmt::Block(BlockStmt { - span: DUMMY_SP, - stmts: vec![ - // f[Symbol.for("functionless:BoundThis")] = self; - // f[Symbol.for("functionless:BoundArgs")] = args; - // f[Symbol.for("functionless:TargetFunction")] = func; - self.set_symbol(f.clone(), "functionless:BoundThis", this.clone()), - self.set_symbol(f.clone(), "functionless:BoundArgs", args.clone()), - self.set_symbol(f.clone(), "functionless:TargetFunction", func.clone()), - ], - })), - }), - Stmt::Return(ReturnStmt { + }))), + // ...stash + PropOrSpread::Spread(SpreadElement { + dot3_token: DUMMY_SP, + expr: Box::new(Expr::Ident(self.stash.clone())), + }), + ], + })), + })), + // typeof stash.f === "function" && (stash.f[]=..., stash.f[]=..., stash.f[]=...) + Box::new(Expr::Bin(BinExpr { + span: DUMMY_SP, + op: BinaryOp::LogicalAnd, + left: Box::new(Expr::Bin(BinExpr { + span: DUMMY_SP, + left: Box::new(Expr::Unary(UnaryExpr { + arg: member_f.clone(), span: DUMMY_SP, - arg: Some(Box::new(Expr::Ident(f.clone()))), - }), - ], - }), - is_generator: false, - is_async: false, - type_params: None, - return_type: None, - }, - }) + op: UnaryOp::TypeOf, + })), + op: BinaryOp::EqEqEq, + right: Box::new(Expr::Lit(Lit::Str(quote_str!("function")))), + })), + right: Box::new(Expr::Seq(SeqExpr { + span: DUMMY_SP, + exprs: vec![ + // stash.f["functionless:BoundThis"] = stash.this; + Box::new(self.set_symbol_expr( + member_f.clone(), + "functionless:BoundThis", + member_this.clone(), + )), + // stash.f["functionless:BoundArgs"] = stash.args; + Box::new(self.set_symbol_expr( + member_f.clone(), + "functionless:BoundArgs", + member_args.clone(), + )), + // stash.f["functionless:TargetFunction"] = stash.func; + Box::new(self.set_symbol_expr( + member_f.clone(), + "functionless:TargetFunction", + member_func.clone(), + )), + ], + })), + })), + // return stash.f + member_f.clone(), + ], + }); } - fn create_proxy_interceptor(&self) -> Decl { - let clss = quote_ident!("clss"); - let args = quote_ident!("args"); - let proxy = quote_ident!("proxy"); - let proxy_map = quote_ident!("proxyMap"); + /** + * (stash="PROXY", + * stash={ args }, // ensure the args are only evaluated once + * stash={ proxy: new clss(...stash.args), ...stash }, // create the proxy + * (globalThis.util.types.isProxy(stash.proxy) && + * (globalThis.proxies = globalThis.proxies ?? new globalThis.WeakMap()) + * .set(stash.proxy, stash.args) + * ), + * proxy + * ) + */ + fn wrap_proxy(&self, clss: Box, args: Vec) -> Expr { + let ident_args = quote_ident!("args"); + let ident_proxy = quote_ident!("proxy"); let global_proxies = Expr::Member(MemberExpr { span: DUMMY_SP, obj: Box::new(Expr::Ident(self.global.clone())), @@ -750,154 +828,156 @@ impl<'a> ClosureDecorator<'a> { }), }); - Decl::Fn(FnDecl { - declare: false, - ident: self.proxy.clone(), - function: Function { - is_async: false, - is_generator: false, - decorators: vec![], - span: DUMMY_SP, - type_params: None, - return_type: None, - params: vec![param(clss.clone(), false), param(args.clone(), true)], - body: Some(BlockStmt { + let member_proxy = Box::new(Expr::Member(MemberExpr { + span: DUMMY_SP, + obj: Box::new(Expr::Ident(self.stash.clone())), + prop: MemberProp::Ident(ident_proxy.clone()), + })); + let member_args = Box::new(Expr::Member(MemberExpr { + span: DUMMY_SP, + obj: Box::new(Expr::Ident(self.stash.clone())), + prop: MemberProp::Ident(ident_args.clone()), + })); + + return Expr::Seq(SeqExpr { + span: DUMMY_SP, + exprs: vec![ + Box::new(Expr::Assign(AssignExpr { + left: PatOrExpr::Expr(Box::new(Expr::Ident(self.stash.clone()))), + op: AssignOp::Assign, + right: string_expr(PROXY_FLAG), span: DUMMY_SP, - stmts: vec![ - // const proxy = new clss(...args); - Stmt::Decl(Decl::Var(VarDecl { - declare: false, - kind: VarDeclKind::Const, - decls: vec![VarDeclarator { - definite: false, - span: DUMMY_SP, - name: Pat::Ident(BindingIdent { - id: proxy.clone(), - type_ann: None, - }), - init: Some(Box::new(Expr::New(NewExpr { + })), + // stash={ args } + Box::new(Expr::Assign(AssignExpr { + span: DUMMY_SP, + left: PatOrExpr::Expr(Box::new(Expr::Ident(self.stash.clone()))), + op: AssignOp::Assign, + right: Box::new(Expr::Object(ObjectLit { + span: DUMMY_SP, + props: vec![ + // args = [args] + PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp { + key: PropName::Ident(ident_args.clone()), + value: Box::new(Expr::Array(ArrayLit { + span: DUMMY_SP, + elems: args.into_iter().map(Some).collect(), + })), + }))), + ], + })), + })), + // stash = { proxy: new clss(...args), ...stash } + Box::new(Expr::Assign(AssignExpr { + span: DUMMY_SP, + left: PatOrExpr::Expr(Box::new(Expr::Ident(self.stash.clone()))), + op: AssignOp::Assign, + right: Box::new(Expr::Object(ObjectLit { + span: DUMMY_SP, + props: vec![ + PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp { + key: PropName::Ident(ident_proxy.clone()), + value: Box::new(Expr::New(NewExpr { span: DUMMY_SP, type_args: None, - callee: Box::new(Expr::Ident(clss.clone())), + callee: Box::new(Expr::Ident(*clss)), args: Some(vec![ExprOrSpread { - expr: Box::new(Expr::Ident(args.clone())), + expr: member_args.clone(), spread: Some(DUMMY_SP), }]), - }))), - }], - span: DUMMY_SP, - })), - // if (globalThis.util.types.isProxy(proxy)) - Stmt::If(IfStmt { - test: Box::new(Expr::Call(CallExpr { - type_args: None, - span: DUMMY_SP, - callee: Callee::Expr(prop_access_expr( - prop_access_expr(ident_expr(self.util.clone()), quote_ident!("types")), - quote_ident!("isProxy"), - )), - args: vec![ExprOrSpread { - expr: Box::new(Expr::Ident(proxy.clone())), - spread: None, - }], - })), - alt: None, - span: DUMMY_SP, - cons: Box::new(Stmt::Block(BlockStmt { + })), + }))), + // ...stash + PropOrSpread::Spread(SpreadElement { + dot3_token: DUMMY_SP, + expr: Box::new(Expr::Ident(self.stash.clone())), + }), + ], + })), + })), + // (globalThis.util.types.isProxy(proxy) && + // (globalThis.proxies = globalThis.proxies ?? new globalThis.WeakMap()) + // .set(stash.proxy, stash.args), stash.proxy) + Box::new(Expr::Bin(BinExpr { + span: DUMMY_SP, + op: BinaryOp::LogicalAnd, + // globalThis.util.types.isProxy(proxy) + left: Box::new(Expr::Call(CallExpr { + type_args: None, + span: DUMMY_SP, + callee: Callee::Expr(prop_access_expr( + prop_access_expr(ident_expr(self.util.clone()), quote_ident!("types")), + quote_ident!("isProxy"), + )), + args: vec![ExprOrSpread { + expr: member_proxy.clone(), + spread: None, + }], + })), + right: Box::new(Expr::Call(CallExpr { + span: DUMMY_SP, + callee: Callee::Expr(Box::new(Expr::Member(MemberExpr { + // globalThis.proxies = globalThis.proxies ?? new globalThis.WeakMap()) + obj: Box::new(Expr::Assign(AssignExpr { + // globalThis.proxies + left: PatOrExpr::Expr(Box::new(global_proxies.clone())), + op: AssignOp::Assign, span: DUMMY_SP, - stmts: vec![ - // const proxyMap = (globalThis.proxies = globalThis.proxies ?? new globalThis.WeakMap()); - Stmt::Decl(Decl::Var(VarDecl { - declare: false, + right: Box::new(Expr::Bin(BinExpr { + op: BinaryOp::NullishCoalescing, + // globalThis.proxies + left: Box::new(global_proxies.clone()), + span: DUMMY_SP, + // new globalThis.WeakMap() + right: Box::new(Expr::New(NewExpr { + type_args: None, span: DUMMY_SP, - kind: VarDeclKind::Const, - decls: vec![VarDeclarator { - definite: false, + // globalThis.WeakMap + callee: Box::new(Expr::Member(MemberExpr { span: DUMMY_SP, - name: Pat::Ident(BindingIdent { - id: proxy_map.clone(), - type_ann: None, - }), - init: Some(Box::new(Expr::Assign(AssignExpr { - // globalThis.proxies - left: PatOrExpr::Expr(Box::new(global_proxies.clone())), - op: AssignOp::Assign, - span: DUMMY_SP, - right: Box::new(Expr::Bin(BinExpr { - op: BinaryOp::NullishCoalescing, - // globalThis.proxies - left: Box::new(global_proxies.clone()), - span: DUMMY_SP, - // new globalThis.WeakMap() - right: Box::new(Expr::New(NewExpr { - type_args: None, - span: DUMMY_SP, - // globalThis.WeakMap - callee: Box::new(Expr::Member(MemberExpr { - span: DUMMY_SP, - obj: Box::new(Expr::Ident(self.global.clone())), - prop: MemberProp::Ident(quote_ident!("WeakMap")), - })), - args: None, - })), - })), - }))), - }], - })), - // proxyMap.set(proxy, args), - Stmt::Expr(ExprStmt { - expr: Box::new(Expr::Call(CallExpr { - type_args: None, - span: DUMMY_SP, - callee: Callee::Expr(Box::new(Expr::Member(MemberExpr { - obj: Box::new(Expr::Ident(proxy_map.clone())), - prop: MemberProp::Ident(quote_ident!("set")), - span: DUMMY_SP, - }))), - args: vec![ - ExprOrSpread { - expr: Box::new(Expr::Ident(proxy.clone())), - spread: None, - }, - ExprOrSpread { - expr: Box::new(Expr::Ident(args.clone())), - spread: None, - }, - ], + obj: Box::new(Expr::Ident(self.global.clone())), + prop: MemberProp::Ident(quote_ident!("WeakMap")), })), - span: DUMMY_SP, - }), - ], + args: None, + })), + })), })), - }), - // return proxy - Stmt::Return(ReturnStmt { span: DUMMY_SP, - arg: Some(Box::new(Expr::Ident(proxy.clone()))), - }), - ], - }), - }, - }) + prop: MemberProp::Ident(quote_ident!("set")), + }))), + args: vec![ + ExprOrSpread { + spread: None, + expr: member_proxy.clone(), + }, + ExprOrSpread { + spread: None, + expr: member_args.clone(), + }, + ], + type_args: None, + })), + })), + // return stash.proxy + member_proxy.clone(), + ], + }); } - fn set_symbol(&self, on: Ident, sym: &str, to: Ident) -> Stmt { - Stmt::Expr(ExprStmt { - span: DUMMY_SP, - expr: Box::new(Expr::Assign(AssignExpr { - // func[Symbol.for("functionless:AST")] = ast - left: PatOrExpr::Expr(Box::new(Expr::Member(MemberExpr { - obj: Box::new(Expr::Ident(on.clone())), - span: DUMMY_SP, - prop: MemberProp::Computed(ComputedPropName { - span: DUMMY_SP, - expr: self.symbol_for(sym), - }), - }))), - op: AssignOp::Assign, - right: Box::new(Expr::Ident(to.clone())), + fn set_symbol_expr(&self, on: Box, sym: &str, to: Box) -> Expr { + Expr::Assign(AssignExpr { + // func[Symbol.for("functionless:AST")] = ast + left: PatOrExpr::Expr(Box::new(Expr::Member(MemberExpr { + obj: on, span: DUMMY_SP, - })), + prop: MemberProp::Computed(ComputedPropName { + span: DUMMY_SP, + expr: self.symbol_for(sym), + }), + }))), + op: AssignOp::Assign, + right: to.clone(), + span: DUMMY_SP, }) } @@ -917,20 +997,3 @@ impl<'a> ClosureDecorator<'a> { })) } } - -fn param(id: Ident, rest: bool) -> Param { - Param { - decorators: vec![], - span: DUMMY_SP, - pat: if rest { - Pat::Rest(RestPat { - arg: Box::new(Pat::Ident(BindingIdent { id, type_ann: None })), - dot3_token: DUMMY_SP, - span: DUMMY_SP, - type_ann: None, - }) - } else { - Pat::Ident(BindingIdent { id, type_ann: None }) - }, - } -}