From 42a2258524c20d80bfbe0ef498f0540e44c8b018 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Wed, 27 Nov 2024 11:20:48 +0800 Subject: [PATCH 01/10] wip --- src/entity/mod.rs | 1 + src/entity/pure_result.rs | 165 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+) create mode 100644 src/entity/pure_result.rs diff --git a/src/entity/mod.rs b/src/entity/mod.rs index 0040d007..2c7da519 100644 --- a/src/entity/mod.rs +++ b/src/entity/mod.rs @@ -15,6 +15,7 @@ mod logical_result; mod object; mod operations; mod primitive; +mod pure_result; mod react_element; mod symbol; mod typeof_result; diff --git a/src/entity/pure_result.rs b/src/entity/pure_result.rs new file mode 100644 index 00000000..70b288bc --- /dev/null +++ b/src/entity/pure_result.rs @@ -0,0 +1,165 @@ +use super::{ + entity::{EnumeratedProperties, IteratedElements}, + Entity, EntityTrait, TypeofResult, +}; +use crate::{ + analyzer::Analyzer, + consumable::{Consumable, ConsumableNode}, + dep::ReferredDeps, +}; +use oxc::ast::ast::{CallExpression, NewExpression}; +use std::{cell::OnceCell, mem}; + +#[derive(Debug)] +pub enum PureCallNode<'a> { + CallExpression(&'a CallExpression<'a>), + NewExpression(&'a NewExpression<'a>), +} + +#[derive(Debug)] +pub struct PureResult<'a> { + pub node: PureCallNode<'a>, + pub result: OnceCell>, + pub referred_deps: ReferredDeps, +} + +impl<'a> EntityTrait<'a> for PureResult<'a> { + fn consume(&self, analyzer: &mut Analyzer<'a>) { + self.value(analyzer).consume(analyzer); + } + + fn unknown_mutate(&self, analyzer: &mut Analyzer<'a>, dep: Consumable<'a>) { + self.value(analyzer).unknown_mutate(analyzer, dep); + } + + fn get_property( + &self, + _rc: Entity<'a>, + analyzer: &mut Analyzer<'a>, + dep: Consumable<'a>, + key: Entity<'a>, + ) -> Entity<'a> { + self.value(analyzer).get_property(analyzer, dep, key) + } + + fn set_property( + &self, + _rc: Entity<'a>, + analyzer: &mut Analyzer<'a>, + dep: Consumable<'a>, + key: Entity<'a>, + value: Entity<'a>, + ) { + self.value(analyzer).set_property(analyzer, dep, key, value); + } + + fn enumerate_properties( + &self, + _rc: Entity<'a>, + analyzer: &mut Analyzer<'a>, + dep: Consumable<'a>, + ) -> EnumeratedProperties<'a> { + self.value(analyzer).enumerate_properties(analyzer, dep) + } + + fn delete_property(&self, analyzer: &mut Analyzer<'a>, dep: Consumable<'a>, key: Entity<'a>) { + self.value(analyzer).delete_property(analyzer, dep, key); + } + + fn call( + &self, + _rc: Entity<'a>, + analyzer: &mut Analyzer<'a>, + dep: Consumable<'a>, + this: Entity<'a>, + args: Entity<'a>, + ) -> Entity<'a> { + self.value(analyzer).call(analyzer, dep, this, args) + } + + fn construct( + &self, + _rc: Entity<'a>, + analyzer: &mut Analyzer<'a>, + dep: Consumable<'a>, + args: Entity<'a>, + ) -> Entity<'a> { + self.value(analyzer).construct(analyzer, dep, args) + } + + fn jsx(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>, props: Entity<'a>) -> Entity<'a> { + self.value(analyzer).jsx(analyzer, props) + } + + fn r#await( + &self, + _rc: Entity<'a>, + analyzer: &mut Analyzer<'a>, + dep: Consumable<'a>, + ) -> Entity<'a> { + self.value(analyzer).r#await(analyzer, dep) + } + + fn iterate( + &self, + _rc: Entity<'a>, + analyzer: &mut Analyzer<'a>, + dep: Consumable<'a>, + ) -> IteratedElements<'a> { + self.value(analyzer).iterate(analyzer, dep) + } + + fn get_destructable(&self, _rc: Entity<'a>, dep: Consumable<'a>) -> Consumable<'a> { + self.value(analyzer).get_destructable(dep) + } + + fn get_typeof(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + self.value(analyzer).get_typeof(analyzer) + } + + fn get_to_string(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + self.value(analyzer).get_to_string(analyzer) + } + + fn get_to_numeric(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + self.value(analyzer).get_to_numeric(analyzer) + } + + fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + self.value(analyzer).get_to_boolean(analyzer) + } + + fn get_to_property_key(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + self.value(analyzer).get_to_property_key(analyzer) + } + + fn get_to_jsx_child(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + self.value(analyzer).get_to_jsx_child(analyzer) + } + + fn test_typeof(&self) -> TypeofResult { + self.value(analyzer).test_typeof() + } + + fn test_truthy(&self) -> Option { + self.value(analyzer).test_truthy() + } + + fn test_nullish(&self) -> Option { + self.value(analyzer).test_nullish() + } +} + +impl<'a> PureResult<'a> { + fn value(&self, analyzer: &mut Analyzer<'a>) -> Entity<'a> { + *self.result.get_or_init(|| { + let parent_referred_deps = mem::replace(&mut analyzer.referred_deps, ReferredDeps::default()); + let val = analyzer.exec_indeterminately(|analyzer| match &self.node { + PureCallNode::CallExpression(node) => analyzer.exec_call_expression(node), + PureCallNode::NewExpression(node) => analyzer.exec_new_expression(node), + }); + let this_referred_deps = mem::replace(&mut analyzer.referred_deps, parent_referred_deps); + analyzer.factory.computed(val, ConsumableNode::new(this_referred_deps)) + }) + } +} From 1d0a83d51a1719ea9e40dc33320b92a8ee8c5051 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Wed, 27 Nov 2024 11:41:55 +0800 Subject: [PATCH 02/10] fix --- src/analyzer.rs | 4 +- src/builtins/globals/object_constructor.rs | 2 +- src/builtins/prototypes/function.rs | 2 +- src/builtins/prototypes/mod.rs | 2 +- src/builtins/react/class_names.rs | 4 +- src/builtins/react/create_element.rs | 6 +- src/builtins/react/jsxs.rs | 3 +- src/entity/arguments.rs | 25 +++--- src/entity/array.rs | 25 +++--- src/entity/builtin_fn.rs | 25 +++--- src/entity/class.rs | 33 ++++---- src/entity/collected.rs | 35 ++++---- src/entity/collector.rs | 10 ++- src/entity/computed.rs | 37 ++++---- src/entity/consumed_object.rs | 4 +- src/entity/entity.rs | 76 ++++++++++------- src/entity/function.rs | 25 +++--- src/entity/literal.rs | 43 ++++++---- src/entity/logical_result.rs | 49 ++++++----- src/entity/object.rs | 27 +++--- src/entity/operations.rs | 93 ++++++++++++++------- src/entity/primitive.rs | 29 ++++--- src/entity/pure_result.rs | 34 ++++---- src/entity/react_element.rs | 27 +++--- src/entity/union.rs | 39 +++++---- src/entity/unknown.rs | 27 +++--- src/entity/utils.rs | 2 +- src/nodes/expr/assignment_expression.rs | 8 +- src/nodes/expr/call_expression.rs | 2 +- src/nodes/expr/conditional_expression.rs | 2 +- src/nodes/expr/logical_expression.rs | 8 +- src/nodes/expr/member_expression.rs | 2 +- src/nodes/expr/unary_expression.rs | 2 +- src/nodes/misc/assignment_target_pattern.rs | 5 +- src/nodes/misc/binding_pattern.rs | 2 +- src/nodes/misc/with_default.rs | 2 +- src/nodes/stmt/do_while_statement.rs | 2 +- src/nodes/stmt/for_in_statement.rs | 4 +- src/nodes/stmt/for_statement.rs | 2 +- src/nodes/stmt/if_statement.rs | 2 +- src/nodes/stmt/while_statement.rs | 2 +- src/utils/dep_id.rs | 35 +++++++- src/utils/symbol_id.rs | 2 +- 43 files changed, 464 insertions(+), 306 deletions(-) diff --git a/src/analyzer.rs b/src/analyzer.rs index 86df8792..6f89624c 100644 --- a/src/analyzer.rs +++ b/src/analyzer.rs @@ -39,7 +39,7 @@ pub struct Analyzer<'a> { pub pending_labels: Vec>, pub pending_deps: FxHashSet>, pub builtins: Builtins<'a>, - pub entity_op: EntityOpHost<'a>, + pub entity_op: &'a EntityOpHost<'a>, pub logger: Option<&'a Logger>, pub debug: usize, @@ -70,7 +70,7 @@ impl<'a> Analyzer<'a> { pending_labels: Vec::new(), pending_deps: Default::default(), builtins: Builtins::new(config, factory), - entity_op: EntityOpHost::new(allocator), + entity_op: allocator.alloc(EntityOpHost::new(allocator)), logger, debug: 0, } diff --git a/src/builtins/globals/object_constructor.rs b/src/builtins/globals/object_constructor.rs index c8304853..b7d8887d 100644 --- a/src/builtins/globals/object_constructor.rs +++ b/src/builtins/globals/object_constructor.rs @@ -66,7 +66,7 @@ impl<'a> Builtins<'a> { let array = analyzer.new_empty_array(); for (_, key, value) in properties { - if key.test_typeof().contains(TypeofResult::String) { + if key.test_typeof(analyzer).contains(TypeofResult::String) { array.init_rest(analyzer.factory.computed(key.get_to_string(analyzer), value)); } } diff --git a/src/builtins/prototypes/function.rs b/src/builtins/prototypes/function.rs index 13029eec..d566a235 100644 --- a/src/builtins/prototypes/function.rs +++ b/src/builtins/prototypes/function.rs @@ -14,7 +14,7 @@ pub fn create_function_prototype<'a>(factory: &EntityFactory<'a>) -> Prototype<' let cf_scope = analyzer.scope_context.cf.current_id(); // This can be any value let arguments_object_id = SymbolId::from_usize(0); - match arg.test_is_undefined() { + match arg.test_is_undefined(analyzer) { Some(true) => analyzer.factory.entity(ArrayEntity::new(cf_scope, arguments_object_id)), Some(false) => arg, None => analyzer.factory.union(( diff --git a/src/builtins/prototypes/mod.rs b/src/builtins/prototypes/mod.rs index 1fc63e2b..80114218 100644 --- a/src/builtins/prototypes/mod.rs +++ b/src/builtins/prototypes/mod.rs @@ -68,7 +68,7 @@ impl<'a> Prototype<'a> { pub fn get_property( &self, - analyzer: &Analyzer<'a>, + analyzer: &mut Analyzer<'a>, rc: Entity<'a>, key: Entity<'a>, dep: Consumable<'a>, diff --git a/src/builtins/react/class_names.rs b/src/builtins/react/class_names.rs index 5f35da6c..458e6e12 100644 --- a/src/builtins/react/class_names.rs +++ b/src/builtins/react/class_names.rs @@ -14,12 +14,12 @@ pub fn create_class_names_namespace<'a>( let mut deps_1 = vec![]; let mut deps_2 = vec![iterate_dep]; for class_name in class_names { - if TypeofResult::Object.contains(class_name.test_typeof()) { + if TypeofResult::Object.contains(class_name.test_typeof(analyzer)) { // This may be an array. However, this makes no difference in this logic. let (properties, enumerate_dep) = class_name.enumerate_properties(analyzer, dep.cloned()); deps_2.push(enumerate_dep); for (_, key, value) in properties { - if value.test_truthy() != Some(false) { + if value.test_truthy(analyzer) != Some(false) { deps_1.push(key); deps_1.push(value); } diff --git a/src/builtins/react/create_element.rs b/src/builtins/react/create_element.rs index 23892466..eea5dd9c 100644 --- a/src/builtins/react/create_element.rs +++ b/src/builtins/react/create_element.rs @@ -7,7 +7,7 @@ pub fn create_react_create_element_impl<'a>(factory: &'a EntityFactory<'a>) -> E factory.implemented_builtin_fn("React::createElement", |analyzer, dep, _this, args| { let (args, children, _) = args.destruct_as_array(analyzer, dep, 2); let [tag, props] = args[..] else { unreachable!() }; - let props = match props.test_nullish() { + let props = match props.test_nullish(analyzer) { Some(true) => { analyzer.factory.entity(analyzer.new_empty_object(&analyzer.builtins.prototypes.object)) } @@ -20,7 +20,7 @@ pub fn create_react_create_element_impl<'a>(factory: &'a EntityFactory<'a>) -> E // Special prop: ref let r#ref = props.get_property(analyzer, box_consumable(()), analyzer.factory.string("ref")); - if r#ref.test_nullish() != Some(true) { + if r#ref.test_nullish(analyzer) != Some(true) { // TODO: currently we haven't implemented useRef, so we just consider it as a callback analyzer.exec_consumed_fn("React_ref", move |analyzer| { r#ref.call( @@ -34,7 +34,7 @@ pub fn create_react_create_element_impl<'a>(factory: &'a EntityFactory<'a>) -> E // Special prop: key let key = props.get_property(analyzer, box_consumable(()), analyzer.factory.string("key")); - if r#ref.test_nullish() != Some(true) { + if r#ref.test_nullish(analyzer) != Some(true) { analyzer.consume(key); } diff --git a/src/builtins/react/jsxs.rs b/src/builtins/react/jsxs.rs index 48d9d622..842724e9 100644 --- a/src/builtins/react/jsxs.rs +++ b/src/builtins/react/jsxs.rs @@ -7,7 +7,8 @@ pub fn create_react_jsxs_impl<'a>(factory: &'a EntityFactory<'a>) -> Entity<'a> factory.implemented_builtin_fn("React::jsxs", |analyzer, dep, _this, args| { let args = args.destruct_as_array(analyzer, dep, 3).0; let [tag, props, key] = args[..] else { unreachable!() }; - analyzer.consume(props.get_destructable(box_consumable(()))); + let destructable_dep = props.get_destructable(analyzer, box_consumable(())); + analyzer.consume(destructable_dep); props.set_property(analyzer, box_consumable(()), analyzer.factory.string("key"), key); analyzer.factory.react_element(tag, props) }) diff --git a/src/entity/arguments.rs b/src/entity/arguments.rs index 5eda2d0c..f4b37b71 100644 --- a/src/entity/arguments.rs +++ b/src/entity/arguments.rs @@ -127,43 +127,48 @@ impl<'a> EntityTrait<'a> for ArgumentsEntity<'a> { (elements, rest.map(|val| analyzer.factory.union(val)), dep) } - fn get_destructable(&self, _rc: Entity<'a>, _dep: Consumable<'a>) -> Consumable<'a> { + fn get_destructable( + &self, + _rc: Entity<'a>, + _analyzer: &mut Analyzer<'a>, + _dep: Consumable<'a>, + ) -> Consumable<'a> { unreachable!() } - fn get_typeof(&self, _rc: Entity<'a>, _analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_typeof(&self, _rc: Entity<'a>, _analyzer: &mut Analyzer<'a>) -> Entity<'a> { unreachable!() } - fn get_to_string(&self, _rc: Entity<'a>, _analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_string(&self, _rc: Entity<'a>, _analyzer: &mut Analyzer<'a>) -> Entity<'a> { unreachable!() } - fn get_to_numeric(&self, _rc: Entity<'a>, _analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_numeric(&self, _rc: Entity<'a>, _analyzer: &mut Analyzer<'a>) -> Entity<'a> { unreachable!() } - fn get_to_boolean(&self, _rc: Entity<'a>, _analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_boolean(&self, _rc: Entity<'a>, _analyzer: &mut Analyzer<'a>) -> Entity<'a> { unreachable!() } - fn get_to_property_key(&self, _rc: Entity<'a>, _analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_property_key(&self, _rc: Entity<'a>, _analyzer: &mut Analyzer<'a>) -> Entity<'a> { unreachable!() } - fn get_to_jsx_child(&self, _rc: Entity<'a>, _analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_jsx_child(&self, _rc: Entity<'a>, _analyzer: &mut Analyzer<'a>) -> Entity<'a> { unreachable!() } - fn test_typeof(&self) -> TypeofResult { + fn test_typeof(&self, _analyzer: &mut Analyzer<'a>) -> TypeofResult { unreachable!() } - fn test_truthy(&self) -> Option { + fn test_truthy(&self, _analyzer: &mut Analyzer<'a>) -> Option { unreachable!() } - fn test_nullish(&self) -> Option { + fn test_nullish(&self, _analyzer: &mut Analyzer<'a>) -> Option { unreachable!() } } diff --git a/src/entity/array.rs b/src/entity/array.rs index bdc6d75c..64be646e 100644 --- a/src/entity/array.rs +++ b/src/entity/array.rs @@ -347,49 +347,54 @@ impl<'a> EntityTrait<'a> for ArrayEntity<'a> { ) } - fn get_destructable(&self, _rc: Entity<'a>, dep: Consumable<'a>) -> Consumable<'a> { + fn get_destructable( + &self, + _rc: Entity<'a>, + analyzer: &mut Analyzer<'a>, + dep: Consumable<'a>, + ) -> Consumable<'a> { dep } - fn get_typeof(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_typeof(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { analyzer.factory.string("object") } - fn get_to_string(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_string(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { if self.consumed.get() { return consumed_object::get_to_string(analyzer); } analyzer.factory.computed_unknown_string(rc) } - fn get_to_numeric(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_numeric(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { if self.consumed.get() { return consumed_object::get_to_numeric(analyzer); } analyzer.factory.computed_unknown(rc) } - fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { analyzer.factory.boolean(true) } - fn get_to_property_key(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_property_key(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.get_to_string(rc, analyzer) } - fn get_to_jsx_child(&self, rc: Entity<'a>, _analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_jsx_child(&self, rc: Entity<'a>, _analyzer: &mut Analyzer<'a>) -> Entity<'a> { rc } - fn test_typeof(&self) -> TypeofResult { + fn test_typeof(&self, _analyzer: &mut Analyzer<'a>) -> TypeofResult { TypeofResult::Object } - fn test_truthy(&self) -> Option { + fn test_truthy(&self, _analyzer: &mut Analyzer<'a>) -> Option { Some(true) } - fn test_nullish(&self) -> Option { + fn test_nullish(&self, _analyzer: &mut Analyzer<'a>) -> Option { Some(false) } } diff --git a/src/entity/builtin_fn.rs b/src/entity/builtin_fn.rs index 111b56ce..5eb16db9 100644 --- a/src/entity/builtin_fn.rs +++ b/src/entity/builtin_fn.rs @@ -136,44 +136,49 @@ impl<'a, T: BuiltinFnEntity<'a>> EntityTrait<'a> for T { consumed_object::iterate(analyzer, dep) } - fn get_destructable(&self, rc: Entity<'a>, dep: Consumable<'a>) -> Consumable<'a> { + fn get_destructable( + &self, + rc: Entity<'a>, + _analyzer: &mut Analyzer<'a>, + dep: Consumable<'a>, + ) -> Consumable<'a> { box_consumable((rc, dep)) } - fn get_typeof(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_typeof(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { analyzer.factory.string("function") } - fn get_to_string(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_string(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { analyzer.factory.computed_unknown_string(rc) } - fn get_to_numeric(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_numeric(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { analyzer.factory.nan } - fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { analyzer.factory.boolean(true) } - fn get_to_property_key(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_property_key(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.get_to_string(rc, analyzer) } - fn get_to_jsx_child(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_jsx_child(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { // TODO: analyzer.thrown_builtin_error("Functions are not valid JSX children"); analyzer.factory.string("") } - fn test_typeof(&self) -> TypeofResult { + fn test_typeof(&self, _analyzer: &mut Analyzer<'a>) -> TypeofResult { TypeofResult::Function } - fn test_truthy(&self) -> Option { + fn test_truthy(&self, _analyzer: &mut Analyzer<'a>) -> Option { Some(true) } - fn test_nullish(&self) -> Option { + fn test_nullish(&self, _analyzer: &mut Analyzer<'a>) -> Option { Some(false) } } diff --git a/src/entity/class.rs b/src/entity/class.rs index 1f62e028..70948adf 100644 --- a/src/entity/class.rs +++ b/src/entity/class.rs @@ -44,11 +44,9 @@ impl<'a> EntityTrait<'a> for ClassEntity<'a> { if self.consumed.get() { return consumed_object::get_property(rc, analyzer, dep, key); } - if analyzer.entity_op.strict_eq( - analyzer, - key.get_to_property_key(analyzer), - analyzer.factory.string("prototype"), - ) != Some(false) + let key = key.get_to_property_key(analyzer); + if analyzer.entity_op.strict_eq(analyzer, key, analyzer.factory.string("prototype")) + != Some(false) { self.consume(analyzer); return consumed_object::get_property(rc, analyzer, dep, key); @@ -127,37 +125,42 @@ impl<'a> EntityTrait<'a> for ClassEntity<'a> { consumed_object::iterate(analyzer, dep) } - fn get_destructable(&self, rc: Entity<'a>, dep: Consumable<'a>) -> Consumable<'a> { + fn get_destructable( + &self, + rc: Entity<'a>, + _analyzer: &mut Analyzer<'a>, + dep: Consumable<'a>, + ) -> Consumable<'a> { box_consumable((rc, dep)) } - fn get_typeof(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_typeof(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { analyzer.factory.string("function") } - fn get_to_string(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_string(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { if self.consumed.get() { return consumed_object::get_to_string(analyzer); } analyzer.factory.computed_unknown_string(rc) } - fn get_to_numeric(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_numeric(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { if self.consumed.get() { return consumed_object::get_to_numeric(analyzer); } analyzer.factory.nan } - fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { analyzer.factory.boolean(true) } - fn get_to_property_key(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_property_key(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.get_to_string(rc, analyzer) } - fn get_to_jsx_child(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_jsx_child(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { if self.consumed.get() { analyzer.factory.immutable_unknown } else { @@ -166,15 +169,15 @@ impl<'a> EntityTrait<'a> for ClassEntity<'a> { } } - fn test_typeof(&self) -> TypeofResult { + fn test_typeof(&self, _analyzer: &mut Analyzer<'a>) -> TypeofResult { TypeofResult::Function } - fn test_truthy(&self) -> Option { + fn test_truthy(&self, _analyzer: &mut Analyzer<'a>) -> Option { Some(true) } - fn test_nullish(&self) -> Option { + fn test_nullish(&self, _analyzer: &mut Analyzer<'a>) -> Option { Some(false) } } diff --git a/src/entity/collected.rs b/src/entity/collected.rs index a57dbd81..c3c769f6 100644 --- a/src/entity/collected.rs +++ b/src/entity/collected.rs @@ -115,58 +115,63 @@ impl<'a> EntityTrait<'a> for CollectedEntity<'a> { (elements, rest, box_consumable((deps, self.deps.clone()))) } - fn get_destructable(&self, _rc: Entity<'a>, dep: Consumable<'a>) -> Consumable<'a> { + fn get_destructable( + &self, + _rc: Entity<'a>, + _analyzer: &mut Analyzer<'a>, + dep: Consumable<'a>, + ) -> Consumable<'a> { box_consumable((self.deps.clone(), dep)) } - fn get_typeof(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_typeof(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { // TODO: Verify this self.forward(self.val.get_typeof(analyzer), analyzer) } - fn get_to_string(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_string(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.forward(self.val.get_to_string(analyzer), analyzer) } - fn get_to_numeric(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_numeric(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.forward(self.val.get_to_numeric(analyzer), analyzer) } - fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.forward(self.val.get_to_boolean(analyzer), analyzer) } - fn get_to_property_key(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_property_key(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.forward(self.val.get_to_property_key(analyzer), analyzer) } - fn get_to_jsx_child(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_jsx_child(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.forward(self.val.get_to_jsx_child(analyzer), analyzer) } fn get_to_literals( &self, _rc: Entity<'a>, - analyzer: &Analyzer<'a>, + analyzer: &mut Analyzer<'a>, ) -> Option>> { self.val.get_to_literals(analyzer) } - fn test_typeof(&self) -> TypeofResult { - self.val.test_typeof() + fn test_typeof(&self, analyzer: &mut Analyzer<'a>) -> TypeofResult { + self.val.test_typeof(analyzer) } - fn test_truthy(&self) -> Option { - self.val.test_truthy() + fn test_truthy(&self, analyzer: &mut Analyzer<'a>) -> Option { + self.val.test_truthy(analyzer) } - fn test_nullish(&self) -> Option { - self.val.test_nullish() + fn test_nullish(&self, analyzer: &mut Analyzer<'a>) -> Option { + self.val.test_nullish(analyzer) } } impl<'a> CollectedEntity<'a> { - fn forward(&self, val: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn forward(&self, val: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { analyzer.factory.collected(val, self.deps.clone()) } diff --git a/src/entity/collector.rs b/src/entity/collector.rs index a7dcb975..544e600e 100644 --- a/src/entity/collector.rs +++ b/src/entity/collector.rs @@ -16,7 +16,11 @@ pub struct LiteralCollector<'a> { } impl<'a> LiteralCollector<'a> { - fn try_collect(&self, analyzer: &Analyzer<'a>, entity: Entity<'a>) -> Option> { + fn try_collect( + &self, + analyzer: &mut Analyzer<'a>, + entity: Entity<'a>, + ) -> Option> { if let Some(lit) = entity.get_literal(analyzer) { lit.can_build_expr(analyzer).then_some(lit) } else { @@ -24,7 +28,7 @@ impl<'a> LiteralCollector<'a> { } } - pub fn collect(&mut self, analyzer: &Analyzer<'a>, entity: Entity<'a>) -> Entity<'a> { + pub fn collect(&mut self, analyzer: &mut Analyzer<'a>, entity: Entity<'a>) -> Entity<'a> { if self.invalid { entity } else if let Some(literal) = self.try_collect(analyzer, entity) { @@ -50,7 +54,7 @@ impl<'a> LiteralCollector<'a> { pub fn get_entity_on_invalid( &mut self, entity: Entity<'a>, - analyzer: &Analyzer<'a>, + analyzer: &mut Analyzer<'a>, ) -> Entity<'a> { if self.collected.is_empty() { entity diff --git a/src/entity/computed.rs b/src/entity/computed.rs index bf361bc6..d643f404 100644 --- a/src/entity/computed.rs +++ b/src/entity/computed.rs @@ -106,52 +106,57 @@ impl<'a, T: ConsumableTrait<'a> + 'a> EntityTrait<'a> for ComputedEntity<'a, T> self.val.iterate(analyzer, self.forward_dep(dep)) } - fn get_destructable(&self, _rc: Entity<'a>, dep: Consumable<'a>) -> Consumable<'a> { - self.val.get_destructable(self.forward_dep(dep)) + fn get_destructable( + &self, + _rc: Entity<'a>, + analyzer: &mut Analyzer<'a>, + dep: Consumable<'a>, + ) -> Consumable<'a> { + self.val.get_destructable(analyzer, self.forward_dep(dep)) } - fn get_typeof(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_typeof(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.forward_value(self.val.get_typeof(analyzer), analyzer) } - fn get_to_string(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_string(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.forward_value(self.val.get_to_string(analyzer), analyzer) } - fn get_to_numeric(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_numeric(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.forward_value(self.val.get_to_numeric(analyzer), analyzer) } - fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.forward_value(self.val.get_to_boolean(analyzer), analyzer) } - fn get_to_property_key(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_property_key(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.forward_value(self.val.get_to_property_key(analyzer), analyzer) } - fn get_to_jsx_child(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_jsx_child(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.forward_value(self.val.get_to_jsx_child(analyzer), analyzer) } fn get_to_literals( &self, _rc: Entity<'a>, - analyzer: &Analyzer<'a>, + analyzer: &mut Analyzer<'a>, ) -> Option>> { self.val.get_to_literals(analyzer) } - fn test_typeof(&self) -> TypeofResult { - self.val.test_typeof() + fn test_typeof(&self, analyzer: &mut Analyzer<'a>) -> TypeofResult { + self.val.test_typeof(analyzer) } - fn test_truthy(&self) -> Option { - self.val.test_truthy() + fn test_truthy(&self, analyzer: &mut Analyzer<'a>) -> Option { + self.val.test_truthy(analyzer) } - fn test_nullish(&self) -> Option { - self.val.test_nullish() + fn test_nullish(&self, analyzer: &mut Analyzer<'a>) -> Option { + self.val.test_nullish(analyzer) } } @@ -164,7 +169,7 @@ impl<'a, T: ConsumableTrait<'a> + 'a> ComputedEntity<'a, T> { } } - pub fn forward_value(&self, val: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + pub fn forward_value(&self, val: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { analyzer.factory.computed(val, self.dep.cloned()) } } diff --git a/src/entity/consumed_object.rs b/src/entity/consumed_object.rs index 85b6af9a..48e59621 100644 --- a/src/entity/consumed_object.rs +++ b/src/entity/consumed_object.rs @@ -133,11 +133,11 @@ pub fn iterate<'a>(analyzer: &mut Analyzer<'a>, dep: Consumable<'a>) -> Iterated } } -pub fn get_to_string<'a>(analyzer: &Analyzer<'a>) -> Entity<'a> { +pub fn get_to_string<'a>(analyzer: &mut Analyzer<'a>) -> Entity<'a> { analyzer.factory.unknown_string } -pub fn get_to_numeric<'a>(analyzer: &Analyzer<'a>) -> Entity<'a> { +pub fn get_to_numeric<'a>(analyzer: &mut Analyzer<'a>) -> Entity<'a> { // Possibly number or bigint analyzer.factory.unknown() } diff --git a/src/entity/entity.rs b/src/entity/entity.rs index f3dd9e59..6dd5bea6 100644 --- a/src/entity/entity.rs +++ b/src/entity/entity.rs @@ -64,21 +64,26 @@ pub trait EntityTrait<'a>: Debug { dep: Consumable<'a>, ) -> IteratedElements<'a>; - fn get_destructable(&self, rc: Entity<'a>, dep: Consumable<'a>) -> Consumable<'a>; - fn get_typeof(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a>; - fn get_to_string(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a>; - fn get_to_numeric(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a>; - fn get_to_boolean(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a>; - fn get_to_property_key(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a>; - fn get_to_jsx_child(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a>; + fn get_destructable( + &self, + rc: Entity<'a>, + analyzer: &mut Analyzer<'a>, + dep: Consumable<'a>, + ) -> Consumable<'a>; + fn get_typeof(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a>; + fn get_to_string(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a>; + fn get_to_numeric(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a>; + fn get_to_boolean(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a>; + fn get_to_property_key(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a>; + fn get_to_jsx_child(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a>; fn get_to_literals( &self, _rc: Entity<'a>, - _analyzer: &Analyzer<'a>, + _analyzer: &mut Analyzer<'a>, ) -> Option>> { None } - fn get_literal(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Option> { + fn get_literal(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Option> { self.get_to_literals(rc, analyzer).and_then(|set| { if set.len() == 1 { set.into_iter().next() @@ -88,11 +93,11 @@ pub trait EntityTrait<'a>: Debug { }) } - fn test_typeof(&self) -> TypeofResult; - fn test_truthy(&self) -> Option; - fn test_nullish(&self) -> Option; - fn test_is_undefined(&self) -> Option { - let t = self.test_typeof(); + fn test_typeof(&self, analyzer: &mut Analyzer<'a>) -> TypeofResult; + fn test_truthy(&self, analyzer: &mut Analyzer<'a>) -> Option; + fn test_nullish(&self, analyzer: &mut Analyzer<'a>) -> Option; + fn test_is_undefined(&self, analyzer: &mut Analyzer<'a>) -> Option { + let t = self.test_typeof(analyzer); match (t == TypeofResult::Undefined, t.contains(TypeofResult::Undefined)) { (true, _) => Some(true), (false, true) => None, @@ -198,56 +203,63 @@ impl<'a> Entity<'a> { self.0.iterate(*self, analyzer, dep.into()) } - pub fn get_destructable(&self, dep: impl Into>) -> Consumable<'a> { - self.0.get_destructable(*self, dep.into()) + pub fn get_destructable( + &self, + analyzer: &mut Analyzer<'a>, + dep: impl Into>, + ) -> Consumable<'a> { + self.0.get_destructable(*self, analyzer, dep.into()) } - pub fn get_typeof(&self, analyzer: &Analyzer<'a>) -> Entity<'a> { + pub fn get_typeof(&self, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.0.get_typeof(*self, analyzer) } - pub fn get_to_string(&self, analyzer: &Analyzer<'a>) -> Entity<'a> { + pub fn get_to_string(&self, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.0.get_to_string(*self, analyzer) } - pub fn get_to_numeric(&self, analyzer: &Analyzer<'a>) -> Entity<'a> { + pub fn get_to_numeric(&self, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.0.get_to_numeric(*self, analyzer) } - pub fn get_to_boolean(&self, analyzer: &Analyzer<'a>) -> Entity<'a> { + pub fn get_to_boolean(&self, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.0.get_to_boolean(*self, analyzer) } - pub fn get_to_property_key(&self, analyzer: &Analyzer<'a>) -> Entity<'a> { + pub fn get_to_property_key(&self, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.0.get_to_property_key(*self, analyzer) } - pub fn get_to_jsx_child(&self, analyzer: &Analyzer<'a>) -> Entity<'a> { + pub fn get_to_jsx_child(&self, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.0.get_to_jsx_child(*self, analyzer) } - pub fn get_to_literals(&self, analyzer: &Analyzer<'a>) -> Option>> { + pub fn get_to_literals( + &self, + analyzer: &mut Analyzer<'a>, + ) -> Option>> { self.0.get_to_literals(*self, analyzer) } - pub fn get_literal(&self, analyzer: &Analyzer<'a>) -> Option> { + pub fn get_literal(&self, analyzer: &mut Analyzer<'a>) -> Option> { self.0.get_literal(*self, analyzer) } - pub fn test_typeof(&self) -> TypeofResult { - self.0.test_typeof() + pub fn test_typeof(&self, analyzer: &mut Analyzer<'a>) -> TypeofResult { + self.0.test_typeof(analyzer) } - pub fn test_truthy(&self) -> Option { - self.0.test_truthy() + pub fn test_truthy(&self, analyzer: &mut Analyzer<'a>) -> Option { + self.0.test_truthy(analyzer) } - pub fn test_nullish(&self) -> Option { - self.0.test_nullish() + pub fn test_nullish(&self, analyzer: &mut Analyzer<'a>) -> Option { + self.0.test_nullish(analyzer) } - pub fn test_is_undefined(&self) -> Option { - self.0.test_is_undefined() + pub fn test_is_undefined(&self, analyzer: &mut Analyzer<'a>) -> Option { + self.0.test_is_undefined(analyzer) } pub fn destruct_as_array( diff --git a/src/entity/function.rs b/src/entity/function.rs index a1d67ca1..fef97cbd 100644 --- a/src/entity/function.rs +++ b/src/entity/function.rs @@ -141,37 +141,42 @@ impl<'a> EntityTrait<'a> for FunctionEntity<'a> { consumed_object::iterate(analyzer, dep) } - fn get_destructable(&self, rc: Entity<'a>, dep: Consumable<'a>) -> Consumable<'a> { + fn get_destructable( + &self, + rc: Entity<'a>, + _analyzer: &mut Analyzer<'a>, + dep: Consumable<'a>, + ) -> Consumable<'a> { box_consumable((rc, dep)) } - fn get_typeof(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_typeof(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { analyzer.factory.string("function") } - fn get_to_string(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_string(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { if self.consumed.get() { return consumed_object::get_to_string(analyzer); } analyzer.factory.computed_unknown_string(rc) } - fn get_to_numeric(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_numeric(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { if self.consumed.get() { return consumed_object::get_to_numeric(analyzer); } analyzer.factory.nan } - fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { analyzer.factory.boolean(true) } - fn get_to_property_key(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_property_key(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.get_to_string(rc, analyzer) } - fn get_to_jsx_child(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_jsx_child(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { if self.consumed.get() { analyzer.factory.immutable_unknown } else { @@ -180,15 +185,15 @@ impl<'a> EntityTrait<'a> for FunctionEntity<'a> { } } - fn test_typeof(&self) -> TypeofResult { + fn test_typeof(&self, _analyzer: &mut Analyzer<'a>) -> TypeofResult { TypeofResult::Function } - fn test_truthy(&self) -> Option { + fn test_truthy(&self, _analyzer: &mut Analyzer<'a>) -> Option { Some(true) } - fn test_nullish(&self) -> Option { + fn test_nullish(&self, _analyzer: &mut Analyzer<'a>) -> Option { Some(false) } } diff --git a/src/entity/literal.rs b/src/entity/literal.rs index 528b8179..d00b9c06 100644 --- a/src/entity/literal.rs +++ b/src/entity/literal.rs @@ -186,19 +186,24 @@ impl<'a> EntityTrait<'a> for LiteralEntity<'a> { } } - fn get_destructable(&self, _rc: Entity<'a>, dep: Consumable<'a>) -> Consumable<'a> { + fn get_destructable( + &self, + _rc: Entity<'a>, + _analyzer: &mut Analyzer<'a>, + dep: Consumable<'a>, + ) -> Consumable<'a> { dep } - fn get_typeof(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { - analyzer.factory.string(self.test_typeof().to_string().unwrap()) + fn get_typeof(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { + analyzer.factory.string(self.test_typeof(analyzer).to_string().unwrap()) } - fn get_to_string(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_string(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { analyzer.factory.string(self.to_string(analyzer.allocator)) } - fn get_to_numeric(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_numeric(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { match self { LiteralEntity::Number(_, _) | LiteralEntity::BigInt(_) @@ -232,22 +237,22 @@ impl<'a> EntityTrait<'a> for LiteralEntity<'a> { } } - fn get_to_boolean(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { - match self.test_truthy() { + fn get_to_boolean(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { + match self.test_truthy(analyzer) { Some(value) => analyzer.factory.boolean(value), None => analyzer.factory.computed_unknown_boolean(rc), } } - fn get_to_property_key(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_property_key(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { match self { LiteralEntity::Symbol(_, _) => rc, _ => self.get_to_string(rc, analyzer), } } - fn get_to_jsx_child(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { - if (TypeofResult::String | TypeofResult::Number).contains(self.test_typeof()) { + fn get_to_jsx_child(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { + if (TypeofResult::String | TypeofResult::Number).contains(self.test_typeof(analyzer)) { self.get_to_string(rc, analyzer) } else { analyzer.factory.string("") @@ -257,18 +262,22 @@ impl<'a> EntityTrait<'a> for LiteralEntity<'a> { fn get_to_literals( &self, _rc: Entity<'a>, - _analyzer: &Analyzer<'a>, + _analyzer: &mut Analyzer<'a>, ) -> Option>> { let mut result = FxHashSet::default(); result.insert(*self); Some(result) } - fn get_literal(&self, _rc: Entity<'a>, _analyzer: &Analyzer<'a>) -> Option> { + fn get_literal( + &self, + _rc: Entity<'a>, + _analyzer: &mut Analyzer<'a>, + ) -> Option> { Some(*self) } - fn test_typeof(&self) -> TypeofResult { + fn test_typeof(&self, _analyzer: &mut Analyzer<'a>) -> TypeofResult { match self { LiteralEntity::String(_) => TypeofResult::String, LiteralEntity::Number(_, _) => TypeofResult::Number, @@ -282,7 +291,7 @@ impl<'a> EntityTrait<'a> for LiteralEntity<'a> { } } - fn test_truthy(&self) -> Option { + fn test_truthy(&self, _analyzer: &mut Analyzer<'a>) -> Option { Some(match self { LiteralEntity::String(value) => !value.is_empty(), LiteralEntity::Number(value, _) => *value != 0.0.into() && *value != (-0.0).into(), @@ -294,7 +303,7 @@ impl<'a> EntityTrait<'a> for LiteralEntity<'a> { }) } - fn test_nullish(&self) -> Option { + fn test_nullish(&self, _analyzer: &mut Analyzer<'a>) -> Option { Some(matches!(self, LiteralEntity::Null | LiteralEntity::Undefined)) } } @@ -384,7 +393,7 @@ impl<'a> LiteralEntity<'a> { } } - pub fn can_build_expr(&self, analyzer: &Analyzer<'a>) -> bool { + pub fn can_build_expr(&self, analyzer: &mut Analyzer<'a>) -> bool { let config = &analyzer.config; match self { LiteralEntity::String(value) => value.len() <= config.max_simple_string_length, @@ -479,7 +488,7 @@ impl<'a> LiteralEntity<'a> { fn get_known_instance_property( &self, - analyzer: &Analyzer<'a>, + analyzer: &mut Analyzer<'a>, key: LiteralEntity<'a>, ) -> Option> { match self { diff --git a/src/entity/logical_result.rs b/src/entity/logical_result.rs index 3c48d81f..0c0d396f 100644 --- a/src/entity/logical_result.rs +++ b/src/entity/logical_result.rs @@ -2,7 +2,7 @@ use oxc::ast::ast::LogicalOperator; use super::{ entity::{EnumeratedProperties, IteratedElements}, - Entity, EntityFactory, EntityTrait, TypeofResult, + Entity, EntityTrait, TypeofResult, }; use crate::{analyzer::Analyzer, consumable::Consumable}; @@ -99,23 +99,28 @@ impl<'a> EntityTrait<'a> for LogicalResultEntity<'a> { self.value.iterate(analyzer, dep) } - fn get_destructable(&self, _rc: Entity<'a>, dep: Consumable<'a>) -> Consumable<'a> { - self.value.get_destructable(dep) + fn get_destructable( + &self, + _rc: Entity<'a>, + analyzer: &mut Analyzer<'a>, + dep: Consumable<'a>, + ) -> Consumable<'a> { + self.value.get_destructable(analyzer, dep) } - fn get_typeof(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_typeof(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.value.get_typeof(analyzer) } - fn get_to_string(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_string(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.value.get_to_string(analyzer) } - fn get_to_numeric(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_numeric(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.value.get_to_numeric(analyzer) } - fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { let value = self.value.get_to_boolean(analyzer); if self.is_coalesce { value @@ -126,56 +131,56 @@ impl<'a> EntityTrait<'a> for LogicalResultEntity<'a> { } } - fn get_to_property_key(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_property_key(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.value.get_to_property_key(analyzer) } - fn get_to_jsx_child(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_jsx_child(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.value.get_to_jsx_child(analyzer) } - fn test_typeof(&self) -> TypeofResult { - self.value.test_typeof() + fn test_typeof(&self, analyzer: &mut Analyzer<'a>) -> TypeofResult { + self.value.test_typeof(analyzer) } - fn test_truthy(&self) -> Option { + fn test_truthy(&self, analyzer: &mut Analyzer<'a>) -> Option { if self.is_coalesce { - self.value.test_truthy() + self.value.test_truthy(analyzer) } else { self.result } } - fn test_nullish(&self) -> Option { + fn test_nullish(&self, analyzer: &mut Analyzer<'a>) -> Option { if self.is_coalesce { self.result } else { - self.value.test_nullish() + self.value.test_nullish(analyzer) } } } -impl<'a> EntityFactory<'a> { +impl<'a> Analyzer<'a> { /// Only used when (maybe_left, maybe_right) == (true, true) pub fn logical_result( - &self, + &mut self, left: Entity<'a>, right: Entity<'a>, operator: LogicalOperator, ) -> Entity<'a> { - self.entity(LogicalResultEntity { - value: self.union((left, right)), + self.factory.entity(LogicalResultEntity { + value: self.factory.union((left, right)), is_coalesce: operator == LogicalOperator::Coalesce, result: match operator { - LogicalOperator::Or => match right.test_truthy() { + LogicalOperator::Or => match right.test_truthy(self) { Some(true) => Some(true), _ => None, }, - LogicalOperator::And => match right.test_truthy() { + LogicalOperator::And => match right.test_truthy(self) { Some(false) => Some(false), _ => None, }, - LogicalOperator::Coalesce => match right.test_nullish() { + LogicalOperator::Coalesce => match right.test_nullish(self) { Some(false) => Some(false), _ => None, }, diff --git a/src/entity/object.rs b/src/entity/object.rs index da0971e8..c79680fa 100644 --- a/src/entity/object.rs +++ b/src/entity/object.rs @@ -60,7 +60,7 @@ impl<'a> Default for ObjectProperty<'a> { impl<'a> ObjectProperty<'a> { pub fn get( &mut self, - analyzer: &Analyzer<'a>, + analyzer: &mut Analyzer<'a>, values: &mut Vec>, getters: &mut Vec>, non_existent: &mut Vec>, @@ -513,15 +513,20 @@ impl<'a> EntityTrait<'a> for ObjectEntity<'a> { consumed_object::iterate(analyzer, dep) } - fn get_destructable(&self, _rc: Entity<'a>, dep: Consumable<'a>) -> Consumable<'a> { + fn get_destructable( + &self, + _rc: Entity<'a>, + _analyzer: &mut Analyzer<'a>, + dep: Consumable<'a>, + ) -> Consumable<'a> { dep } - fn get_typeof(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_typeof(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { analyzer.factory.string("object") } - fn get_to_string(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_string(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { // FIXME: Special methods if self.consumed.get() { return consumed_object::get_to_string(analyzer); @@ -529,7 +534,7 @@ impl<'a> EntityTrait<'a> for ObjectEntity<'a> { analyzer.factory.computed_unknown_string(rc) } - fn get_to_numeric(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_numeric(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { // FIXME: Special methods if self.consumed.get() { return consumed_object::get_to_numeric(analyzer); @@ -537,27 +542,27 @@ impl<'a> EntityTrait<'a> for ObjectEntity<'a> { analyzer.factory.computed_unknown(rc) } - fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { analyzer.factory.boolean(true) } - fn get_to_property_key(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_property_key(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.get_to_string(rc, analyzer) } - fn get_to_jsx_child(&self, rc: Entity<'a>, _analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_jsx_child(&self, rc: Entity<'a>, _analyzer: &mut Analyzer<'a>) -> Entity<'a> { rc } - fn test_typeof(&self) -> TypeofResult { + fn test_typeof(&self, _analyzer: &mut Analyzer<'a>) -> TypeofResult { TypeofResult::Object } - fn test_truthy(&self) -> Option { + fn test_truthy(&self, _analyzer: &mut Analyzer<'a>) -> Option { Some(true) } - fn test_nullish(&self) -> Option { + fn test_nullish(&self, _analyzer: &mut Analyzer<'a>) -> Option { Some(false) } } diff --git a/src/entity/operations.rs b/src/entity/operations.rs index 3cf6a232..435fb07f 100644 --- a/src/entity/operations.rs +++ b/src/entity/operations.rs @@ -15,25 +15,25 @@ impl<'a> EntityOpHost<'a> { Self { allocator } } - pub fn eq(&self, analyzer: &Analyzer<'a>, lhs: Entity<'a>, rhs: Entity<'a>) -> Option { + pub fn eq(&self, analyzer: &mut Analyzer<'a>, lhs: Entity<'a>, rhs: Entity<'a>) -> Option { if self.strict_eq(analyzer, lhs, rhs) == Some(true) { return Some(true); } - if lhs.test_nullish() == Some(true) && rhs.test_nullish() == Some(true) { + if lhs.test_nullish(analyzer) == Some(true) && rhs.test_nullish(analyzer) == Some(true) { return Some(true); } None } - pub fn neq(&self, analyzer: &Analyzer<'a>, lhs: Entity<'a>, rhs: Entity<'a>) -> Option { + pub fn neq(&self, analyzer: &mut Analyzer<'a>, lhs: Entity<'a>, rhs: Entity<'a>) -> Option { self.eq(analyzer, lhs, rhs).map(|v| !v) } pub fn strict_eq( &self, - analyzer: &Analyzer<'a>, + analyzer: &mut Analyzer<'a>, lhs: Entity<'a>, rhs: Entity<'a>, ) -> Option { @@ -42,8 +42,8 @@ impl<'a> EntityOpHost<'a> { // return Some(true); // } - let lhs_t = lhs.test_typeof(); - let rhs_t = rhs.test_typeof(); + let lhs_t = lhs.test_typeof(analyzer); + let rhs_t = rhs.test_typeof(analyzer); if lhs_t & rhs_t == TypeofResult::_None { return Some(false); } @@ -76,7 +76,7 @@ impl<'a> EntityOpHost<'a> { pub fn strict_neq( &self, - analyzer: &Analyzer<'a>, + analyzer: &mut Analyzer<'a>, lhs: Entity<'a>, rhs: Entity<'a>, ) -> Option { @@ -85,7 +85,7 @@ impl<'a> EntityOpHost<'a> { pub fn lt( &self, - analyzer: &Analyzer<'a>, + analyzer: &mut Analyzer<'a>, lhs: Entity<'a>, rhs: Entity<'a>, eq: bool, @@ -139,7 +139,7 @@ impl<'a> EntityOpHost<'a> { pub fn gt( &self, - analyzer: &Analyzer<'a>, + analyzer: &mut Analyzer<'a>, lhs: Entity<'a>, rhs: Entity<'a>, eq: bool, @@ -147,15 +147,20 @@ impl<'a> EntityOpHost<'a> { self.lt(analyzer, rhs, lhs, eq) } - pub fn instanceof(&self, lhs: Entity<'a>, _rhs: Entity<'a>) -> Option { + pub fn instanceof( + &self, + analyzer: &mut Analyzer<'a>, + lhs: Entity<'a>, + _rhs: Entity<'a>, + ) -> Option { if (TypeofResult::String | TypeofResult::Number | TypeofResult::BigInt | TypeofResult::Boolean | TypeofResult::Symbol | TypeofResult::Undefined) - .contains(lhs.test_typeof()) - || lhs.test_nullish() == Some(true) + .contains(lhs.test_typeof(analyzer)) + || lhs.test_nullish(analyzer) == Some(true) { Some(false) } else { @@ -163,9 +168,9 @@ impl<'a> EntityOpHost<'a> { } } - pub fn add(&self, analyzer: &Analyzer<'a>, lhs: Entity<'a>, rhs: Entity<'a>) -> Entity<'a> { - let lhs_t = lhs.test_typeof(); - let rhs_t = rhs.test_typeof(); + pub fn add(&self, analyzer: &mut Analyzer<'a>, lhs: Entity<'a>, rhs: Entity<'a>) -> Entity<'a> { + let lhs_t = lhs.test_typeof(analyzer); + let rhs_t = rhs.test_typeof(analyzer); let lhs_lit = lhs.get_literal(analyzer); let rhs_lit = rhs.get_literal(analyzer); @@ -231,7 +236,7 @@ impl<'a> EntityOpHost<'a> { fn number_only_op( &self, - analyzer: &Analyzer<'a>, + analyzer: &mut Analyzer<'a>, lhs: Entity<'a>, rhs: Entity<'a>, calc: fn(f64, f64) -> f64, @@ -250,7 +255,7 @@ impl<'a> EntityOpHost<'a> { pub fn update( &self, - analyzer: &Analyzer<'a>, + analyzer: &mut Analyzer<'a>, input: Entity<'a>, operator: UpdateOperator, ) -> Entity<'a> { @@ -272,7 +277,7 @@ impl<'a> EntityOpHost<'a> { ); } - let input_t = input.test_typeof(); + let input_t = input.test_typeof(analyzer); let mut values = vec![]; if input_t.contains(TypeofResult::BigInt) { @@ -291,23 +296,48 @@ impl<'a> EntityOpHost<'a> { pub fn binary_op( &self, - analyzer: &Analyzer<'a>, + analyzer: &mut Analyzer<'a>, operator: BinaryOperator, lhs: Entity<'a>, rhs: Entity<'a>, ) -> Entity<'a> { - let to_result = - |result: Option| boolean_from_test_result(analyzer, result, (lhs.clone(), rhs.clone())); + let to_entity = |analyzer: &mut Analyzer<'a>, result: Option| { + boolean_from_test_result(analyzer, result, (lhs.clone(), rhs.clone())) + }; match operator { - BinaryOperator::Equality => to_result(self.eq(analyzer, lhs, rhs)), - BinaryOperator::Inequality => to_result(self.neq(analyzer, lhs, rhs)), - BinaryOperator::StrictEquality => to_result(self.strict_eq(analyzer, lhs, rhs)), - BinaryOperator::StrictInequality => to_result(self.strict_neq(analyzer, lhs, rhs)), - BinaryOperator::LessThan => to_result(self.lt(analyzer, lhs, rhs, false)), - BinaryOperator::LessEqualThan => to_result(self.lt(analyzer, lhs, rhs, true)), - BinaryOperator::GreaterThan => to_result(self.gt(analyzer, lhs, rhs, false)), - BinaryOperator::GreaterEqualThan => to_result(self.gt(analyzer, lhs, rhs, true)), + BinaryOperator::Equality => { + let result = self.eq(analyzer, lhs, rhs); + to_entity(analyzer, result) + } + BinaryOperator::Inequality => { + let result = self.neq(analyzer, lhs, rhs); + to_entity(analyzer, result) + } + BinaryOperator::StrictEquality => { + let result = self.strict_eq(analyzer, lhs, rhs); + to_entity(analyzer, result) + } + BinaryOperator::StrictInequality => { + let result = self.strict_neq(analyzer, lhs, rhs); + to_entity(analyzer, result) + } + BinaryOperator::LessThan => { + let result = self.lt(analyzer, lhs, rhs, false); + to_entity(analyzer, result) + } + BinaryOperator::LessEqualThan => { + let result = self.lt(analyzer, lhs, rhs, true); + to_entity(analyzer, result) + } + BinaryOperator::GreaterThan => { + let result = self.gt(analyzer, lhs, rhs, false); + to_entity(analyzer, result) + } + BinaryOperator::GreaterEqualThan => { + let result = self.gt(analyzer, lhs, rhs, true); + to_entity(analyzer, result) + } BinaryOperator::Addition => self.add(analyzer, lhs, rhs), BinaryOperator::Subtraction => self.number_only_op(analyzer, lhs, rhs, |l, r| l - r), BinaryOperator::Multiplication => self.number_only_op(analyzer, lhs, rhs, |l, r| l * r), @@ -333,7 +363,10 @@ impl<'a> EntityOpHost<'a> { } BinaryOperator::Exponential => self.number_only_op(analyzer, lhs, rhs, |l, r| l.powf(r)), BinaryOperator::In => analyzer.factory.computed_unknown_boolean((lhs.clone(), rhs.clone())), - BinaryOperator::Instanceof => to_result(self.instanceof(lhs, rhs)), + BinaryOperator::Instanceof => { + let result = self.instanceof(analyzer, lhs, rhs); + to_entity(analyzer, result) + } } } } diff --git a/src/entity/primitive.rs b/src/entity/primitive.rs index 49649e00..db7bd326 100644 --- a/src/entity/primitive.rs +++ b/src/entity/primitive.rs @@ -124,38 +124,43 @@ impl<'a> EntityTrait<'a> for PrimitiveEntity { consumed_object::iterate(analyzer, dep) } - fn get_destructable(&self, rc: Entity<'a>, dep: Consumable<'a>) -> Consumable<'a> { + fn get_destructable( + &self, + rc: Entity<'a>, + _analyzer: &mut Analyzer<'a>, + dep: Consumable<'a>, + ) -> Consumable<'a> { box_consumable((rc, dep)) } - fn get_typeof(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { - if let Some(str) = self.test_typeof().to_string() { + fn get_typeof(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { + if let Some(str) = self.test_typeof(analyzer).to_string() { analyzer.factory.string(str) } else { analyzer.factory.unknown_string } } - fn get_to_string(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_string(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { analyzer.factory.unknown_string } - fn get_to_numeric(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_numeric(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { analyzer.factory.unknown() } - fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { - match self.test_truthy() { + fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { + match self.test_truthy(analyzer) { Some(val) => analyzer.factory.boolean(val), None => analyzer.factory.unknown_boolean, } } - fn get_to_property_key(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_property_key(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { analyzer.factory.unknown() } - fn get_to_jsx_child(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_jsx_child(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { if matches!(self, PrimitiveEntity::Mixed | PrimitiveEntity::String | PrimitiveEntity::Number) { analyzer.factory.unknown_string } else { @@ -163,7 +168,7 @@ impl<'a> EntityTrait<'a> for PrimitiveEntity { } } - fn test_typeof(&self) -> TypeofResult { + fn test_typeof(&self, _analyzer: &mut Analyzer<'a>) -> TypeofResult { match self { PrimitiveEntity::String => TypeofResult::String, PrimitiveEntity::Number => TypeofResult::Number, @@ -174,14 +179,14 @@ impl<'a> EntityTrait<'a> for PrimitiveEntity { } } - fn test_truthy(&self) -> Option { + fn test_truthy(&self, _analyzer: &mut Analyzer<'a>) -> Option { match self { PrimitiveEntity::Symbol => Some(true), _ => None, } } - fn test_nullish(&self) -> Option { + fn test_nullish(&self, _analyzer: &mut Analyzer<'a>) -> Option { Some(false) } } diff --git a/src/entity/pure_result.rs b/src/entity/pure_result.rs index 70b288bc..1fad325b 100644 --- a/src/entity/pure_result.rs +++ b/src/entity/pure_result.rs @@ -20,7 +20,6 @@ pub enum PureCallNode<'a> { pub struct PureResult<'a> { pub node: PureCallNode<'a>, pub result: OnceCell>, - pub referred_deps: ReferredDeps, } impl<'a> EntityTrait<'a> for PureResult<'a> { @@ -109,44 +108,49 @@ impl<'a> EntityTrait<'a> for PureResult<'a> { self.value(analyzer).iterate(analyzer, dep) } - fn get_destructable(&self, _rc: Entity<'a>, dep: Consumable<'a>) -> Consumable<'a> { - self.value(analyzer).get_destructable(dep) + fn get_destructable( + &self, + _rc: Entity<'a>, + analyzer: &mut Analyzer<'a>, + dep: Consumable<'a>, + ) -> Consumable<'a> { + self.value(analyzer).get_destructable(analyzer, dep) } - fn get_typeof(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_typeof(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.value(analyzer).get_typeof(analyzer) } - fn get_to_string(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_string(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.value(analyzer).get_to_string(analyzer) } - fn get_to_numeric(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_numeric(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.value(analyzer).get_to_numeric(analyzer) } - fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.value(analyzer).get_to_boolean(analyzer) } - fn get_to_property_key(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_property_key(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.value(analyzer).get_to_property_key(analyzer) } - fn get_to_jsx_child(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_jsx_child(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { self.value(analyzer).get_to_jsx_child(analyzer) } - fn test_typeof(&self) -> TypeofResult { - self.value(analyzer).test_typeof() + fn test_typeof(&self, analyzer: &mut Analyzer<'a>) -> TypeofResult { + self.value(analyzer).test_typeof(analyzer) } - fn test_truthy(&self) -> Option { - self.value(analyzer).test_truthy() + fn test_truthy(&self, analyzer: &mut Analyzer<'a>) -> Option { + self.value(analyzer).test_truthy(analyzer) } - fn test_nullish(&self) -> Option { - self.value(analyzer).test_nullish() + fn test_nullish(&self, analyzer: &mut Analyzer<'a>) -> Option { + self.value(analyzer).test_nullish(analyzer) } } diff --git a/src/entity/react_element.rs b/src/entity/react_element.rs index 20c6d5b9..409cd646 100644 --- a/src/entity/react_element.rs +++ b/src/entity/react_element.rs @@ -128,46 +128,51 @@ impl<'a> EntityTrait<'a> for ReactElementEntity<'a> { consumed_object::iterate(analyzer, dep) } - fn get_destructable(&self, rc: Entity<'a>, dep: Consumable<'a>) -> Consumable<'a> { + fn get_destructable( + &self, + rc: Entity<'a>, + _analyzer: &mut Analyzer<'a>, + dep: Consumable<'a>, + ) -> Consumable<'a> { box_consumable((rc, dep)) } - fn get_typeof(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_typeof(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { analyzer.factory.computed_unknown_string(rc) } - fn get_to_string(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_string(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { analyzer.factory.computed_unknown_string(rc) } - fn get_to_numeric(&self, rc: Entity<'a>, _analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_numeric(&self, rc: Entity<'a>, _analyzer: &mut Analyzer<'a>) -> Entity<'a> { rc } - fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { - match self.test_truthy() { + fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { + match self.test_truthy(analyzer) { Some(val) => analyzer.factory.boolean(val), None => analyzer.factory.unknown_boolean, } } - fn get_to_property_key(&self, rc: Entity<'a>, _analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_property_key(&self, rc: Entity<'a>, _analyzer: &mut Analyzer<'a>) -> Entity<'a> { rc } - fn get_to_jsx_child(&self, rc: Entity<'a>, _analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_jsx_child(&self, rc: Entity<'a>, _analyzer: &mut Analyzer<'a>) -> Entity<'a> { rc } - fn test_typeof(&self) -> TypeofResult { + fn test_typeof(&self, _analyzer: &mut Analyzer<'a>) -> TypeofResult { TypeofResult::_Unknown } - fn test_truthy(&self) -> Option { + fn test_truthy(&self, _analyzer: &mut Analyzer<'a>) -> Option { None } - fn test_nullish(&self) -> Option { + fn test_nullish(&self, _analyzer: &mut Analyzer<'a>) -> Option { None } } diff --git a/src/entity/union.rs b/src/entity/union.rs index e3f214a6..342ad497 100644 --- a/src/entity/union.rs +++ b/src/entity/union.rs @@ -149,43 +149,48 @@ impl<'a, V: UnionLike<'a, Entity<'a>> + Debug + 'a> EntityTrait<'a> for UnionEnt (vec![], analyzer.factory.try_union(results), box_consumable(())) } - fn get_destructable(&self, _rc: Entity<'a>, dep: Consumable<'a>) -> Consumable<'a> { + fn get_destructable( + &self, + _rc: Entity<'a>, + analyzer: &mut Analyzer<'a>, + dep: Consumable<'a>, + ) -> Consumable<'a> { let mut values = Vec::new(); for entity in self.values.iter() { - values.push(entity.get_destructable(dep.cloned())); + values.push(entity.get_destructable(analyzer, dep.cloned())); } box_consumable(values) } - fn get_typeof(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_typeof(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { // TODO: collect literals let values = self.values.map(|v| v.get_typeof(analyzer)); analyzer.factory.union(values) } - fn get_to_string(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_string(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { // TODO: dedupe let values = self.values.map(|v| v.get_to_string(analyzer)); analyzer.factory.union(values) } - fn get_to_numeric(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_numeric(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { // TODO: dedupe let values = self.values.map(|v| v.get_to_numeric(analyzer)); analyzer.factory.union(values) } - fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { let values = self.values.map(|v| v.get_to_boolean(analyzer)); analyzer.factory.union(values) } - fn get_to_property_key(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_property_key(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { let values = self.values.map(|v| v.get_to_property_key(analyzer)); analyzer.factory.union(values) } - fn get_to_jsx_child(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_jsx_child(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { let values = self.values.map(|v| v.get_to_jsx_child(analyzer)); analyzer.factory.union(values) } @@ -193,7 +198,7 @@ impl<'a, V: UnionLike<'a, Entity<'a>> + Debug + 'a> EntityTrait<'a> for UnionEnt fn get_to_literals( &self, _rc: Entity<'a>, - analyzer: &Analyzer<'a>, + analyzer: &mut Analyzer<'a>, ) -> Option>> { let mut iter = self.values.iter(); let mut result = iter.next().unwrap().get_to_literals(analyzer)?; @@ -203,30 +208,30 @@ impl<'a, V: UnionLike<'a, Entity<'a>> + Debug + 'a> EntityTrait<'a> for UnionEnt Some(result) } - fn test_typeof(&self) -> TypeofResult { + fn test_typeof(&self, analyzer: &mut Analyzer<'a>) -> TypeofResult { let mut result = TypeofResult::_None; for entity in self.values.iter() { - result |= entity.test_typeof(); + result |= entity.test_typeof(analyzer); } result } - fn test_truthy(&self) -> Option { + fn test_truthy(&self, analyzer: &mut Analyzer<'a>) -> Option { let mut iter = self.values.iter(); - let result = iter.next().unwrap().test_truthy()?; + let result = iter.next().unwrap().test_truthy(analyzer)?; for entity in iter { - if entity.test_truthy()? != result { + if entity.test_truthy(analyzer)? != result { return None; } } Some(result) } - fn test_nullish(&self) -> Option { + fn test_nullish(&self, analyzer: &mut Analyzer<'a>) -> Option { let mut iter = self.values.iter(); - let result = iter.next().unwrap().test_nullish()?; + let result = iter.next().unwrap().test_nullish(analyzer)?; for entity in iter { - if entity.test_nullish()? != result { + if entity.test_nullish(analyzer)? != result { return None; } } diff --git a/src/entity/unknown.rs b/src/entity/unknown.rs index 5806e6f1..65a8d727 100644 --- a/src/entity/unknown.rs +++ b/src/entity/unknown.rs @@ -103,46 +103,51 @@ impl<'a> EntityTrait<'a> for UnknownEntity<'a> { consumed_object::iterate(analyzer, dep) } - fn get_destructable(&self, rc: Entity<'a>, dep: Consumable<'a>) -> Consumable<'a> { + fn get_destructable( + &self, + rc: Entity<'a>, + _analyzer: &mut Analyzer<'a>, + dep: Consumable<'a>, + ) -> Consumable<'a> { box_consumable((rc, dep)) } - fn get_typeof(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_typeof(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { analyzer.factory.computed_unknown_string(rc) } - fn get_to_string(&self, rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_string(&self, rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { analyzer.factory.computed_unknown_string(rc) } - fn get_to_numeric(&self, rc: Entity<'a>, _analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_numeric(&self, rc: Entity<'a>, _analyzer: &mut Analyzer<'a>) -> Entity<'a> { rc } - fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &Analyzer<'a>) -> Entity<'a> { - match self.test_truthy() { + fn get_to_boolean(&self, _rc: Entity<'a>, analyzer: &mut Analyzer<'a>) -> Entity<'a> { + match self.test_truthy(analyzer) { Some(val) => analyzer.factory.boolean(val), None => analyzer.factory.unknown_boolean, } } - fn get_to_property_key(&self, rc: Entity<'a>, _analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_property_key(&self, rc: Entity<'a>, _analyzer: &mut Analyzer<'a>) -> Entity<'a> { rc } - fn get_to_jsx_child(&self, rc: Entity<'a>, _analyzer: &Analyzer<'a>) -> Entity<'a> { + fn get_to_jsx_child(&self, rc: Entity<'a>, _analyzer: &mut Analyzer<'a>) -> Entity<'a> { rc } - fn test_typeof(&self) -> TypeofResult { + fn test_typeof(&self, _analyzer: &mut Analyzer<'a>) -> TypeofResult { TypeofResult::_Unknown } - fn test_truthy(&self) -> Option { + fn test_truthy(&self, _analyzer: &mut Analyzer<'a>) -> Option { None } - fn test_nullish(&self) -> Option { + fn test_nullish(&self, _analyzer: &mut Analyzer<'a>) -> Option { None } } diff --git a/src/entity/utils.rs b/src/entity/utils.rs index 3d510dc1..fafbf630 100644 --- a/src/entity/utils.rs +++ b/src/entity/utils.rs @@ -4,7 +4,7 @@ use core::slice; use std::{array, iter::Copied}; pub fn boolean_from_test_result<'a>( - analyzer: &Analyzer<'a>, + analyzer: &mut Analyzer<'a>, result: Option, dep: impl ConsumableTrait<'a> + 'a, ) -> Entity<'a> { diff --git a/src/nodes/expr/assignment_expression.rs b/src/nodes/expr/assignment_expression.rs index 83b94088..4dbc85c6 100644 --- a/src/nodes/expr/assignment_expression.rs +++ b/src/nodes/expr/assignment_expression.rs @@ -15,17 +15,17 @@ impl<'a> Analyzer<'a> { let (left, cache) = self.exec_assignment_target_read(&node.left); let (maybe_left, maybe_right) = match &node.operator { - AssignmentOperator::LogicalAnd => match left.test_truthy() { + AssignmentOperator::LogicalAnd => match left.test_truthy(self) { Some(true) => (false, true), Some(false) => (true, false), None => (true, true), }, - AssignmentOperator::LogicalOr => match left.test_truthy() { + AssignmentOperator::LogicalOr => match left.test_truthy(self) { Some(true) => (true, false), Some(false) => (false, true), None => (true, true), }, - AssignmentOperator::LogicalNullish => match left.test_nullish() { + AssignmentOperator::LogicalNullish => match left.test_nullish(self) { Some(true) => (false, true), Some(false) => (true, false), None => (true, true), @@ -60,7 +60,7 @@ impl<'a> Analyzer<'a> { (true, true) => { let left = forward_left(self); let right = exec_right(self); - self.factory.logical_result(left, right, to_logical_operator(node.operator)) + self.logical_result(left, right, to_logical_operator(node.operator)) } (false, false) => { unreachable!("Logical assignment expression should have at least one side") diff --git a/src/nodes/expr/call_expression.rs b/src/nodes/expr/call_expression.rs index 8fb05cb2..e29af7e8 100644 --- a/src/nodes/expr/call_expression.rs +++ b/src/nodes/expr/call_expression.rs @@ -30,7 +30,7 @@ impl<'a> Analyzer<'a> { if let Some((callee_indeterminate, callee, this)) = callee { let self_indeterminate = if node.optional { - match callee.test_nullish() { + match callee.test_nullish(self) { Some(true) => return (Some(true), self.factory.undefined), Some(false) => false, None => true, diff --git a/src/nodes/expr/conditional_expression.rs b/src/nodes/expr/conditional_expression.rs index 97a8e3fe..1389912b 100644 --- a/src/nodes/expr/conditional_expression.rs +++ b/src/nodes/expr/conditional_expression.rs @@ -8,7 +8,7 @@ impl<'a> Analyzer<'a> { pub fn exec_conditional_expression(&mut self, node: &'a ConditionalExpression<'a>) -> Entity<'a> { let test = self.exec_expression(&node.test); - let (maybe_true, maybe_false) = match test.test_truthy() { + let (maybe_true, maybe_false) = match test.test_truthy(self) { Some(true) => (true, false), Some(false) => (false, true), None => (true, true), diff --git a/src/nodes/expr/logical_expression.rs b/src/nodes/expr/logical_expression.rs index 4e4a3a32..d9cc61ac 100644 --- a/src/nodes/expr/logical_expression.rs +++ b/src/nodes/expr/logical_expression.rs @@ -8,17 +8,17 @@ impl<'a> Analyzer<'a> { let left = self.exec_expression(&node.left); let (maybe_left, maybe_right) = match &node.operator { - LogicalOperator::And => match left.test_truthy() { + LogicalOperator::And => match left.test_truthy(self) { Some(true) => (false, true), Some(false) => (true, false), None => (true, true), }, - LogicalOperator::Or => match left.test_truthy() { + LogicalOperator::Or => match left.test_truthy(self) { Some(true) => (true, false), Some(false) => (false, true), None => (true, true), }, - LogicalOperator::Coalesce => match left.test_nullish() { + LogicalOperator::Coalesce => match left.test_nullish(self) { Some(true) => (false, true), Some(false) => (true, false), None => (true, true), @@ -52,7 +52,7 @@ impl<'a> Analyzer<'a> { (true, true) => { let left = forward_left(self); let right = exec_right(self); - self.factory.logical_result(left, right, node.operator) + self.logical_result(left, right, node.operator) } (false, false) => unreachable!("Logical expression should have at least one possible branch"), }; diff --git a/src/nodes/expr/member_expression.rs b/src/nodes/expr/member_expression.rs index 6322ce87..1a65cf28 100644 --- a/src/nodes/expr/member_expression.rs +++ b/src/nodes/expr/member_expression.rs @@ -43,7 +43,7 @@ impl<'a> Analyzer<'a> { }; let self_indeterminate = if node.optional() { - match object.test_nullish() { + match object.test_nullish(self) { Some(true) => return (Some(true), self.factory.undefined, None), Some(false) => false, None => true, diff --git a/src/nodes/expr/unary_expression.rs b/src/nodes/expr/unary_expression.rs index 30420b8e..2809ea40 100644 --- a/src/nodes/expr/unary_expression.rs +++ b/src/nodes/expr/unary_expression.rs @@ -65,7 +65,7 @@ impl<'a> Analyzer<'a> { } UnaryOperator::UnaryPlus => argument.get_to_numeric(self), UnaryOperator::LogicalNot => self.factory.computed( - match argument.test_truthy() { + match argument.test_truthy(self) { Some(value) => self.factory.boolean(!value), None => self.factory.unknown_boolean, }, diff --git a/src/nodes/misc/assignment_target_pattern.rs b/src/nodes/misc/assignment_target_pattern.rs index e56de862..0d8cf13b 100644 --- a/src/nodes/misc/assignment_target_pattern.rs +++ b/src/nodes/misc/assignment_target_pattern.rs @@ -30,9 +30,10 @@ impl<'a> Analyzer<'a> { self.pop_cf_scope(); } AssignmentTargetPattern::ObjectAssignmentTarget(node) => { - self.push_dependent_cf_scope(value.get_destructable(box_consumable(()))); + let destructable_dep = value.get_destructable(self, box_consumable(())); + self.push_dependent_cf_scope(destructable_dep); - let is_nullish = value.test_nullish(); + let is_nullish = value.test_nullish(self); if is_nullish != Some(false) { if is_nullish == Some(true) { self.thrown_builtin_error("Cannot destructure nullish value"); diff --git a/src/nodes/misc/binding_pattern.rs b/src/nodes/misc/binding_pattern.rs index 4420904d..2b050825 100644 --- a/src/nodes/misc/binding_pattern.rs +++ b/src/nodes/misc/binding_pattern.rs @@ -73,7 +73,7 @@ impl<'a> Analyzer<'a> { self.factory.unknown() }); - let is_nullish = init.test_nullish(); + let is_nullish = init.test_nullish(self); if is_nullish != Some(false) { if is_nullish == Some(true) { self.thrown_builtin_error("Cannot destructure nullish value"); diff --git a/src/nodes/misc/with_default.rs b/src/nodes/misc/with_default.rs index c900bd2e..184d16b3 100644 --- a/src/nodes/misc/with_default.rs +++ b/src/nodes/misc/with_default.rs @@ -7,7 +7,7 @@ impl<'a> Analyzer<'a> { default: &'a Expression<'a>, value: Entity<'a>, ) -> (bool, Entity<'a>) { - let is_undefined = value.test_is_undefined(); + let is_undefined = value.test_is_undefined(self); self.push_dependent_cf_scope(value.to_consumable()); let binding_val = match is_undefined { diff --git a/src/nodes/stmt/do_while_statement.rs b/src/nodes/stmt/do_while_statement.rs index 93078d1f..0b86efeb 100644 --- a/src/nodes/stmt/do_while_statement.rs +++ b/src/nodes/stmt/do_while_statement.rs @@ -32,7 +32,7 @@ impl<'a> Analyzer<'a> { let test = self.exec_expression(&node.test); // The rest is the same as while statement. - if test.test_truthy() == Some(false) { + if test.test_truthy(self) == Some(false) { self.pop_cf_scope(); return; } diff --git a/src/nodes/stmt/for_in_statement.rs b/src/nodes/stmt/for_in_statement.rs index 67bd2c3a..5dc27cad 100644 --- a/src/nodes/stmt/for_in_statement.rs +++ b/src/nodes/stmt/for_in_statement.rs @@ -22,8 +22,8 @@ impl<'a> Analyzer<'a> { | TypeofResult::Symbol; // TODO: empty object, simple function, array - if (right.test_typeof() & !types_have_no_keys) == TypeofResult::_None - || right.test_nullish() == Some(true) + if (right.test_typeof(self) & !types_have_no_keys) == TypeofResult::_None + || right.test_nullish(self) == Some(true) { return; } diff --git a/src/nodes/stmt/for_statement.rs b/src/nodes/stmt/for_statement.rs index 142a4a27..f5d5ee98 100644 --- a/src/nodes/stmt/for_statement.rs +++ b/src/nodes/stmt/for_statement.rs @@ -25,7 +25,7 @@ impl<'a> Analyzer<'a> { let dep = if let Some(test) = &node.test { let test = self.exec_expression(test); - if test.test_truthy() == Some(false) { + if test.test_truthy(self) == Some(false) { return; } box_consumable((AstKind2::ForStatement(node), test)) diff --git a/src/nodes/stmt/if_statement.rs b/src/nodes/stmt/if_statement.rs index e860b127..fb252786 100644 --- a/src/nodes/stmt/if_statement.rs +++ b/src/nodes/stmt/if_statement.rs @@ -13,7 +13,7 @@ impl<'a> Analyzer<'a> { let test = self.exec_expression(&node.test).get_to_boolean(self); - let (maybe_consequent, maybe_alternate) = match test.test_truthy() { + let (maybe_consequent, maybe_alternate) = match test.test_truthy(self) { Some(true) => (true, false), Some(false) => (false, true), None => (true, true), diff --git a/src/nodes/stmt/while_statement.rs b/src/nodes/stmt/while_statement.rs index 7af8682c..f295b16b 100644 --- a/src/nodes/stmt/while_statement.rs +++ b/src/nodes/stmt/while_statement.rs @@ -16,7 +16,7 @@ impl<'a> Analyzer<'a> { // `a: while(() => { break a }) { }` is illegal. let test = self.exec_expression(&node.test); - if test.test_truthy() == Some(false) { + if test.test_truthy(self) == Some(false) { return; } diff --git a/src/utils/dep_id.rs b/src/utils/dep_id.rs index a0190235..09d02907 100644 --- a/src/utils/dep_id.rs +++ b/src/utils/dep_id.rs @@ -1,8 +1,13 @@ -use crate::{analyzer::Analyzer, ast::AstKind2, transformer::Transformer}; +use crate::{ + analyzer::Analyzer, + ast::AstKind2, + consumable::{Consumable, ConsumableTrait}, + transformer::Transformer, +}; use oxc::span::{GetSpan, Span}; use rustc_hash::FxHashMap; use std::{ - fmt::Debug, + fmt::{self, Debug}, hash::Hash, sync::atomic::{AtomicUsize, Ordering}, }; @@ -75,6 +80,10 @@ impl ReferredDeps { _ => self.by_ptr.contains_key(&dep), } } + + pub fn debug_count(&self) -> usize { + self.by_ptr.len() + self.by_index.iter().filter(|&&x| x > 0).count() + } } impl<'a> Analyzer<'a> { @@ -92,3 +101,25 @@ impl<'a> Transformer<'a> { self.referred_deps.is_referred(dep) } } + +impl<'a> fmt::Debug for ReferredDeps { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "ReferencedDeps".fmt(f) + } +} + +impl<'a> ConsumableTrait<'a> for ReferredDeps { + fn consume(&self, analyzer: &mut Analyzer<'a>) { + if self.by_index.len() > analyzer.referred_deps.by_index.len() { + analyzer.referred_deps.by_index.resize(self.by_index.len(), 0); + } + for (i, v) in self.by_index.iter().enumerate() { + analyzer.referred_deps.by_index[i] += v; + } + analyzer.referred_deps.by_ptr.extend(self.by_ptr.iter().map(|(k, v)| (k.clone(), *v))); + } + + fn cloned(&self) -> Consumable<'a> { + unreachable!("Should not clone ReferredDeps") + } +} diff --git a/src/utils/symbol_id.rs b/src/utils/symbol_id.rs index 13a1ff3d..9e56d56f 100644 --- a/src/utils/symbol_id.rs +++ b/src/utils/symbol_id.rs @@ -9,7 +9,7 @@ impl<'a> Analyzer<'a> { self.factory.string(self.allocator.alloc(format!("__#symbol__{}", symbol_id.index()))) } - pub fn parse_internal_symbol_id(&self, entity: Entity<'a>) -> Option { + pub fn parse_internal_symbol_id(&mut self, entity: Entity<'a>) -> Option { let literal = entity.get_literal(self)?; let LiteralEntity::String(string) = literal else { return None }; if string.starts_with("__#symbol__") { From 27cf785f1c3450c03fd874b836faa45a34898178 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Wed, 27 Nov 2024 14:18:25 +0800 Subject: [PATCH 03/10] wip --- src/entity/consumed_object.rs | 32 ++++---------- src/entity/mod.rs | 1 + src/entity/pure_result.rs | 27 +++++++----- src/nodes/expr/call_expression.rs | 61 ++++++++++++++++++++++---- src/nodes/expr/chain_expression.rs | 4 +- src/nodes/expr/mod.rs | 2 +- src/nodes/expr/new_expression.rs | 23 ++++++---- src/nodes/misc/identifier_reference.rs | 14 +++--- src/scope/cf_scope.rs | 4 -- src/scope/exhaustive.rs | 6 +-- src/scope/mod.rs | 9 +--- src/scope/pure.rs | 14 ++++++ src/utils/annotation.rs | 26 +++++------ 13 files changed, 132 insertions(+), 91 deletions(-) create mode 100644 src/scope/pure.rs diff --git a/src/entity/consumed_object.rs b/src/entity/consumed_object.rs index 48e59621..328b6126 100644 --- a/src/entity/consumed_object.rs +++ b/src/entity/consumed_object.rs @@ -19,10 +19,7 @@ pub fn get_property<'a>( key: Entity<'a>, ) -> Entity<'a> { let dep = (rc, dep, key); - if analyzer.is_inside_pure() { - rc.unknown_mutate(analyzer, dep.cloned()); - analyzer.factory.computed_unknown(dep) - } else if analyzer.config.unknown_property_read_side_effects { + if analyzer.config.unknown_property_read_side_effects { analyzer.may_throw(); analyzer.consume(dep); analyzer.refer_to_global(); @@ -80,16 +77,10 @@ pub fn call<'a>( args: Entity<'a>, ) -> Entity<'a> { let dep = (rc, dep, this, args); - if analyzer.is_inside_pure() { - this.unknown_mutate(analyzer, dep.cloned()); - args.unknown_mutate(analyzer, dep.cloned()); - analyzer.factory.computed_unknown(dep) - } else { - analyzer.consume(dep); - analyzer.may_throw(); - analyzer.refer_to_global(); - analyzer.factory.unknown() - } + analyzer.consume(dep); + analyzer.may_throw(); + analyzer.refer_to_global(); + analyzer.factory.unknown() } pub fn construct<'a>( @@ -99,15 +90,10 @@ pub fn construct<'a>( args: Entity<'a>, ) -> Entity<'a> { let dep = (rc, dep, args); - if analyzer.is_inside_pure() { - args.unknown_mutate(analyzer, dep.cloned()); - analyzer.factory.computed_unknown(dep) - } else { - analyzer.consume(dep); - analyzer.may_throw(); - analyzer.refer_to_global(); - analyzer.factory.unknown() - } + analyzer.consume(dep); + analyzer.may_throw(); + analyzer.refer_to_global(); + analyzer.factory.unknown() } pub fn jsx<'a>(rc: Entity<'a>, analyzer: &mut Analyzer<'a>, props: Entity<'a>) -> Entity<'a> { diff --git a/src/entity/mod.rs b/src/entity/mod.rs index 2c7da519..e63338cd 100644 --- a/src/entity/mod.rs +++ b/src/entity/mod.rs @@ -34,5 +34,6 @@ pub use literal::LiteralEntity; pub use object::{ObjectEntity, ObjectProperty, ObjectPropertyValue}; pub use operations::EntityOpHost; pub use primitive::PrimitiveEntity; +pub use pure_result::PureCallNode; pub use typeof_result::TypeofResult; pub use unknown::UnknownEntity; diff --git a/src/entity/pure_result.rs b/src/entity/pure_result.rs index 1fad325b..f4a70986 100644 --- a/src/entity/pure_result.rs +++ b/src/entity/pure_result.rs @@ -1,19 +1,18 @@ use super::{ entity::{EnumeratedProperties, IteratedElements}, - Entity, EntityTrait, TypeofResult, + Entity, EntityFactory, EntityTrait, TypeofResult, }; use crate::{ analyzer::Analyzer, consumable::{Consumable, ConsumableNode}, - dep::ReferredDeps, }; use oxc::ast::ast::{CallExpression, NewExpression}; -use std::{cell::OnceCell, mem}; +use std::cell::OnceCell; #[derive(Debug)] pub enum PureCallNode<'a> { - CallExpression(&'a CallExpression<'a>), - NewExpression(&'a NewExpression<'a>), + CallExpression(&'a CallExpression<'a>, Entity<'a>), + NewExpression(&'a NewExpression<'a>, Entity<'a>), } #[derive(Debug)] @@ -157,13 +156,21 @@ impl<'a> EntityTrait<'a> for PureResult<'a> { impl<'a> PureResult<'a> { fn value(&self, analyzer: &mut Analyzer<'a>) -> Entity<'a> { *self.result.get_or_init(|| { - let parent_referred_deps = mem::replace(&mut analyzer.referred_deps, ReferredDeps::default()); - let val = analyzer.exec_indeterminately(|analyzer| match &self.node { - PureCallNode::CallExpression(node) => analyzer.exec_call_expression(node), - PureCallNode::NewExpression(node) => analyzer.exec_new_expression(node), + let (val, this_referred_deps) = analyzer.exec_in_pure(|analyzer| match &self.node { + PureCallNode::CallExpression(node, arguments) => { + analyzer.exec_call_expression_in_chain(node, Some(*arguments)).1 + } + PureCallNode::NewExpression(node, arguments) => { + analyzer.exec_new_expression(node, Some(*arguments)) + } }); - let this_referred_deps = mem::replace(&mut analyzer.referred_deps, parent_referred_deps); analyzer.factory.computed(val, ConsumableNode::new(this_referred_deps)) }) } } + +impl<'a> EntityFactory<'a> { + pub fn pure_result(&self, node: PureCallNode<'a>) -> Entity<'a> { + self.entity(PureResult { node, result: OnceCell::new() }) + } +} diff --git a/src/nodes/expr/call_expression.rs b/src/nodes/expr/call_expression.rs index e29af7e8..a0e58587 100644 --- a/src/nodes/expr/call_expression.rs +++ b/src/nodes/expr/call_expression.rs @@ -1,5 +1,9 @@ use crate::{ - analyzer::Analyzer, ast::AstKind2, build_effect, consumable::box_consumable, entity::Entity, + analyzer::Analyzer, + ast::AstKind2, + build_effect, + consumable::box_consumable, + entity::{Entity, PureCallNode}, transformer::Transformer, }; use oxc::ast::{ @@ -14,19 +18,21 @@ pub struct Data { impl<'a> Analyzer<'a> { pub fn exec_call_expression(&mut self, node: &'a CallExpression) -> Entity<'a> { - self.exec_call_expression_in_chain(node).1 + self.exec_call_expression_in_chain(node, None).1 } /// Returns (short-circuit, value) pub fn exec_call_expression_in_chain( &mut self, node: &'a CallExpression, + args_from_pure: Option>, ) -> (Option, Entity<'a>) { - let pure = self.has_pure_notation(node.span); + if args_from_pure.is_none() && self.has_pure_notation(node.span) { + let args = self.exec_arguments(&node.arguments); + return self.factory.pure_result(PureCallNode::CallExpression(node, args)); + } - self.scope_context.pure += pure; let callee = self.exec_callee(&node.callee); - self.scope_context.pure -= pure; if let Some((callee_indeterminate, callee, this)) = callee { let self_indeterminate = if node.optional { @@ -48,11 +54,9 @@ impl<'a> Analyzer<'a> { self.push_indeterminate_cf_scope(); } - let args = self.exec_arguments(&node.arguments); + let args = args_from_pure.unwrap_or_else(|| self.exec_arguments(&node.arguments)); - self.scope_context.pure += pure; let ret_val = callee.call(self, box_consumable(AstKind2::CallExpression(node)), this, args); - self.scope_context.pure -= pure; if indeterminate { self.pop_cf_scope(); @@ -64,6 +68,47 @@ impl<'a> Analyzer<'a> { (Some(true), self.factory.undefined) } } + + pub fn exec_call_expression_by_pure( + &mut self, + node: &'a CallExpression, + arguments: Entity<'a>, + ) -> Entity<'a> { + let callee = self.exec_callee(&node.callee); + + if let Some((callee_indeterminate, callee, this)) = callee { + let self_indeterminate = if node.optional { + match callee.test_nullish(self) { + Some(true) => return self.factory.undefined, + Some(false) => false, + None => true, + } + } else { + false + }; + + let data = self.load_data::(AstKind2::CallExpression(node)); + data.need_optional |= self_indeterminate; + + let indeterminate = callee_indeterminate || self_indeterminate; + + if indeterminate { + self.push_indeterminate_cf_scope(); + } + + let ret_val = + callee.call(self, box_consumable(AstKind2::CallExpression(node)), this, arguments); + + if indeterminate { + self.pop_cf_scope(); + self.factory.union((ret_val, self.factory.undefined)) + } else { + ret_val + } + } else { + self.factory.undefined + } + } } impl<'a> Transformer<'a> { diff --git a/src/nodes/expr/chain_expression.rs b/src/nodes/expr/chain_expression.rs index d912aa4e..0d84af3c 100644 --- a/src/nodes/expr/chain_expression.rs +++ b/src/nodes/expr/chain_expression.rs @@ -7,7 +7,7 @@ use oxc::ast::{ impl<'a> Analyzer<'a> { pub fn exec_chain_expression(&mut self, node: &'a ChainExpression<'a>) -> Entity<'a> { match &node.expression { - ChainElement::CallExpression(node) => self.exec_call_expression_in_chain(node).1, + ChainElement::CallExpression(node) => self.exec_call_expression_in_chain(node, None).1, node => self.exec_member_expression_read_in_chain(node.to_member_expression(), false).1, } } @@ -22,7 +22,7 @@ impl<'a> Analyzer<'a> { self.exec_member_expression_read_in_chain(node.to_member_expression(), false); (short_circuit, value) } - Expression::CallExpression(node) => self.exec_call_expression_in_chain(node), + Expression::CallExpression(node) => self.exec_call_expression_in_chain(node, None), _ => (Some(false), self.exec_expression(node)), } } diff --git a/src/nodes/expr/mod.rs b/src/nodes/expr/mod.rs index 384abfd4..390c8ec8 100644 --- a/src/nodes/expr/mod.rs +++ b/src/nodes/expr/mod.rs @@ -75,7 +75,7 @@ impl<'a> Analyzer<'a> { Expression::ChainExpression(node) => self.exec_chain_expression(node), Expression::ImportExpression(node) => self.exec_import_expression(node), Expression::MetaProperty(node) => self.exec_meta_property(node), - Expression::NewExpression(node) => self.exec_new_expression(node), + Expression::NewExpression(node) => self.exec_new_expression(node,None), Expression::ClassExpression(node) => self.exec_class(node), Expression::ThisExpression(node) => self.exec_this_expression(node), Expression::Super(node) => self.exec_super(node), diff --git a/src/nodes/expr/new_expression.rs b/src/nodes/expr/new_expression.rs index 5cf0b8fe..369af3b4 100644 --- a/src/nodes/expr/new_expression.rs +++ b/src/nodes/expr/new_expression.rs @@ -1,22 +1,29 @@ use crate::{ - analyzer::Analyzer, ast::AstKind2, build_effect, consumable::box_consumable, entity::Entity, + analyzer::Analyzer, + ast::AstKind2, + build_effect, + consumable::box_consumable, + entity::{Entity, PureCallNode}, transformer::Transformer, }; use oxc::ast::ast::{Expression, NewExpression, TSTypeParameterInstantiation}; impl<'a> Analyzer<'a> { - pub fn exec_new_expression(&mut self, node: &'a NewExpression<'a>) -> Entity<'a> { - let pure = self.has_pure_notation(node.span); + pub fn exec_new_expression( + &mut self, + node: &'a NewExpression<'a>, + args_from_pure: Option>, + ) -> Entity<'a> { + if args_from_pure.is_none() && self.has_pure_notation(node.span) { + let arguments = self.exec_arguments(&node.arguments); + return self.factory.pure_result(PureCallNode::NewExpression(node, arguments)); + } - self.scope_context.pure += pure; let callee = self.exec_expression(&node.callee); - self.scope_context.pure -= pure; - let arguments = self.exec_arguments(&node.arguments); + let arguments = args_from_pure.unwrap_or_else(|| self.exec_arguments(&node.arguments)); - self.scope_context.pure += pure; let value = callee.construct(self, box_consumable(AstKind2::NewExpression(node)), arguments); - self.scope_context.pure -= pure; value } diff --git a/src/nodes/misc/identifier_reference.rs b/src/nodes/misc/identifier_reference.rs index c7b975c7..71dc0a2d 100644 --- a/src/nodes/misc/identifier_reference.rs +++ b/src/nodes/misc/identifier_reference.rs @@ -36,16 +36,12 @@ impl<'a> Analyzer<'a> { *global } else { // Unknown global - if self.is_inside_pure() { - self.factory.computed_unknown(AstKind2::IdentifierReference(node)) - } else { - if self.config.unknown_global_side_effects { - self.set_data(AstKind2::IdentifierReference(node), Data { has_effect: true }); - self.refer_to_global(); - self.may_throw(); - } - self.factory.unknown() + if self.config.unknown_global_side_effects { + self.set_data(AstKind2::IdentifierReference(node), Data { has_effect: true }); + self.refer_to_global(); + self.may_throw(); } + self.factory.unknown() } } diff --git a/src/scope/cf_scope.rs b/src/scope/cf_scope.rs index 0ef0b527..c5270757 100644 --- a/src/scope/cf_scope.rs +++ b/src/scope/cf_scope.rs @@ -309,10 +309,6 @@ impl<'a> Analyzer<'a> { } pub fn refer_to_global(&mut self) { - if self.is_inside_pure() { - return; - } - for depth in (0..self.scope_context.cf.stack.len()).rev() { let scope = self.scope_context.cf.get_mut_from_depth(depth); match scope.referred_state { diff --git a/src/scope/exhaustive.rs b/src/scope/exhaustive.rs index 78f5d339..439202c7 100644 --- a/src/scope/exhaustive.rs +++ b/src/scope/exhaustive.rs @@ -49,10 +49,8 @@ impl<'a> Analyzer<'a> { analyzer.push_try_scope(); let ret_val = runner(analyzer); let thrown_val = analyzer.pop_try_scope().thrown_val(analyzer); - if !analyzer.is_inside_pure() { - analyzer.consume(ret_val); - analyzer.consume(thrown_val); - } + analyzer.consume(ret_val); + analyzer.consume(thrown_val); analyzer.pop_cf_scope(); }); let deps = self.exec_exhaustively(kind, runner.clone(), false); diff --git a/src/scope/mod.rs b/src/scope/mod.rs index 13d2d7f6..5518b2da 100644 --- a/src/scope/mod.rs +++ b/src/scope/mod.rs @@ -3,6 +3,7 @@ pub mod cf_scope; pub mod conditional; pub mod exhaustive; pub mod r#loop; +mod pure; mod scope_tree; pub mod try_scope; mod utils; @@ -31,7 +32,6 @@ pub struct ScopeContext<'a> { pub call: Vec>, pub variable: ScopeTree>, pub cf: ScopeTree>, - pub pure: usize, pub object_scope_id: ScopeId, pub object_symbol_counter: usize, @@ -65,7 +65,6 @@ impl<'a> ScopeContext<'a> { )], variable, cf, - pure: 0, object_scope_id, object_symbol_counter: 128, @@ -76,7 +75,6 @@ impl<'a> ScopeContext<'a> { debug_assert_eq!(self.call.len(), 1); debug_assert_eq!(self.variable.current_depth(), 0); debug_assert_eq!(self.cf.current_depth(), 0); - debug_assert_eq!(self.pure, 0); for scope in self.cf.iter_all() { if let Some(data) = &scope.exhaustive_data { @@ -132,11 +130,6 @@ impl<'a> Analyzer<'a> { self.scope_context.variable.get_current_mut() } - pub fn is_inside_pure(&self) -> bool { - // TODO: self.scope_context.pure > 0 - false - } - fn replace_variable_scope_stack(&mut self, new_stack: Vec) -> Vec { if let Some(logger) = self.logger { logger.push_event(DebuggerEvent::ReplaceVarScopeStack(new_stack.clone())); diff --git a/src/scope/pure.rs b/src/scope/pure.rs new file mode 100644 index 00000000..9cffd0e8 --- /dev/null +++ b/src/scope/pure.rs @@ -0,0 +1,14 @@ +use crate::{analyzer::Analyzer, dep::ReferredDeps, entity::Entity}; +use std::mem; + +impl<'a> Analyzer<'a> { + pub fn exec_in_pure( + &mut self, + runner: impl FnOnce(&mut Analyzer<'a>) -> Entity<'a>, + ) -> (Entity<'a>, ReferredDeps) { + let parent_referred_deps = mem::replace(&mut self.referred_deps, ReferredDeps::default()); + let val = runner(self); + let this_referred_deps = mem::replace(&mut self.referred_deps, parent_referred_deps); + (val, this_referred_deps) + } +} diff --git a/src/utils/annotation.rs b/src/utils/annotation.rs index 68ae501a..a49da7c2 100644 --- a/src/utils/annotation.rs +++ b/src/utils/annotation.rs @@ -12,14 +12,16 @@ impl<'a> Analyzer<'a> { // we treat the `comment` not belongs to the `span`. let range_text = Span::new(comment.span.end, span.start).source_text(self.semantic.source_text()); - let only_whitespace = match comment.kind { - CommentKind::Line => range_text.trim().is_empty(), - CommentKind::Block => { - range_text - .strip_prefix("*/") // for multi-line comment - .is_some_and(|s| s.trim().is_empty()) - } - }; + + // let only_whitespace = match comment.kind { + // CommentKind::Line => range_text.trim().is_empty(), + // CommentKind::Block => { + // range_text + // .strip_prefix("*/") // for multi-line comment + // .is_some_and(|s| s.trim().is_empty()) + // } + // }; + let only_whitespace = range_text.trim().is_empty(); if !only_whitespace { return false; } @@ -27,12 +29,8 @@ impl<'a> Analyzer<'a> { test(raw) } - pub fn has_pure_notation(&self, span: Span) -> usize { - if self.has_annotation(span, |raw| raw.contains("@__PURE__") || raw.contains("#__PURE__")) { - 1 - } else { - 0 - } + pub fn has_pure_notation(&self, span: Span) -> bool { + self.has_annotation(span, |raw| raw.contains("@__PURE__") || raw.contains("#__PURE__")) } pub fn has_finite_recursion_notation(&self, span: Span) -> bool { From e4f10722e0dfc8f8d0f953d977fbe535eb008517 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Thu, 28 Nov 2024 09:03:23 +0800 Subject: [PATCH 04/10] wip --- src/entity/consumed_object.rs | 2 +- src/entity/pure_result.rs | 40 ++++++++++----- src/nodes/expr/call_expression.rs | 80 ++++++++++++----------------- src/nodes/expr/chain_expression.rs | 4 +- src/nodes/expr/member_expression.rs | 5 +- src/nodes/expr/new_expression.rs | 7 ++- src/scope/pure.rs | 11 ++-- src/utils/annotation.rs | 10 ++-- 8 files changed, 83 insertions(+), 76 deletions(-) diff --git a/src/entity/consumed_object.rs b/src/entity/consumed_object.rs index 328b6126..4d385c30 100644 --- a/src/entity/consumed_object.rs +++ b/src/entity/consumed_object.rs @@ -4,7 +4,7 @@ use super::{ }; use crate::{ analyzer::Analyzer, - consumable::{box_consumable, Consumable, ConsumableTrait}, + consumable::{box_consumable, Consumable}, }; pub fn unknown_mutate<'a>(analyzer: &mut Analyzer<'a>, dep: Consumable<'a>) { diff --git a/src/entity/pure_result.rs b/src/entity/pure_result.rs index f4a70986..285dd263 100644 --- a/src/entity/pure_result.rs +++ b/src/entity/pure_result.rs @@ -5,13 +5,14 @@ use super::{ use crate::{ analyzer::Analyzer, consumable::{Consumable, ConsumableNode}, + dep::ReferredDeps, }; use oxc::ast::ast::{CallExpression, NewExpression}; -use std::cell::OnceCell; +use std::cell::{OnceCell, RefCell}; #[derive(Debug)] pub enum PureCallNode<'a> { - CallExpression(&'a CallExpression<'a>, Entity<'a>), + CallExpression(&'a CallExpression<'a>, (Entity<'a>, Entity<'a>, Entity<'a>)), NewExpression(&'a NewExpression<'a>, Entity<'a>), } @@ -19,6 +20,7 @@ pub enum PureCallNode<'a> { pub struct PureResult<'a> { pub node: PureCallNode<'a>, pub result: OnceCell>, + pub referred_deps: RefCell, } impl<'a> EntityTrait<'a> for PureResult<'a> { @@ -156,21 +158,35 @@ impl<'a> EntityTrait<'a> for PureResult<'a> { impl<'a> PureResult<'a> { fn value(&self, analyzer: &mut Analyzer<'a>) -> Entity<'a> { *self.result.get_or_init(|| { - let (val, this_referred_deps) = analyzer.exec_in_pure(|analyzer| match &self.node { - PureCallNode::CallExpression(node, arguments) => { - analyzer.exec_call_expression_in_chain(node, Some(*arguments)).1 - } - PureCallNode::NewExpression(node, arguments) => { - analyzer.exec_new_expression(node, Some(*arguments)) - } - }); + let (val, this_referred_deps) = analyzer.exec_in_pure( + |analyzer| match &self.node { + PureCallNode::CallExpression(node, arguments) => { + let result = analyzer.exec_call_expression_in_chain(node, Some(*arguments)); + match result { + Ok((scope_count, value, undefined)) => { + analyzer.pop_multiple_cf_scopes(scope_count); + analyzer.factory.optional_union(value, undefined) + } + Err(value) => value, + } + } + PureCallNode::NewExpression(node, arguments) => { + analyzer.exec_new_expression(node, Some(*arguments)) + } + }, + self.referred_deps.take(), + ); analyzer.factory.computed(val, ConsumableNode::new(this_referred_deps)) }) } } impl<'a> EntityFactory<'a> { - pub fn pure_result(&self, node: PureCallNode<'a>) -> Entity<'a> { - self.entity(PureResult { node, result: OnceCell::new() }) + pub fn pure_result(&self, node: PureCallNode<'a>, referred_deps: ReferredDeps) -> Entity<'a> { + self.entity(PureResult { + node, + result: OnceCell::new(), + referred_deps: RefCell::new(referred_deps), + }) } } diff --git a/src/nodes/expr/call_expression.rs b/src/nodes/expr/call_expression.rs index 3150262a..73e12b7e 100644 --- a/src/nodes/expr/call_expression.rs +++ b/src/nodes/expr/call_expression.rs @@ -3,6 +3,7 @@ use crate::{ ast::AstKind2, build_effect, consumable::box_consumable, + dep::ReferredDeps, entity::{Entity, PureCallNode}, transformer::Transformer, }; @@ -16,7 +17,7 @@ use oxc::{ impl<'a> Analyzer<'a> { pub fn exec_call_expression(&mut self, node: &'a CallExpression) -> Entity<'a> { - let (scope_count, value, undefined) = self.exec_call_expression_in_chain(node).unwrap(); + let (scope_count, value, undefined) = self.exec_call_expression_in_chain(node, None).unwrap(); assert_eq!(scope_count, 0); assert!(undefined.is_none()); @@ -24,17 +25,30 @@ impl<'a> Analyzer<'a> { value } - /// Returns (short-circuit, value) + /// Returns (short-circuit, value, undefined) pub fn exec_call_expression_in_chain( &mut self, node: &'a CallExpression, + cache_from_pure: Option<(Entity<'a>, Entity<'a>, Entity<'a>)>, ) -> Result<(usize, Entity<'a>, Option>), Entity<'a>> { - let (mut scope_count, callee, mut undefined, this) = self.exec_callee(&node.callee)?; + let pure = cache_from_pure.is_none() && self.has_pure_notation(node); + let mut referred_deps = None; + + let (mut scope_count, callee, mut undefined, this) = if pure { + let (result, this_referred_deps) = + self.exec_in_pure(|analyzer| analyzer.exec_callee(&node.callee), ReferredDeps::default()); + referred_deps = Some(this_referred_deps); + result? + } else if let Some((callee, this, _)) = cache_from_pure { + (0, callee, None, this) + } else { + self.exec_callee(&node.callee)? + }; let dep_id = AstKind2::CallExpression(node); if node.optional { - let maybe_left = match callee.test_nullish() { + let maybe_left = match callee.test_nullish(self) { Some(true) => { self.pop_multiple_cf_scopes(scope_count); return Err(self.forward_logical_left_val(dep_id, self.factory.undefined, true, false)); @@ -55,53 +69,23 @@ impl<'a> Analyzer<'a> { scope_count += 1; } - let args = self.exec_arguments(&node.arguments); - - let ret_val = callee.call(self, box_consumable(dep_id), this, args); + let args = if let Some((_, _, args)) = cache_from_pure { + args + } else { + self.exec_arguments(&node.arguments) + }; + + let ret_val = if pure { + self.factory.pure_result( + PureCallNode::CallExpression(node, (callee, this, args)), + referred_deps.unwrap(), + ) + } else { + callee.call(self, box_consumable(dep_id), this, args) + }; Ok((scope_count, ret_val, undefined)) } - - pub fn exec_call_expression_by_pure( - &mut self, - node: &'a CallExpression, - arguments: Entity<'a>, - ) -> Entity<'a> { - let callee = self.exec_callee(&node.callee); - - if let Some((callee_indeterminate, callee, this)) = callee { - let self_indeterminate = if node.optional { - match callee.test_nullish(self) { - Some(true) => return self.factory.undefined, - Some(false) => false, - None => true, - } - } else { - false - }; - - let data = self.load_data::(AstKind2::CallExpression(node)); - data.need_optional |= self_indeterminate; - - let indeterminate = callee_indeterminate || self_indeterminate; - - if indeterminate { - self.push_indeterminate_cf_scope(); - } - - let ret_val = - callee.call(self, box_consumable(AstKind2::CallExpression(node)), this, arguments); - - if indeterminate { - self.pop_cf_scope(); - self.factory.union((ret_val, self.factory.undefined)) - } else { - ret_val - } - } else { - self.factory.undefined - } - } } impl<'a> Transformer<'a> { diff --git a/src/nodes/expr/chain_expression.rs b/src/nodes/expr/chain_expression.rs index c71d4703..e74a5f13 100644 --- a/src/nodes/expr/chain_expression.rs +++ b/src/nodes/expr/chain_expression.rs @@ -8,7 +8,7 @@ impl<'a> Analyzer<'a> { pub fn exec_chain_expression(&mut self, node: &'a ChainExpression<'a>) -> Entity<'a> { match &node.expression { ChainElement::CallExpression(node) => { - let result = self.exec_call_expression_in_chain(node); + let result = self.exec_call_expression_in_chain(node, None); match result { Ok((scope_count, value, undefined)) => { self.pop_multiple_cf_scopes(scope_count); @@ -38,7 +38,7 @@ impl<'a> Analyzer<'a> { match_member_expression!(Expression) => self .exec_member_expression_read_in_chain(node.to_member_expression(), false) .map(|(scope_count, value, undefined, _)| (scope_count, value, undefined)), - Expression::CallExpression(node) => self.exec_call_expression_in_chain(node), + Expression::CallExpression(node) => self.exec_call_expression_in_chain(node, None), _ => Ok((0, self.exec_expression(node), None)), } } diff --git a/src/nodes/expr/member_expression.rs b/src/nodes/expr/member_expression.rs index 746d1910..a5c3bd56 100644 --- a/src/nodes/expr/member_expression.rs +++ b/src/nodes/expr/member_expression.rs @@ -32,12 +32,15 @@ impl<'a> Analyzer<'a> { node: &'a MemberExpression<'a>, will_write: bool, ) -> Result<(usize, Entity<'a>, Option>, (Entity<'a>, Entity<'a>)), Entity<'a>> { + + + let (mut scope_count, object, mut undefined) = self.exec_expression_in_chain(node.object())?; let dep_id = AstKind2::MemberExpression(node); if node.optional() { - let maybe_left = match object.test_nullish() { + let maybe_left = match object.test_nullish(self) { Some(true) => { self.pop_multiple_cf_scopes(scope_count); return Err(self.forward_logical_left_val(dep_id, self.factory.undefined, true, false)); diff --git a/src/nodes/expr/new_expression.rs b/src/nodes/expr/new_expression.rs index 369af3b4..14706736 100644 --- a/src/nodes/expr/new_expression.rs +++ b/src/nodes/expr/new_expression.rs @@ -3,6 +3,7 @@ use crate::{ ast::AstKind2, build_effect, consumable::box_consumable, + dep::ReferredDeps, entity::{Entity, PureCallNode}, transformer::Transformer, }; @@ -14,9 +15,11 @@ impl<'a> Analyzer<'a> { node: &'a NewExpression<'a>, args_from_pure: Option>, ) -> Entity<'a> { - if args_from_pure.is_none() && self.has_pure_notation(node.span) { + if args_from_pure.is_none() && self.has_pure_notation(node) { let arguments = self.exec_arguments(&node.arguments); - return self.factory.pure_result(PureCallNode::NewExpression(node, arguments)); + return self + .factory + .pure_result(PureCallNode::NewExpression(node, arguments), ReferredDeps::default()); } let callee = self.exec_expression(&node.callee); diff --git a/src/scope/pure.rs b/src/scope/pure.rs index 9cffd0e8..2882f859 100644 --- a/src/scope/pure.rs +++ b/src/scope/pure.rs @@ -1,12 +1,13 @@ -use crate::{analyzer::Analyzer, dep::ReferredDeps, entity::Entity}; +use crate::{analyzer::Analyzer, dep::ReferredDeps}; use std::mem; impl<'a> Analyzer<'a> { - pub fn exec_in_pure( + pub fn exec_in_pure( &mut self, - runner: impl FnOnce(&mut Analyzer<'a>) -> Entity<'a>, - ) -> (Entity<'a>, ReferredDeps) { - let parent_referred_deps = mem::replace(&mut self.referred_deps, ReferredDeps::default()); + runner: impl FnOnce(&mut Analyzer<'a>) -> T, + referred_deps: ReferredDeps, + ) -> (T, ReferredDeps) { + let parent_referred_deps = mem::replace(&mut self.referred_deps, referred_deps); let val = runner(self); let this_referred_deps = mem::replace(&mut self.referred_deps, parent_referred_deps); (val, this_referred_deps) diff --git a/src/utils/annotation.rs b/src/utils/annotation.rs index a49da7c2..d0e569b3 100644 --- a/src/utils/annotation.rs +++ b/src/utils/annotation.rs @@ -1,5 +1,5 @@ use crate::analyzer::Analyzer; -use oxc::{ast::CommentKind, span::Span}; +use oxc::span::{GetSpan, Span}; impl<'a> Analyzer<'a> { fn has_annotation(&self, span: Span, test: fn(&str) -> bool) -> bool { @@ -29,12 +29,12 @@ impl<'a> Analyzer<'a> { test(raw) } - pub fn has_pure_notation(&self, span: Span) -> bool { - self.has_annotation(span, |raw| raw.contains("@__PURE__") || raw.contains("#__PURE__")) + pub fn has_pure_notation(&self, node: &impl GetSpan) -> bool { + self.has_annotation(node.span(), |raw| raw.contains("@__PURE__") || raw.contains("#__PURE__")) } - pub fn has_finite_recursion_notation(&self, span: Span) -> bool { - self.has_annotation(span, |raw| { + pub fn has_finite_recursion_notation(&self, node: impl GetSpan) -> bool { + self.has_annotation(node.span(), |raw| { raw.contains("@__FINITE_RECURSION__") || raw.contains("#__FINITE_RECURSION__") }) } From 9923dfec2214c6ad1a8a0b0612e2413df910a5da Mon Sep 17 00:00:00 2001 From: _Kerman Date: Thu, 28 Nov 2024 09:09:44 +0800 Subject: [PATCH 05/10] perf --- src/analyzer.rs | 4 ++-- src/entity/pure_result.rs | 8 ++++---- src/nodes/expr/call_expression.rs | 6 ++++-- src/nodes/expr/new_expression.rs | 7 ++++--- src/scope/pure.rs | 4 ++-- src/transformer.rs | 15 +++------------ src/utils/dep_id.rs | 2 +- 7 files changed, 20 insertions(+), 26 deletions(-) diff --git a/src/analyzer.rs b/src/analyzer.rs index 9ab7921b..64a486b1 100644 --- a/src/analyzer.rs +++ b/src/analyzer.rs @@ -30,7 +30,7 @@ pub struct Analyzer<'a> { pub semantic: Semantic<'a>, pub span_stack: Vec, pub data: ExtraData<'a>, - pub referred_deps: ReferredDeps, + pub referred_deps: &'a mut ReferredDeps, pub conditional_data: ConditionalDataMap<'a>, pub loop_data: LoopDataMap<'a>, pub named_exports: Vec, @@ -59,7 +59,7 @@ impl<'a> Analyzer<'a> { semantic, span_stack: vec![], data: Default::default(), - referred_deps: Default::default(), + referred_deps: allocator.alloc(ReferredDeps::default()), conditional_data: Default::default(), loop_data: Default::default(), named_exports: Vec::new(), diff --git a/src/entity/pure_result.rs b/src/entity/pure_result.rs index 285dd263..e489b7f5 100644 --- a/src/entity/pure_result.rs +++ b/src/entity/pure_result.rs @@ -20,7 +20,7 @@ pub enum PureCallNode<'a> { pub struct PureResult<'a> { pub node: PureCallNode<'a>, pub result: OnceCell>, - pub referred_deps: RefCell, + pub referred_deps: RefCell>, } impl<'a> EntityTrait<'a> for PureResult<'a> { @@ -174,7 +174,7 @@ impl<'a> PureResult<'a> { analyzer.exec_new_expression(node, Some(*arguments)) } }, - self.referred_deps.take(), + self.referred_deps.take().unwrap(), ); analyzer.factory.computed(val, ConsumableNode::new(this_referred_deps)) }) @@ -182,11 +182,11 @@ impl<'a> PureResult<'a> { } impl<'a> EntityFactory<'a> { - pub fn pure_result(&self, node: PureCallNode<'a>, referred_deps: ReferredDeps) -> Entity<'a> { + pub fn pure_result(&self, node: PureCallNode<'a>, referred_deps: &'a mut ReferredDeps) -> Entity<'a> { self.entity(PureResult { node, result: OnceCell::new(), - referred_deps: RefCell::new(referred_deps), + referred_deps: RefCell::new(Some(referred_deps)), }) } } diff --git a/src/nodes/expr/call_expression.rs b/src/nodes/expr/call_expression.rs index 73e12b7e..1602cdb1 100644 --- a/src/nodes/expr/call_expression.rs +++ b/src/nodes/expr/call_expression.rs @@ -35,8 +35,10 @@ impl<'a> Analyzer<'a> { let mut referred_deps = None; let (mut scope_count, callee, mut undefined, this) = if pure { - let (result, this_referred_deps) = - self.exec_in_pure(|analyzer| analyzer.exec_callee(&node.callee), ReferredDeps::default()); + let (result, this_referred_deps) = self.exec_in_pure( + |analyzer| analyzer.exec_callee(&node.callee), + self.allocator.alloc(ReferredDeps::default()), + ); referred_deps = Some(this_referred_deps); result? } else if let Some((callee, this, _)) = cache_from_pure { diff --git a/src/nodes/expr/new_expression.rs b/src/nodes/expr/new_expression.rs index 14706736..0adbfd52 100644 --- a/src/nodes/expr/new_expression.rs +++ b/src/nodes/expr/new_expression.rs @@ -17,9 +17,10 @@ impl<'a> Analyzer<'a> { ) -> Entity<'a> { if args_from_pure.is_none() && self.has_pure_notation(node) { let arguments = self.exec_arguments(&node.arguments); - return self - .factory - .pure_result(PureCallNode::NewExpression(node, arguments), ReferredDeps::default()); + return self.factory.pure_result( + PureCallNode::NewExpression(node, arguments), + self.allocator.alloc(ReferredDeps::default()), + ); } let callee = self.exec_expression(&node.callee); diff --git a/src/scope/pure.rs b/src/scope/pure.rs index 2882f859..c8e50704 100644 --- a/src/scope/pure.rs +++ b/src/scope/pure.rs @@ -5,8 +5,8 @@ impl<'a> Analyzer<'a> { pub fn exec_in_pure( &mut self, runner: impl FnOnce(&mut Analyzer<'a>) -> T, - referred_deps: ReferredDeps, - ) -> (T, ReferredDeps) { + referred_deps: &'a mut ReferredDeps, + ) -> (T, &'a mut ReferredDeps) { let parent_referred_deps = mem::replace(&mut self.referred_deps, referred_deps); let val = runner(self); let this_referred_deps = mem::replace(&mut self.referred_deps, parent_referred_deps); diff --git a/src/transformer.rs b/src/transformer.rs index 3afd61ba..f5b25ce1 100644 --- a/src/transformer.rs +++ b/src/transformer.rs @@ -33,7 +33,7 @@ pub struct Transformer<'a> { pub semantic: Semantic<'a>, pub ast_builder: AstBuilder<'a>, pub data: ExtraData<'a>, - pub referred_deps: ReferredDeps, + pub referred_deps: &'a ReferredDeps, pub conditional_data: ConditionalDataMap<'a>, pub var_decls: RefCell>, @@ -51,27 +51,18 @@ impl<'a> Transformer<'a> { allocator, semantic, data, - referred_deps: referred_nodes, + referred_deps, conditional_data, .. } = analyzer; - // let mut counts: Vec<_> = referred_nodes.clone().into_iter().collect(); - // counts.sort_by(|a, b| b.1.cmp(&a.1)); - // for (key, v) in counts { - // if v > 10 { - // println!("{key:?}: {v}"); - // } - // } - // println!("---"); - Transformer { config, allocator, semantic, ast_builder: AstBuilder::new(allocator), data, - referred_deps: referred_nodes, + referred_deps, conditional_data, var_decls: Default::default(), diff --git a/src/utils/dep_id.rs b/src/utils/dep_id.rs index 09d02907..a83eba3b 100644 --- a/src/utils/dep_id.rs +++ b/src/utils/dep_id.rs @@ -108,7 +108,7 @@ impl<'a> fmt::Debug for ReferredDeps { } } -impl<'a> ConsumableTrait<'a> for ReferredDeps { +impl<'a> ConsumableTrait<'a> for &'a mut ReferredDeps { fn consume(&self, analyzer: &mut Analyzer<'a>) { if self.by_index.len() > analyzer.referred_deps.by_index.len() { analyzer.referred_deps.by_index.resize(self.by_index.len(), 0); From 4054feaa2e1e6a0e91736f6e3a4f7e41706ea574 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Thu, 28 Nov 2024 09:55:41 +0800 Subject: [PATCH 06/10] fix --- src/nodes/expr/call_expression.rs | 2 + src/nodes/expr/member_expression.rs | 3 -- src/nodes/expr/mod.rs | 4 +- src/nodes/jsx/jsx_element_name.rs | 4 +- src/nodes/jsx/jsx_member_expression_object.rs | 4 +- src/nodes/misc/assignment_target_property.rs | 2 +- src/nodes/misc/identifier_reference.rs | 46 ++++--------------- src/nodes/misc/simple_assignment_target.rs | 4 +- 8 files changed, 20 insertions(+), 49 deletions(-) diff --git a/src/nodes/expr/call_expression.rs b/src/nodes/expr/call_expression.rs index 1602cdb1..24dc8e7e 100644 --- a/src/nodes/expr/call_expression.rs +++ b/src/nodes/expr/call_expression.rs @@ -32,6 +32,8 @@ impl<'a> Analyzer<'a> { cache_from_pure: Option<(Entity<'a>, Entity<'a>, Entity<'a>)>, ) -> Result<(usize, Entity<'a>, Option>), Entity<'a>> { let pure = cache_from_pure.is_none() && self.has_pure_notation(node); + println!("pure: {}", pure); + let mut referred_deps = None; let (mut scope_count, callee, mut undefined, this) = if pure { diff --git a/src/nodes/expr/member_expression.rs b/src/nodes/expr/member_expression.rs index a5c3bd56..15327d6a 100644 --- a/src/nodes/expr/member_expression.rs +++ b/src/nodes/expr/member_expression.rs @@ -32,9 +32,6 @@ impl<'a> Analyzer<'a> { node: &'a MemberExpression<'a>, will_write: bool, ) -> Result<(usize, Entity<'a>, Option>, (Entity<'a>, Entity<'a>)), Entity<'a>> { - - - let (mut scope_count, object, mut undefined) = self.exec_expression_in_chain(node.object())?; let dep_id = AstKind2::MemberExpression(node); diff --git a/src/nodes/expr/mod.rs b/src/nodes/expr/mod.rs index 8c562548..6cca4bad 100644 --- a/src/nodes/expr/mod.rs +++ b/src/nodes/expr/mod.rs @@ -75,7 +75,7 @@ impl<'a> Analyzer<'a> { Expression::ChainExpression(node) => self.exec_chain_expression(node), Expression::ImportExpression(node) => self.exec_import_expression(node), Expression::MetaProperty(node) => self.exec_meta_property(node), - Expression::NewExpression(node) => self.exec_new_expression(node,None), + Expression::NewExpression(node) => self.exec_new_expression(node, None), Expression::ClassExpression(node) => self.exec_class(node), Expression::ThisExpression(node) => self.exec_this_expression(node), Expression::Super(node) => self.exec_super(node), @@ -120,7 +120,7 @@ impl<'a> Transformer<'a> { | Expression::RegExpLiteral(_) => need_val.then(|| self.clone_node(node)), Expression::TemplateLiteral(node) => self.transform_template_literal(node, need_val), Expression::Identifier(node) => { - self.transform_identifier_reference_read(node, need_val).map(Expression::Identifier) + self.transform_identifier_reference(node, need_val).map(Expression::Identifier) } Expression::FunctionExpression(node) => { self.transform_function(node, need_val).map(Expression::FunctionExpression) diff --git a/src/nodes/jsx/jsx_element_name.rs b/src/nodes/jsx/jsx_element_name.rs index 10abde5d..ef1aa287 100644 --- a/src/nodes/jsx/jsx_element_name.rs +++ b/src/nodes/jsx/jsx_element_name.rs @@ -21,7 +21,7 @@ impl<'a> Transformer<'a> { match node { JSXElementName::Identifier(_node) => None, JSXElementName::IdentifierReference(node) => { - self.transform_identifier_reference_read(node, false).map(Expression::Identifier) + self.transform_identifier_reference(node, false).map(Expression::Identifier) } JSXElementName::NamespacedName(_node) => None, JSXElementName::MemberExpression(node) => { @@ -38,7 +38,7 @@ impl<'a> Transformer<'a> { match node { JSXElementName::Identifier(node) => JSXElementName::Identifier(self.clone_node(node)), JSXElementName::IdentifierReference(node) => JSXElementName::IdentifierReference( - self.transform_identifier_reference_read(node, true).unwrap(), + self.transform_identifier_reference(node, true).unwrap(), ), JSXElementName::MemberExpression(node) => { JSXElementName::MemberExpression(self.transform_jsx_member_expression_need_val(node)) diff --git a/src/nodes/jsx/jsx_member_expression_object.rs b/src/nodes/jsx/jsx_member_expression_object.rs index 71c294d4..f83c1281 100644 --- a/src/nodes/jsx/jsx_member_expression_object.rs +++ b/src/nodes/jsx/jsx_member_expression_object.rs @@ -24,7 +24,7 @@ impl<'a> Transformer<'a> { ) -> Option> { match node { JSXMemberExpressionObject::IdentifierReference(node) => { - self.transform_identifier_reference_read(node, need_val).map(Expression::Identifier) + self.transform_identifier_reference(node, need_val).map(Expression::Identifier) } JSXMemberExpressionObject::MemberExpression(node) => { self.transform_jsx_member_expression_effect_only(node, need_val) @@ -42,7 +42,7 @@ impl<'a> Transformer<'a> { match node { JSXMemberExpressionObject::IdentifierReference(node) => { JSXMemberExpressionObject::IdentifierReference( - self.transform_identifier_reference_read(node, true).unwrap(), + self.transform_identifier_reference(node, true).unwrap(), ) } JSXMemberExpressionObject::MemberExpression(node) => { diff --git a/src/nodes/misc/assignment_target_property.rs b/src/nodes/misc/assignment_target_property.rs index 97a72e3e..1e98005d 100644 --- a/src/nodes/misc/assignment_target_property.rs +++ b/src/nodes/misc/assignment_target_property.rs @@ -71,7 +71,7 @@ impl<'a> Transformer<'a> { let binding_span = binding.span(); let binding_name = binding.name.as_str(); - let binding = self.transform_identifier_reference_write(binding); + let binding = self.transform_identifier_reference(binding, false); let init = data .need_init .then(|| { diff --git a/src/nodes/misc/identifier_reference.rs b/src/nodes/misc/identifier_reference.rs index 71dc0a2d..2e199895 100644 --- a/src/nodes/misc/identifier_reference.rs +++ b/src/nodes/misc/identifier_reference.rs @@ -1,19 +1,12 @@ -use crate::{ - analyzer::Analyzer, ast::AstKind2, consumable::box_consumable, entity::Entity, - transformer::Transformer, -}; +use crate::{analyzer::Analyzer, ast::AstKind2, entity::Entity, transformer::Transformer}; use oxc::{allocator, ast::ast::IdentifierReference}; -#[derive(Debug, Default, Clone)] -pub struct Data { - has_effect: bool, -} - impl<'a> Analyzer<'a> { pub fn exec_identifier_reference_read( &mut self, node: &'a IdentifierReference<'a>, ) -> Entity<'a> { + let dep_id = AstKind2::IdentifierReference(node); let reference = self.semantic.symbols().get_reference(node.reference_id()); let symbol = reference.symbol_id(); @@ -23,7 +16,7 @@ impl<'a> Analyzer<'a> { value } else { // TDZ - self.set_data(AstKind2::IdentifierReference(node), Data { has_effect: true }); + self.refer_dep(dep_id); self.factory.unknown() } } else if node.name == "arguments" { @@ -37,7 +30,7 @@ impl<'a> Analyzer<'a> { } else { // Unknown global if self.config.unknown_global_side_effects { - self.set_data(AstKind2::IdentifierReference(node), Data { has_effect: true }); + self.refer_dep(dep_id); self.refer_to_global(); self.may_throw(); } @@ -50,8 +43,8 @@ impl<'a> Analyzer<'a> { node: &'a IdentifierReference<'a>, value: Entity<'a>, ) { - let dep = box_consumable(AstKind2::IdentifierReference(node)); - let value = self.factory.computed(value, dep); + let dep_id = AstKind2::IdentifierReference(node); + let value = self.factory.computed(value, dep_id); let reference = self.semantic.symbols().get_reference(node.reference_id()); debug_assert!(reference.is_write()); @@ -64,8 +57,8 @@ impl<'a> Analyzer<'a> { "Should not write to builtin object, it may cause unexpected tree-shaking behavior", ); } else { - self.set_data(AstKind2::IdentifierReference(node), Data { has_effect: true }); value.consume(self); + self.refer_dep(dep_id); self.may_throw(); self.refer_to_global(); } @@ -73,33 +66,12 @@ impl<'a> Analyzer<'a> { } impl<'a> Transformer<'a> { - pub fn transform_identifier_reference_read( + pub fn transform_identifier_reference( &self, node: &'a IdentifierReference<'a>, need_val: bool, ) -> Option>> { - let data = self.get_data::(AstKind2::IdentifierReference(node)); - - self.transform_identifier_reference(node, data.has_effect || need_val) - } - - pub fn transform_identifier_reference_write( - &self, - node: &'a IdentifierReference<'a>, - ) -> Option>> { - let data = self.get_data::(AstKind2::IdentifierReference(node)); - - let referred = self.is_referred(AstKind2::IdentifierReference(node)); - - self.transform_identifier_reference(node, data.has_effect || referred) - } - - fn transform_identifier_reference( - &self, - node: &'a IdentifierReference<'a>, - included: bool, - ) -> Option>> { - if included { + if need_val || self.is_referred(AstKind2::IdentifierReference(node)) { let IdentifierReference { span, name, .. } = node; let reference = self.semantic.symbols().get_reference(node.reference_id()); diff --git a/src/nodes/misc/simple_assignment_target.rs b/src/nodes/misc/simple_assignment_target.rs index 0e7e7748..2ed00875 100644 --- a/src/nodes/misc/simple_assignment_target.rs +++ b/src/nodes/misc/simple_assignment_target.rs @@ -50,7 +50,7 @@ impl<'a> Transformer<'a> { self.transform_member_expression_read(node.to_member_expression(), need_val) } SimpleAssignmentTarget::AssignmentTargetIdentifier(node) => { - self.transform_identifier_reference_read(node, need_val).map(Expression::Identifier) + self.transform_identifier_reference(node, need_val).map(Expression::Identifier) } _ => unreachable!(), } @@ -65,7 +65,7 @@ impl<'a> Transformer<'a> { .transform_member_expression_write(node.to_member_expression()) .map(SimpleAssignmentTarget::from), SimpleAssignmentTarget::AssignmentTargetIdentifier(node) => self - .transform_identifier_reference_write(node) + .transform_identifier_reference(node, false) .map(SimpleAssignmentTarget::AssignmentTargetIdentifier), _ => unreachable!(), } From 47f89819d131a9df3ca67802fd780ea4e46304ae Mon Sep 17 00:00:00 2001 From: _Kerman Date: Thu, 28 Nov 2024 10:53:06 +0800 Subject: [PATCH 07/10] fix --- src/entity/pure_result.rs | 6 ++++- src/nodes/expr/call_expression.rs | 31 +++++++++++----------- src/nodes/expr/chain_expression.rs | 41 ++++++++++++++++++----------- src/nodes/expr/member_expression.rs | 19 ++++++++----- src/transformer.rs | 11 ++------ 5 files changed, 60 insertions(+), 48 deletions(-) diff --git a/src/entity/pure_result.rs b/src/entity/pure_result.rs index e489b7f5..1366dd8f 100644 --- a/src/entity/pure_result.rs +++ b/src/entity/pure_result.rs @@ -182,7 +182,11 @@ impl<'a> PureResult<'a> { } impl<'a> EntityFactory<'a> { - pub fn pure_result(&self, node: PureCallNode<'a>, referred_deps: &'a mut ReferredDeps) -> Entity<'a> { + pub fn pure_result( + &self, + node: PureCallNode<'a>, + referred_deps: &'a mut ReferredDeps, + ) -> Entity<'a> { self.entity(PureResult { node, result: OnceCell::new(), diff --git a/src/nodes/expr/call_expression.rs b/src/nodes/expr/call_expression.rs index 24dc8e7e..3c5bb8c7 100644 --- a/src/nodes/expr/call_expression.rs +++ b/src/nodes/expr/call_expression.rs @@ -7,12 +7,9 @@ use crate::{ entity::{Entity, PureCallNode}, transformer::Transformer, }; -use oxc::{ - ast::{ - ast::{CallExpression, Expression}, - NONE, - }, - span::SPAN, +use oxc::ast::{ + ast::{CallExpression, Expression}, + NONE, }; impl<'a> Analyzer<'a> { @@ -32,7 +29,6 @@ impl<'a> Analyzer<'a> { cache_from_pure: Option<(Entity<'a>, Entity<'a>, Entity<'a>)>, ) -> Result<(usize, Entity<'a>, Option>), Entity<'a>> { let pure = cache_from_pure.is_none() && self.has_pure_notation(node); - println!("pure: {}", pure); let mut referred_deps = None; @@ -97,6 +93,15 @@ impl<'a> Transformer<'a> { &self, node: &'a CallExpression<'a>, need_val: bool, + ) -> Option> { + self.transform_call_expression_in_chain(node, need_val, None) + } + + pub fn transform_call_expression_in_chain( + &self, + node: &'a CallExpression<'a>, + need_val: bool, + parent_effects: Option>, ) -> Option> { let dep_id: AstKind2<'_> = AstKind2::CallExpression(node); @@ -108,21 +113,15 @@ impl<'a> Transformer<'a> { if !need_call { let args_effect = may_not_short_circuit.then(|| self.transform_arguments_no_call(arguments)); + let all_effects = build_effect!(&self.ast_builder, *span, parent_effects, args_effect); return if need_optional { - // FIXME: How to get the actual span? - let args_span = SPAN; Some(self.build_chain_expression_mock( *span, self.transform_expression(callee, true).unwrap(), - build_effect!(&self.ast_builder, args_span, args_effect).unwrap(), + all_effects.unwrap(), )) } else { - build_effect!( - &self.ast_builder, - *span, - self.transform_expression(callee, false), - args_effect - ) + self.transform_expression_in_chain(callee, false, all_effects) }; } diff --git a/src/nodes/expr/chain_expression.rs b/src/nodes/expr/chain_expression.rs index e74a5f13..ba4846e4 100644 --- a/src/nodes/expr/chain_expression.rs +++ b/src/nodes/expr/chain_expression.rs @@ -1,6 +1,6 @@ use crate::{analyzer::Analyzer, entity::Entity, transformer::Transformer}; use oxc::ast::{ - ast::{ChainElement, ChainExpression, Expression, MemberExpression}, + ast::{ChainElement, ChainExpression, Expression}, match_member_expression, }; @@ -50,23 +50,34 @@ impl<'a> Transformer<'a> { node: &'a ChainExpression<'a>, need_val: bool, ) -> Option> { - let ChainExpression { span, expression } = node; + let ChainExpression { expression, .. } = node; - let expression = match expression { - ChainElement::CallExpression(node) => self.transform_call_expression(node, need_val), - node => self.transform_member_expression_read(node.to_member_expression(), need_val), - }; + match expression { + ChainElement::CallExpression(node) => { + self.transform_call_expression_in_chain(node, need_val, None) + } + node => { + self.transform_member_expression_read_in_chain(node.to_member_expression(), need_val, None) + } + } + } - // FIXME: is this correct? - expression.map(|expression| match expression { + pub fn transform_expression_in_chain( + &self, + node: &'a Expression<'a>, + need_val: bool, + parent_effects: Option>, + ) -> Option> { + match node { + match_member_expression!(Expression) => self.transform_member_expression_read_in_chain( + node.to_member_expression(), + need_val, + parent_effects, + ), Expression::CallExpression(node) => { - self.ast_builder.expression_chain(*span, ChainElement::CallExpression(node)) + self.transform_call_expression_in_chain(node, need_val, parent_effects) } - match_member_expression!(Expression) => self.ast_builder.expression_chain( - *span, - ChainElement::from(MemberExpression::try_from(expression).unwrap()), - ), - _ => expression, - }) + expression => self.transform_expression(expression, need_val), + } } } diff --git a/src/nodes/expr/member_expression.rs b/src/nodes/expr/member_expression.rs index 15327d6a..61c46f30 100644 --- a/src/nodes/expr/member_expression.rs +++ b/src/nodes/expr/member_expression.rs @@ -108,6 +108,15 @@ impl<'a> Transformer<'a> { &self, node: &'a MemberExpression<'a>, need_val: bool, + ) -> Option> { + self.transform_member_expression_read_in_chain(node, need_val, None) + } + + pub fn transform_member_expression_read_in_chain( + &self, + node: &'a MemberExpression<'a>, + need_val: bool, + parent_effects: Option>, ) -> Option> { let dep_id = AstKind2::MemberExpression(node); @@ -122,19 +131,15 @@ impl<'a> Transformer<'a> { } _ => None, }); + let all_effects = build_effect!(&self.ast_builder, node.span(), parent_effects, key_effect); return if need_optional { Some(self.build_chain_expression_mock( node.span(), self.transform_expression(node.object(), true).unwrap(), - key_effect.unwrap().unwrap(), + all_effects.unwrap(), )) } else { - build_effect!( - &self.ast_builder, - node.span(), - self.transform_expression(node.object(), false), - key_effect - ) + self.transform_expression_in_chain(node.object(), false, all_effects) }; } diff --git a/src/transformer.rs b/src/transformer.rs index f5b25ce1..22f5ff18 100644 --- a/src/transformer.rs +++ b/src/transformer.rs @@ -46,15 +46,8 @@ pub struct Transformer<'a> { impl<'a> Transformer<'a> { pub fn new(analyzer: Analyzer<'a>) -> Self { - let Analyzer { - config, - allocator, - semantic, - data, - referred_deps, - conditional_data, - .. - } = analyzer; + let Analyzer { config, allocator, semantic, data, referred_deps, conditional_data, .. } = + analyzer; Transformer { config, From 452cb2a7629cea01827bc281c9fc855d33618718 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Thu, 28 Nov 2024 11:11:04 +0800 Subject: [PATCH 08/10] fix --- src/nodes/expr/chain_expression.rs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/nodes/expr/chain_expression.rs b/src/nodes/expr/chain_expression.rs index ba4846e4..7fd3e963 100644 --- a/src/nodes/expr/chain_expression.rs +++ b/src/nodes/expr/chain_expression.rs @@ -1,7 +1,10 @@ -use crate::{analyzer::Analyzer, entity::Entity, transformer::Transformer}; -use oxc::ast::{ - ast::{ChainElement, ChainExpression, Expression}, - match_member_expression, +use crate::{analyzer::Analyzer, build_effect, entity::Entity, transformer::Transformer}; +use oxc::{ + ast::{ + ast::{ChainElement, ChainExpression, Expression}, + match_member_expression, + }, + span::GetSpan, }; impl<'a> Analyzer<'a> { @@ -77,7 +80,16 @@ impl<'a> Transformer<'a> { Expression::CallExpression(node) => { self.transform_call_expression_in_chain(node, need_val, parent_effects) } - expression => self.transform_expression(expression, need_val), + _ => { + let expression = self.transform_expression(node, need_val); + if need_val { + debug_assert!(parent_effects.is_none()); + debug_assert!(expression.is_some()); + expression + } else { + build_effect!(&self.ast_builder, node.span(), expression, parent_effects) + } + } } } } From cf7984595215703fb79e6fe59cae994b177d5381 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Thu, 28 Nov 2024 13:17:50 +0800 Subject: [PATCH 09/10] fix --- src/entity/pure_result.rs | 72 ++++++++++++++++++------------- src/nodes/expr/call_expression.rs | 2 +- src/nodes/expr/new_expression.rs | 2 +- 3 files changed, 44 insertions(+), 32 deletions(-) diff --git a/src/entity/pure_result.rs b/src/entity/pure_result.rs index 1366dd8f..8c1fde6b 100644 --- a/src/entity/pure_result.rs +++ b/src/entity/pure_result.rs @@ -8,7 +8,7 @@ use crate::{ dep::ReferredDeps, }; use oxc::ast::ast::{CallExpression, NewExpression}; -use std::cell::{OnceCell, RefCell}; +use std::cell::{Cell, RefCell}; #[derive(Debug)] pub enum PureCallNode<'a> { @@ -18,9 +18,9 @@ pub enum PureCallNode<'a> { #[derive(Debug)] pub struct PureResult<'a> { - pub node: PureCallNode<'a>, - pub result: OnceCell>, - pub referred_deps: RefCell>, + node: PureCallNode<'a>, + result: Cell>>, + referred_deps: RefCell>, } impl<'a> EntityTrait<'a> for PureResult<'a> { @@ -156,41 +156,53 @@ impl<'a> EntityTrait<'a> for PureResult<'a> { } impl<'a> PureResult<'a> { - fn value(&self, analyzer: &mut Analyzer<'a>) -> Entity<'a> { - *self.result.get_or_init(|| { - let (val, this_referred_deps) = analyzer.exec_in_pure( - |analyzer| match &self.node { - PureCallNode::CallExpression(node, arguments) => { - let result = analyzer.exec_call_expression_in_chain(node, Some(*arguments)); - match result { - Ok((scope_count, value, undefined)) => { - analyzer.pop_multiple_cf_scopes(scope_count); - analyzer.factory.optional_union(value, undefined) - } - Err(value) => value, - } - } - PureCallNode::NewExpression(node, arguments) => { - analyzer.exec_new_expression(node, Some(*arguments)) + fn exec(&self, analyzer: &mut Analyzer<'a>) -> Entity<'a> { + match &self.node { + PureCallNode::CallExpression(node, cache) => { + let result = analyzer.exec_call_expression_in_chain(node, Some(*cache)); + match result { + Ok((scope_count, value, undefined)) => { + analyzer.pop_multiple_cf_scopes(scope_count); + analyzer.factory.optional_union(value, undefined) } - }, - self.referred_deps.take().unwrap(), - ); - analyzer.factory.computed(val, ConsumableNode::new(this_referred_deps)) - }) + Err(value) => value, + } + } + PureCallNode::NewExpression(node, arguments) => { + analyzer.exec_new_expression(node, Some(*arguments)) + } + } + } + + fn value(&self, analyzer: &mut Analyzer<'a>) -> Entity<'a> { + if let Some(result) = self.result.get() { + result + } else { + let result = if let Some(referred_deps) = self.referred_deps.take() { + let (val, this_referred_deps) = + analyzer.exec_in_pure(|analyzer| self.exec(analyzer), referred_deps); + analyzer.factory.computed(val, ConsumableNode::new(this_referred_deps)) + } else { + self.exec(analyzer) + }; + self.result.set(Some(result)); + result + } } } -impl<'a> EntityFactory<'a> { +impl<'a> Analyzer<'a> { pub fn pure_result( - &self, + &mut self, node: PureCallNode<'a>, referred_deps: &'a mut ReferredDeps, ) -> Entity<'a> { - self.entity(PureResult { + let x = PureResult { node, - result: OnceCell::new(), + result: Cell::new(None), referred_deps: RefCell::new(Some(referred_deps)), - }) + }; + x.value(self); + self.factory.entity(x) } } diff --git a/src/nodes/expr/call_expression.rs b/src/nodes/expr/call_expression.rs index 3c5bb8c7..e529c1a5 100644 --- a/src/nodes/expr/call_expression.rs +++ b/src/nodes/expr/call_expression.rs @@ -76,7 +76,7 @@ impl<'a> Analyzer<'a> { }; let ret_val = if pure { - self.factory.pure_result( + self.pure_result( PureCallNode::CallExpression(node, (callee, this, args)), referred_deps.unwrap(), ) diff --git a/src/nodes/expr/new_expression.rs b/src/nodes/expr/new_expression.rs index 0adbfd52..8eed44e2 100644 --- a/src/nodes/expr/new_expression.rs +++ b/src/nodes/expr/new_expression.rs @@ -17,7 +17,7 @@ impl<'a> Analyzer<'a> { ) -> Entity<'a> { if args_from_pure.is_none() && self.has_pure_notation(node) { let arguments = self.exec_arguments(&node.arguments); - return self.factory.pure_result( + return self.pure_result( PureCallNode::NewExpression(node, arguments), self.allocator.alloc(ReferredDeps::default()), ); From 1a1078d7b837ce85a9fcc746fdb4471584f80ef7 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Thu, 28 Nov 2024 05:18:58 +0000 Subject: [PATCH 10/10] [autofix.ci] apply automated fixes --- src/entity/pure_result.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/entity/pure_result.rs b/src/entity/pure_result.rs index 8c1fde6b..87ef71ea 100644 --- a/src/entity/pure_result.rs +++ b/src/entity/pure_result.rs @@ -1,6 +1,6 @@ use super::{ entity::{EnumeratedProperties, IteratedElements}, - Entity, EntityFactory, EntityTrait, TypeofResult, + Entity, EntityTrait, TypeofResult, }; use crate::{ analyzer::Analyzer,