From 090635dc635b996707632d2bf2f8e7d755149976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Og=C3=B3rek?= Date: Tue, 22 Nov 2022 21:33:52 +0100 Subject: [PATCH 1/2] fix: Correctly handle computed object assignments --- src/swc.rs | 31 +++++++++++++++++++++++++++++++ tests/fixtures/trace/sync.mjs | 14 ++++++++++++++ tests/integration.rs | 4 ++++ 3 files changed, 49 insertions(+) diff --git a/src/swc.rs b/src/swc.rs index fdae5b3..f200280 100644 --- a/src/swc.rs +++ b/src/swc.rs @@ -340,6 +340,7 @@ fn prefix_getters_setters(kind: ast::MethodKind, scope_name: &mut ScopeName) { /// This is only possible if the expression is an identifier or a member expression. fn infer_name_from_expr(mut expr: &ast::Expr) -> Option { let mut scope_name = ScopeName::new(); + loop { match expr { ast::Expr::Ident(ident) => { @@ -356,6 +357,18 @@ fn infer_name_from_expr(mut expr: &ast::Expr) -> Option { .push_front(NameComponent::ident(ident.clone())); scope_name.components.push_front(NameComponent::interp(".")); } + + if let Some(computed_prop) = member.prop.as_computed() { + if let Some((computed_name, need_delimiter)) = + computed_prop_name_to_component(computed_prop) + { + scope_name.components.push_front(computed_name); + if need_delimiter { + scope_name.components.push_front(NameComponent::interp(".")); + } + } + } + expr = &member.obj; } @@ -371,6 +384,24 @@ fn infer_name_from_expr(mut expr: &ast::Expr) -> Option { } } +fn computed_prop_name_to_component(prop: &ast::ComputedPropName) -> Option<(NameComponent, bool)> { + if let Some(lit) = prop.expr.as_lit() { + if let ast::Lit::Str(prop) = lit { + return Some((NameComponent::interp(prop.value.to_string()), true)); + } + + if let ast::Lit::Num(prop) = lit { + return Some((NameComponent::interp(format!("[{}]", prop.value)), false)); + } + } + + if let Some(ident) = prop.expr.as_ident() { + return Some((NameComponent::interp(format!("[{}]", ident.sym)), false)); + } + + None +} + fn prop_name_to_component(prop: &ast::PropName) -> NameComponent { match prop { ast::PropName::Ident(ref i) => NameComponent::ident(i.clone()), diff --git a/tests/fixtures/trace/sync.mjs b/tests/fixtures/trace/sync.mjs index 425cd0d..513c0a2 100644 --- a/tests/fixtures/trace/sync.mjs +++ b/tests/fixtures/trace/sync.mjs @@ -84,3 +84,17 @@ let obj = { throw new Error(); }, }; + +Klass.prototype[42] = function () { + beepBoop(); +}; + +Klass.prototype["method"] = function () { + beepBoop(); +}; + +const method = "computedMethod"; + +Klass.prototype[method] = function () { + beepBoop(); +}; diff --git a/tests/integration.rs b/tests/integration.rs index 121f080..116613c 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -69,6 +69,10 @@ fn resolves_scope_names() { index.lookup(offset) }; + assert_eq!(lookup(99, 3), NamedScope("Klass.prototype[method]")); + assert_eq!(lookup(93, 3), NamedScope("Klass.prototype.method")); + assert_eq!(lookup(89, 3), NamedScope("Klass.prototype[42]")); + // objectLiteralAnon@http://127.0.0.1:8080/sync.mjs:84:11 // at Object.objectLiteralAnon (http://127.0.0.1:8080/sync.mjs:84:11) assert_eq!(lookup(84, 11), NamedScope("obj.objectLiteralAnon")); From a1a823ed99d8e6eeece3df81e831651dda11661e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Og=C3=B3rek?= Date: Wed, 23 Nov 2022 17:51:00 +0100 Subject: [PATCH 2/2] Apply code review --- src/swc.rs | 46 +++++++++++++++++++---------------- tests/extract.rs | 22 ++++++++++++++++- tests/fixtures/trace/sync.mjs | 14 ----------- tests/integration.rs | 4 --- 4 files changed, 46 insertions(+), 40 deletions(-) diff --git a/src/swc.rs b/src/swc.rs index f200280..8f2cc37 100644 --- a/src/swc.rs +++ b/src/swc.rs @@ -359,14 +359,7 @@ fn infer_name_from_expr(mut expr: &ast::Expr) -> Option { } if let Some(computed_prop) = member.prop.as_computed() { - if let Some((computed_name, need_delimiter)) = - computed_prop_name_to_component(computed_prop) - { - scope_name.components.push_front(computed_name); - if need_delimiter { - scope_name.components.push_front(NameComponent::interp(".")); - } - } + push_computed_prop_name(computed_prop, &mut scope_name) } expr = &member.obj; @@ -384,22 +377,33 @@ fn infer_name_from_expr(mut expr: &ast::Expr) -> Option { } } -fn computed_prop_name_to_component(prop: &ast::ComputedPropName) -> Option<(NameComponent, bool)> { - if let Some(lit) = prop.expr.as_lit() { - if let ast::Lit::Str(prop) = lit { - return Some((NameComponent::interp(prop.value.to_string()), true)); - } - - if let ast::Lit::Num(prop) = lit { - return Some((NameComponent::interp(format!("[{}]", prop.value)), false)); - } +fn push_computed_prop_name(prop_name: &ast::ComputedPropName, scope_name: &mut ScopeName) { + if let Some(literal) = prop_name.expr.as_lit() { + let component = NameComponent::interp(format!("[{}]", lit_as_string(literal))); + scope_name.components.push_front(component); + } else if let Some(ident) = prop_name.expr.as_ident() { + scope_name.components.push_front(NameComponent::interp("]")); + scope_name + .components + .push_front(NameComponent::ident(ident.clone())); + scope_name.components.push_front(NameComponent::interp("[")); + } else { + scope_name + .components + .push_front(NameComponent::interp("[]")); } +} - if let Some(ident) = prop.expr.as_ident() { - return Some((NameComponent::interp(format!("[{}]", ident.sym)), false)); +fn lit_as_string(lit: &ast::Lit) -> String { + match lit { + ast::Lit::Str(v) => format!("\"{}\"", v.value), + ast::Lit::Num(v) => v.value.to_string(), + ast::Lit::BigInt(v) => format!("{}n", v.value), + ast::Lit::Bool(v) => v.value.to_string(), + ast::Lit::Regex(v) => format!("/{}/{}", v.exp, v.flags), + ast::Lit::Null(_) => String::from("null"), + ast::Lit::JSXText(v) => v.value.to_string(), } - - None } fn prop_name_to_component(prop: &ast::PropName) -> NameComponent { diff --git a/tests/extract.rs b/tests/extract.rs index d8c1ad2..aac347e 100644 --- a/tests/extract.rs +++ b/tests/extract.rs @@ -218,7 +218,7 @@ fn extract_anon_obj_literal() { #[test] fn extract_empty_function() { let src = r#" - (function () { + (function () { return () => {}; })() "#; @@ -254,3 +254,23 @@ fn extract_nested_iife_objects() { ]; assert_eq!(scopes, expected); } + +#[test] +fn extract_computed_properties() { + let src = r#" + Klass.prototype[42] = () => {} + Klass.prototype["method"] = () => {} + Klass.prototype[method] = () => {} + Klass.prototype[1 + 1] = () => {}; + "#; + let scopes = extract_scope_names(src).unwrap(); + let scopes = scope_strs(scopes); + + let expected = [ + Some("Klass.prototype[42]".into()), + Some("Klass.prototype[\"method\"]".into()), + Some("Klass.prototype[method]".into()), + Some("Klass.prototype[]".into()), + ]; + assert_eq!(scopes, expected); +} diff --git a/tests/fixtures/trace/sync.mjs b/tests/fixtures/trace/sync.mjs index 513c0a2..425cd0d 100644 --- a/tests/fixtures/trace/sync.mjs +++ b/tests/fixtures/trace/sync.mjs @@ -84,17 +84,3 @@ let obj = { throw new Error(); }, }; - -Klass.prototype[42] = function () { - beepBoop(); -}; - -Klass.prototype["method"] = function () { - beepBoop(); -}; - -const method = "computedMethod"; - -Klass.prototype[method] = function () { - beepBoop(); -}; diff --git a/tests/integration.rs b/tests/integration.rs index 116613c..121f080 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -69,10 +69,6 @@ fn resolves_scope_names() { index.lookup(offset) }; - assert_eq!(lookup(99, 3), NamedScope("Klass.prototype[method]")); - assert_eq!(lookup(93, 3), NamedScope("Klass.prototype.method")); - assert_eq!(lookup(89, 3), NamedScope("Klass.prototype[42]")); - // objectLiteralAnon@http://127.0.0.1:8080/sync.mjs:84:11 // at Object.objectLiteralAnon (http://127.0.0.1:8080/sync.mjs:84:11) assert_eq!(lookup(84, 11), NamedScope("obj.objectLiteralAnon"));