From 03f9b93f580f437267c1e324384f5ee9938ae2e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Wed, 9 Dec 2020 14:23:44 +0900 Subject: [PATCH] Fix bundler (#1247) swc_bundler: - Handle indirect wrapped es modules. (denoland/deno#8597, denoland/deno#8625) - Respect `export { foo }`. (denoland/deno#8626) swc_ecma_parser: - Allow `??=`, `||=`, `??=` in non-ts modules. (denoland/deno#8627) swc_ecma_transforms: - Make `hygiene` check if a variable with expanded name exists. (denoland/deno#8620) - Handle `??=` correctly. --- bundler/Cargo.toml | 2 +- bundler/scripts/test.sh | 3 +- bundler/src/bundler/chunk/computed_key.rs | 11 +- bundler/src/bundler/chunk/merge.rs | 57 ++- bundler/src/util.rs | 2 +- bundler/tests/deno.rs | 484 ++++++++++++++++++ bundler/tests/deno/deno-8620/entry.ts | 8 + bundler/tests/deno/deno-8627/input.ts | 1 + bundler/tests/fixture/deno-8625/input/bar.ts | 5 + .../tests/fixture/deno-8625/input/entry.ts | 4 + bundler/tests/fixture/deno-8625/input/foo.ts | 3 + .../tests/fixture/deno-8625/output/entry.ts | 15 + .../tests/fixture/deno-8626-1/input/entry.ts | 3 + .../tests/fixture/deno-8626-1/input/mod.ts | 3 + .../tests/fixture/deno-8626-1/output/entry.ts | 10 + .../tests/fixture/deno-8627/input/entry.ts | 2 + .../tests/fixture/deno-8627/output/entry.ts | 2 + ecmascript/parser/Cargo.toml | 2 +- ecmascript/parser/src/lexer/mod.rs | 9 +- ecmascript/transforms/Cargo.toml | 2 +- .../compat/es2020/nullish_coalescing/mod.rs | 114 ++++- .../compat/es2020/nullish_coalescing/tests.rs | 12 + ecmascript/transforms/src/hygiene.rs | 71 +-- ecmascript/transforms/src/resolver/tests.rs | 23 + 24 files changed, 761 insertions(+), 87 deletions(-) create mode 100644 bundler/tests/deno/deno-8620/entry.ts create mode 100644 bundler/tests/deno/deno-8627/input.ts create mode 100644 bundler/tests/fixture/deno-8625/input/bar.ts create mode 100644 bundler/tests/fixture/deno-8625/input/entry.ts create mode 100644 bundler/tests/fixture/deno-8625/input/foo.ts create mode 100644 bundler/tests/fixture/deno-8625/output/entry.ts create mode 100644 bundler/tests/fixture/deno-8626-1/input/entry.ts create mode 100644 bundler/tests/fixture/deno-8626-1/input/mod.ts create mode 100644 bundler/tests/fixture/deno-8626-1/output/entry.ts create mode 100644 bundler/tests/fixture/deno-8627/input/entry.ts create mode 100644 bundler/tests/fixture/deno-8627/output/entry.ts diff --git a/bundler/Cargo.toml b/bundler/Cargo.toml index a27a3f55e3d9..b48d013f95d4 100644 --- a/bundler/Cargo.toml +++ b/bundler/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" license = "Apache-2.0/MIT" name = "swc_bundler" repository = "https://github.com/swc-project/swc.git" -version = "0.17.5" +version = "0.17.6" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] diff --git a/bundler/scripts/test.sh b/bundler/scripts/test.sh index d13815b455d2..6188bbce0984 100755 --- a/bundler/scripts/test.sh +++ b/bundler/scripts/test.sh @@ -2,4 +2,5 @@ set -eux cargo test --test fixture -cargo test --test deno $@ \ No newline at end of file +(cd ../spack && cargo test --test fixture) +cargo test --test deno $@ -- --nocapture \ No newline at end of file diff --git a/bundler/src/bundler/chunk/computed_key.rs b/bundler/src/bundler/chunk/computed_key.rs index 919c20a6ae39..25bde77771ac 100644 --- a/bundler/src/bundler/chunk/computed_key.rs +++ b/bundler/src/bundler/chunk/computed_key.rs @@ -184,10 +184,6 @@ impl Fold for ExportToReturn { Some(Stmt::Decl(export.decl)) } - // Ignore export {} specified by user. - ModuleDecl::ExportNamed(NamedExport { - span, src: None, .. - }) if span.ctxt != self.synthesized_ctxt => None, ModuleDecl::ExportDefaultDecl(export) => match export.decl { DefaultDecl::Class(expr) => { let ident = expr.ident; @@ -248,7 +244,12 @@ impl Fold for ExportToReturn { } } - return ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(named)); + // Ignore export {} specified by user. + if named.src.is_none() && named.span.ctxt != self.synthesized_ctxt { + None + } else { + return ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(named)); + } } ModuleDecl::TsImportEquals(_) => None, ModuleDecl::TsExportAssignment(_) => None, diff --git a/bundler/src/bundler/chunk/merge.rs b/bundler/src/bundler/chunk/merge.rs index b01507d48780..7f99503a37be 100644 --- a/bundler/src/bundler/chunk/merge.rs +++ b/bundler/src/bundler/chunk/merge.rs @@ -116,24 +116,23 @@ where module = self.wrap_esm(ctx, dep_id, module)?; // Inject local_name = wrapped_esm_module_name - let module_ident = specifiers - .iter() - .find_map(|specifier| match specifier { - Specifier::Namespace { - local, all: true, .. - } => Some(local.clone()), - _ => None, - }) - .unwrap(); + let module_ident = specifiers.iter().find_map(|specifier| match specifier { + Specifier::Namespace { + local, all: true, .. + } => Some(local.clone()), + _ => None, + }); let esm_id = self.scope.wrapped_esm_id(dep_id).unwrap(); - module.body.push( - esm_id - .clone() - .assign_to(module_ident.clone()) - .into_module_item("merge_direct_import"), - ); + if let Some(module_ident) = &module_ident { + module.body.push( + esm_id + .clone() + .assign_to(module_ident.clone()) + .into_module_item("merge_direct_import"), + ); + } let plan = ctx.plan.normal.get(&dep_id); let default_plan; @@ -760,7 +759,26 @@ where continue; } } - ImportSpecifier::Namespace(_) => {} + ImportSpecifier::Namespace(s) => { + if let Some((src, _)) = info + .imports + .specifiers + .iter() + .find(|s| s.0.src.value == import.src.value) + { + let esm_id = self.scope.wrapped_esm_id(src.module_id).expect( + "If a namespace impoet specifier is preserved, it means \ + failutre of deblobbing and as a result module should be \ + marked as wrpaped esm", + ); + new.push( + esm_id.clone().assign_to(s.local.clone()).into_module_item( + "from_replace_import_specifiers: namespaced", + ), + ); + continue; + } + } } } @@ -828,9 +846,10 @@ where .orig .clone() .assign_to(lhs) - .into_module_item( - "import_deps_named_alias", - ), + .into_module_item(&format!( + "import_deps_named_alias of {}", + info.fm.name + )), ); } } diff --git a/bundler/src/util.rs b/bundler/src/util.rs index d2b21d0940ed..14e12da5ecb4 100644 --- a/bundler/src/util.rs +++ b/bundler/src/util.rs @@ -6,7 +6,7 @@ use swc_ecma_utils::ident::IdentLike; use swc_ecma_visit::{noop_visit_mut_type, VisitMut}; pub(crate) trait VarDeclaratorExt: Into { - fn into_module_item(self, _name: &'static str) -> ModuleItem { + fn into_module_item(self, _name: &str) -> ModuleItem { ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl { span: DUMMY_SP, kind: VarDeclKind::Const, diff --git a/bundler/tests/deno.rs b/bundler/tests/deno.rs index 211151caf001..68d47de3c184 100644 --- a/bundler/tests/deno.rs +++ b/bundler/tests/deno.rs @@ -389,6 +389,490 @@ fn deno_8545() { fn deno_8573() { run("tests/deno/deno-8573/entry.ts", &[]) } +#[test] +fn deno_8597() { + run( + "https://cdn.skypack.dev/@tensorflow/tfjs", + &[ + "Abs", + "Acos", + "Acosh", + "AdadeltaOptimizer", + "AdagradOptimizer", + "AdamOptimizer", + "AdamaxOptimizer", + "Add", + "AddN", + "All", + "Any", + "ArgMax", + "ArgMin", + "Asin", + "Asinh", + "Atan", + "Atan2", + "Atanh", + "AvgPool", + "AvgPool3D", + "AvgPool3DBackprop", + "AvgPoolBackprop", + "BatchMatMul", + "BatchToSpaceND", + "BroadcastTo", + "Callback", + "CallbackList", + "Cast", + "Ceil", + "ClipByValue", + "Complex", + "Concat", + "Conv2D", + "Conv2DBackpropFilter", + "Conv2DBackpropInput", + "Conv3D", + "Conv3DBackpropFilterV2", + "Conv3DBackpropInputV2", + "Cos", + "Cosh", + "CropAndResize", + "Cumsum", + "CustomCallback", + "DataStorage", + "DepthToSpace", + "DepthwiseConv2dNative", + "DepthwiseConv2dNativeBackpropFilter", + "DepthwiseConv2dNativeBackpropInput", + "Diag", + "Dilation2D", + "Dilation2DBackpropFilter", + "Dilation2DBackpropInput", + "Div", + "ENV", + "EarlyStopping", + "Elu", + "EluGrad", + "Environment", + "Equal", + "Erf", + "Exp", + "Expm1", + "FFT", + "Fill", + "FlipLeftRight", + "Floor", + "FloorDiv", + "FromPixels", + "FusedBatchNorm", + "FusedConv2D", + "FusedDepthwiseConv2D", + "GatherNd", + "GatherV2", + "GraphModel", + "Greater", + "GreaterEqual", + "History", + "IFFT", + "Identity", + "Imag", + "InputSpec", + "IsFinite", + "IsInf", + "IsNan", + "KernelBackend", + "LRN", + "LRNBackprop", + "LayerVariable", + "LayersModel", + "Less", + "LessEqual", + "LinSpace", + "Log", + "Log1p", + "LogSoftmax", + "LogicalAnd", + "LogicalNot", + "LogicalOr", + "Max", + "MaxPool", + "MaxPool3D", + "MaxPool3DBackprop", + "MaxPoolBackprop", + "MaxPoolWithArgmax", + "Maximum", + "Mean", + "Min", + "Minimum", + "MirrorPad", + "Mod", + "MomentumOptimizer", + "Multiply", + "Negate", + "NonMaxSuppressionV3", + "NonMaxSuppressionV4", + "NonMaxSuppressionV5", + "NotEqual", + "OP_SCOPE_SUFFIX", + "OneHot", + "OnesLike", + "Optimizer", + "PadV2", + "Pool", + "Pow", + "Prelu", + "Prod", + "RMSPropOptimizer", + "RNN", + "Range", + "Rank", + "Real", + "Reciprocal", + "Reduction", + "Relu", + "Relu6", + "Reshape", + "ResizeBilinear", + "ResizeBilinearGrad", + "ResizeNearestNeighbor", + "ResizeNearestNeighborGrad", + "Reverse", + "RotateWithOffset", + "Round", + "Rsqrt", + "SGDOptimizer", + "ScatterNd", + "SelectV2", + "Selu", + "Sequential", + "Sigmoid", + "Sign", + "Sin", + "Sinh", + "Slice", + "Softmax", + "Softplus", + "SpaceToBatchND", + "SparseToDense", + "SplitV", + "Sqrt", + "Square", + "SquaredDifference", + "Step", + "StridedSlice", + "Sub", + "Sum", + "SymbolicTensor", + "Tan", + "Tanh", + "Tensor", + "TensorBuffer", + "Tile", + "TopK", + "Transpose", + "Unique", + "Unpack", + "UnsortedSegmentSum", + "Variable", + "ZerosLike", + "_FusedMatMul", + "abs", + "acos", + "acosh", + "add", + "addN", + "addStrict", + "all", + "any", + "argMax", + "argMin", + "asin", + "asinh", + "atan", + "atan2", + "atanh", + "avgPool", + "avgPool3d", + "backend", + "backend_util", + "basicLSTMCell", + "batchNorm", + "batchNorm2d", + "batchNorm3d", + "batchNorm4d", + "batchToSpaceND", + "booleanMaskAsync", + "broadcastTo", + "browser", + "buffer", + "callbacks", + "cast", + "ceil", + "clipByValue", + "clone", + "complex", + "concat", + "concat1d", + "concat2d", + "concat3d", + "concat4d", + "constraints", + "conv1d", + "conv2d", + "conv2dTranspose", + "conv3d", + "conv3dTranspose", + "copyRegisteredKernels", + "cos", + "cosh", + "cosineWindow", + "cumsum", + "customGrad", + "data", + "default", + "deprecationWarn", + "depthToSpace", + "depthwiseConv2d", + "deregisterOp", + "device_util", + "diag", + "dilation2d", + "disableDeprecationWarnings", + "dispose", + "disposeVariables", + "div", + "divNoNan", + "divStrict", + "dot", + "dropout", + "elu", + "enableDebugMode", + "enableProdMode", + "enclosingPowerOfTwo", + "engine", + "env", + "equal", + "equalStrict", + "erf", + "exp", + "expandDims", + "expm1", + "eye", + "fft", + "fill", + "findBackend", + "findBackendFactory", + "floor", + "floorDiv", + "fused", + "gather", + "gatherND", + "gather_util", + "getBackend", + "getGradient", + "getKernel", + "getKernelsForBackend", + "grad", + "grads", + "greater", + "greaterEqual", + "greaterEqualStrict", + "greaterStrict", + "ifft", + "imag", + "image", + "inTopKAsync", + "initializers", + "input", + "io", + "irfft", + "isFinite", + "isInf", + "isNaN", + "keep", + "kernel_impls", + "layers", + "leakyRelu", + "less", + "lessEqual", + "lessEqualStrict", + "lessStrict", + "linalg", + "linspace", + "loadGraphModel", + "loadLayersModel", + "localResponseNormalization", + "log", + "log1p", + "logSigmoid", + "logSoftmax", + "logSumExp", + "logicalAnd", + "logicalNot", + "logicalOr", + "logicalXor", + "losses", + "matMul", + "math", + "max", + "maxPool", + "maxPool3d", + "maxPoolWithArgmax", + "maximum", + "maximumStrict", + "mean", + "memory", + "metrics", + "min", + "minimum", + "minimumStrict", + "mirrorPad", + "mod", + "modStrict", + "model", + "models", + "moments", + "movingAverage", + "mul", + "mulStrict", + "multiRNNCell", + "multinomial", + "neg", + "nextFrame", + "norm", + "notEqual", + "notEqualStrict", + "oneHot", + "ones", + "onesLike", + "op", + "outerProduct", + "pad", + "pad1d", + "pad2d", + "pad3d", + "pad4d", + "pool", + "pow", + "powStrict", + "prelu", + "print", + "prod", + "profile", + "rand", + "randomGamma", + "randomNormal", + "randomUniform", + "range", + "ready", + "real", + "reciprocal", + "registerBackend", + "registerCallbackConstructor", + "registerGradient", + "registerKernel", + "registerOp", + "regularizers", + "relu", + "relu6", + "removeBackend", + "reshape", + "reverse", + "reverse1d", + "reverse2d", + "reverse3d", + "reverse4d", + "rfft", + "round", + "rsqrt", + "scalar", + "scatterND", + "scatter_util", + "selu", + "separableConv2d", + "sequential", + "serialization", + "setBackend", + "setPlatform", + "setdiff1dAsync", + "sigmoid", + "sign", + "signal", + "sin", + "sinh", + "slice", + "slice1d", + "slice2d", + "slice3d", + "slice4d", + "slice_util", + "softmax", + "softplus", + "spaceToBatchND", + "sparseToDense", + "spectral", + "split", + "sqrt", + "square", + "squaredDifference", + "squaredDifferenceStrict", + "squeeze", + "stack", + "step", + "stridedSlice", + "sub", + "subStrict", + "sum", + "sumOutType", + "tan", + "tanh", + "tensor", + "tensor1d", + "tensor2d", + "tensor3d", + "tensor4d", + "tensor5d", + "tensor6d", + "tensor_util", + "test_util", + "tidy", + "tile", + "time", + "topk", + "train", + "transpose", + "truncatedNormal", + "unique", + "unregisterGradient", + "unregisterKernel", + "unsortedSegmentSum", + "unstack", + "upcastType", + "util", + "valueAndGrad", + "valueAndGrads", + "variable", + "variableGrads", + "version", + "version_converter", + "version_core", + "version_layers", + "where", + "whereAsync", + "zeros", + "zerosLike", + ], + ) +} + +#[test] +fn deno_8620() { + run("tests/deno/deno-8620/entry.ts", &[]) +} + +#[test] +#[ignore = "Requires newer version of deno"] +fn deno_8627() { + run("tests/deno/deno-8627/input.ts", &[]) +} #[test] fn merging_order_01() { diff --git a/bundler/tests/deno/deno-8620/entry.ts b/bundler/tests/deno/deno-8620/entry.ts new file mode 100644 index 000000000000..d02c7e44734a --- /dev/null +++ b/bundler/tests/deno/deno-8620/entry.ts @@ -0,0 +1,8 @@ +/// +/// +/// +/// +/// +/// +import * as THREE from 'https://cdn.skypack.dev/three?dts'; + diff --git a/bundler/tests/deno/deno-8627/input.ts b/bundler/tests/deno/deno-8627/input.ts new file mode 100644 index 000000000000..3402d3f78fcc --- /dev/null +++ b/bundler/tests/deno/deno-8627/input.ts @@ -0,0 +1 @@ +let a = null, b = 2; console.log(a ??= b); \ No newline at end of file diff --git a/bundler/tests/fixture/deno-8625/input/bar.ts b/bundler/tests/fixture/deno-8625/input/bar.ts new file mode 100644 index 000000000000..1ad5d0a8ada7 --- /dev/null +++ b/bundler/tests/fixture/deno-8625/input/bar.ts @@ -0,0 +1,5 @@ +import { Foo } from "./foo" + +const bar = Foo('bar') + +export default bar \ No newline at end of file diff --git a/bundler/tests/fixture/deno-8625/input/entry.ts b/bundler/tests/fixture/deno-8625/input/entry.ts new file mode 100644 index 000000000000..806fbbeaa1fe --- /dev/null +++ b/bundler/tests/fixture/deno-8625/input/entry.ts @@ -0,0 +1,4 @@ +import * as foo from "./foo" +import bar from "./bar" + +console.log(foo, bar) \ No newline at end of file diff --git a/bundler/tests/fixture/deno-8625/input/foo.ts b/bundler/tests/fixture/deno-8625/input/foo.ts new file mode 100644 index 000000000000..fae1824a8cc2 --- /dev/null +++ b/bundler/tests/fixture/deno-8625/input/foo.ts @@ -0,0 +1,3 @@ +export function Foo(name: string) { + return 'foo' + name +} \ No newline at end of file diff --git a/bundler/tests/fixture/deno-8625/output/entry.ts b/bundler/tests/fixture/deno-8625/output/entry.ts new file mode 100644 index 000000000000..3f926bcd35b7 --- /dev/null +++ b/bundler/tests/fixture/deno-8625/output/entry.ts @@ -0,0 +1,15 @@ +const mod = function() { + function Foo(name) { + return 'foo' + name; + } + return { + Foo + }; +}(); +const Foo = mod.Foo; +const Foo1 = Foo; +const bar = Foo1('bar'); +const __default = bar; +const bar1 = __default; +const foo = mod; +console.log(foo, bar1); diff --git a/bundler/tests/fixture/deno-8626-1/input/entry.ts b/bundler/tests/fixture/deno-8626-1/input/entry.ts new file mode 100644 index 000000000000..6d2051dd296e --- /dev/null +++ b/bundler/tests/fixture/deno-8626-1/input/entry.ts @@ -0,0 +1,3 @@ +import * as foo from "./mod.ts" + +console.log(foo) \ No newline at end of file diff --git a/bundler/tests/fixture/deno-8626-1/input/mod.ts b/bundler/tests/fixture/deno-8626-1/input/mod.ts new file mode 100644 index 000000000000..512f3efc0ce9 --- /dev/null +++ b/bundler/tests/fixture/deno-8626-1/input/mod.ts @@ -0,0 +1,3 @@ +const foo = 'bar' +export { foo } +export const bar = 123 \ No newline at end of file diff --git a/bundler/tests/fixture/deno-8626-1/output/entry.ts b/bundler/tests/fixture/deno-8626-1/output/entry.ts new file mode 100644 index 000000000000..55d78ee659d7 --- /dev/null +++ b/bundler/tests/fixture/deno-8626-1/output/entry.ts @@ -0,0 +1,10 @@ +const mod = function() { + const foo = 'bar'; + const bar = 123; + return { + foo: foo, + bar + }; +}(); +const foo = mod; +console.log(foo); diff --git a/bundler/tests/fixture/deno-8627/input/entry.ts b/bundler/tests/fixture/deno-8627/input/entry.ts new file mode 100644 index 000000000000..23fc017c6a84 --- /dev/null +++ b/bundler/tests/fixture/deno-8627/input/entry.ts @@ -0,0 +1,2 @@ +let a = null, b = 2; +console.log(a ??= b); \ No newline at end of file diff --git a/bundler/tests/fixture/deno-8627/output/entry.ts b/bundler/tests/fixture/deno-8627/output/entry.ts new file mode 100644 index 000000000000..28437318b325 --- /dev/null +++ b/bundler/tests/fixture/deno-8627/output/entry.ts @@ -0,0 +1,2 @@ +let a = null, b = 2; +console.log(a ??= b); diff --git a/ecmascript/parser/Cargo.toml b/ecmascript/parser/Cargo.toml index 23ac492d8aa4..bbd4ed435f21 100644 --- a/ecmascript/parser/Cargo.toml +++ b/ecmascript/parser/Cargo.toml @@ -7,7 +7,7 @@ include = ["Cargo.toml", "src/**/*.rs", "examples/**/*.rs"] license = "Apache-2.0/MIT" name = "swc_ecma_parser" repository = "https://github.com/swc-project/swc.git" -version = "0.43.1" +version = "0.43.2" [features] default = [] diff --git a/ecmascript/parser/src/lexer/mod.rs b/ecmascript/parser/src/lexer/mod.rs index 924a7420f05c..08a8f09db1f5 100644 --- a/ecmascript/parser/src/lexer/mod.rs +++ b/ecmascript/parser/src/lexer/mod.rs @@ -18,6 +18,7 @@ use swc_common::{ comments::{Comment, Comments}, BytePos, Span, }; +use swc_ecma_ast::op; pub mod input; mod jsx; @@ -225,7 +226,7 @@ impl<'a, I: Input> Lexer<'a, I> { Some('?') => { self.input.bump(); self.input.bump(); - if self.syntax.typescript() && self.input.cur() == Some('=') { + if self.input.cur() == Some('=') { self.input.bump(); return Ok(Some(tok!("??="))); } @@ -336,11 +337,11 @@ impl<'a, I: Input> Lexer<'a, I> { if self.input.cur() == Some(c) { self.input.bump(); - if self.syntax.typescript() && self.input.cur() == Some('=') { + if self.input.cur() == Some('=') { self.input.bump(); return Ok(Some(AssignOp(match token { - BitAnd => AndAssign, - BitOr => OrAssign, + BitAnd => op!("&&="), + BitOr => op!("||="), _ => unreachable!(), }))); } diff --git a/ecmascript/transforms/Cargo.toml b/ecmascript/transforms/Cargo.toml index edec05902f8e..95efaf249677 100644 --- a/ecmascript/transforms/Cargo.toml +++ b/ecmascript/transforms/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" license = "Apache-2.0/MIT" name = "swc_ecma_transforms" repository = "https://github.com/swc-project/swc.git" -version = "0.30.4" +version = "0.30.5" [features] const-modules = ["dashmap"] diff --git a/ecmascript/transforms/src/compat/es2020/nullish_coalescing/mod.rs b/ecmascript/transforms/src/compat/es2020/nullish_coalescing/mod.rs index 4fd84582e933..72edd773d294 100644 --- a/ecmascript/transforms/src/compat/es2020/nullish_coalescing/mod.rs +++ b/ecmascript/transforms/src/compat/es2020/nullish_coalescing/mod.rs @@ -1,9 +1,10 @@ use crate::{ + ext::MapWithMut, perf::Check, util::{alias_if_required, undefined, StmtLike}, }; use std::mem::replace; -use swc_common::DUMMY_SP; +use swc_common::{Span, DUMMY_SP}; use swc_ecma_ast::*; use swc_ecma_transforms_macros::fast_path; use swc_ecma_visit::{noop_fold_type, noop_visit_type, Fold, FoldWith, Node, Visit, VisitWith}; @@ -69,7 +70,7 @@ impl Fold for NullishCoalescing { } fn fold_expr(&mut self, e: Expr) -> Expr { - let e = e.fold_children_with(self); + let mut e = e.fold_children_with(self); match e { Expr::Bin(BinExpr { @@ -101,27 +102,62 @@ impl Fold for NullishCoalescing { Expr::Ident(l.clone()) }; - return Expr::Cond(CondExpr { - span, - test: Box::new(Expr::Bin(BinExpr { - span: DUMMY_SP, - left: Box::new(Expr::Bin(BinExpr { - span: DUMMY_SP, - left: Box::new(var_expr), - op: op!("!=="), - right: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))), - })), - op: op!("&&"), - right: Box::new(Expr::Bin(BinExpr { - span: DUMMY_SP, - left: Box::new(Expr::Ident(l.clone())), - op: op!("!=="), - right: undefined(DUMMY_SP), - })), - })), - cons: Box::new(Expr::Ident(l.clone())), - alt: right, - }); + return make_cond(span, &l, var_expr, right); + } + + Expr::Assign(ref mut assign @ AssignExpr { op: op!("??="), .. }) => { + match &mut assign.left { + PatOrExpr::Expr(left) => { + let (alias, aliased) = alias_if_required(&left, "ref$"); + if aliased { + self.vars.push(VarDeclarator { + span: DUMMY_SP, + name: Pat::Ident(alias.clone()), + init: None, + definite: false, + }); + } + + let var_expr = if aliased { + Expr::Assign(AssignExpr { + span: DUMMY_SP, + op: op!("="), + left: PatOrExpr::Pat(Box::new(Pat::Ident(alias.clone()))), + right: left.take(), + }) + } else { + Expr::Ident(alias.clone()) + }; + + return Expr::Assign(AssignExpr { + span: assign.span, + op: op!("="), + left: PatOrExpr::Pat(Box::new(Pat::Ident(alias.clone()))), + right: Box::new(make_cond( + assign.span, + &alias, + var_expr, + assign.right.take(), + )), + }); + } + PatOrExpr::Pat(left) => match &mut **left { + Pat::Ident(i) => { + return Expr::Assign(AssignExpr { + span: assign.span, + op: op!("="), + left: PatOrExpr::Pat(Box::new(Pat::Ident(i.clone()))), + right: Box::new(make_cond( + assign.span, + &i, + Expr::Ident(i.clone()), + assign.right.take(), + )), + }); + } + _ => {} + }, + } } _ => {} @@ -147,6 +183,14 @@ impl Visit for ShouldWork { e.visit_children_with(self) } } + + fn visit_assign_expr(&mut self, e: &AssignExpr, _: &dyn Node) { + if e.op == op!("??=") { + self.found = true; + } else { + e.visit_children_with(self) + } + } } impl Check for ShouldWork { @@ -154,3 +198,27 @@ impl Check for ShouldWork { self.found } } + +fn make_cond(span: Span, alias: &Ident, var_expr: Expr, init: Box) -> Expr { + Expr::Cond(CondExpr { + span, + test: Box::new(Expr::Bin(BinExpr { + span: DUMMY_SP, + left: Box::new(Expr::Bin(BinExpr { + span: DUMMY_SP, + left: Box::new(var_expr), + op: op!("!=="), + right: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))), + })), + op: op!("&&"), + right: Box::new(Expr::Bin(BinExpr { + span: DUMMY_SP, + left: Box::new(Expr::Ident(alias.clone())), + op: op!("!=="), + right: undefined(DUMMY_SP), + })), + })), + cons: Box::new(Expr::Ident(alias.clone())), + alt: init, + }) +} diff --git a/ecmascript/transforms/src/compat/es2020/nullish_coalescing/tests.rs b/ecmascript/transforms/src/compat/es2020/nullish_coalescing/tests.rs index 226c7895039b..206246c161ad 100644 --- a/ecmascript/transforms/src/compat/es2020/nullish_coalescing/tests.rs +++ b/ecmascript/transforms/src/compat/es2020/nullish_coalescing/tests.rs @@ -159,3 +159,15 @@ test!( console.log(submissions); "# ); + +test!( + Default::default(), + |_| tr(()), + assign_01, + " + a ??= b; + ", + " + a = a !== null && a !== void 0 ? a : b; + " +); diff --git a/ecmascript/transforms/src/hygiene.rs b/ecmascript/transforms/src/hygiene.rs index 193a3b7bca05..7c42e9247e7a 100644 --- a/ecmascript/transforms/src/hygiene.rs +++ b/ecmascript/transforms/src/hygiene.rs @@ -3,7 +3,7 @@ use crate::{ compat::es2015::classes::native::{is_native, is_native_word}, scope::{IdentType, ScopeKind}, }; -use fxhash::FxHashMap; +use fxhash::{FxHashMap, FxHashSet}; use smallvec::{smallvec, SmallVec}; use std::cell::RefCell; use swc_atoms::JsWord; @@ -116,36 +116,39 @@ impl<'a> Hygiene<'a> { let sym = self.current.change_symbol(sym, ctxt); let boxed_sym = sym.to_boxed_str(); - let scope = self.current.scope_of(&boxed_sym, ctxt); - - // Update symbol list - let mut declared_symbols = scope.declared_symbols.borrow_mut(); - - let is_not_renamed = !scope.ops.borrow().rename.contains_key(&(sym.clone(), ctxt)); - - debug_assert!( - is_not_renamed, - "failed to rename {}{:?}: should not rename an ident multiple time\n{:?}", - sym, - ctxt, - scope.ops.borrow(), - ); - - let old = declared_symbols.entry(sym.to_boxed_str()).or_default(); - old.retain(|c| *c != ctxt); - // debug_assert!(old.is_empty() || old.len() == 1); - - let new = declared_symbols - .entry(renamed.clone().into_boxed_str()) - .or_insert_with(|| Vec::with_capacity(2)); - new.push(ctxt); - debug_assert!(new.len() == 1); - - scope - .ops - .borrow_mut() - .rename - .insert((sym, ctxt), renamed.into()); + { + let scope = self.current.scope_of(&boxed_sym, ctxt); + + // Update symbol list + let mut declared_symbols = scope.declared_symbols.borrow_mut(); + + let is_not_renamed = !scope.ops.borrow().rename.contains_key(&(sym.clone(), ctxt)); + + debug_assert!( + is_not_renamed, + "failed to rename {}{:?}: should not rename an ident multiple time\n{:?}", + sym, + ctxt, + scope.ops.borrow(), + ); + + let old = declared_symbols.entry(sym.to_boxed_str()).or_default(); + old.retain(|c| *c != ctxt); + // debug_assert!(old.is_empty() || old.len() == 1); + + let new = declared_symbols + .entry(renamed.clone().into_boxed_str()) + .or_insert_with(|| Vec::with_capacity(2)); + new.push(ctxt); + debug_assert!(new.len() == 1); + + scope + .ops + .borrow_mut() + .rename + .insert((sym, ctxt), renamed.clone().into()); + } + self.current.renamed.insert(renamed.into()); } } @@ -224,6 +227,7 @@ struct Scope<'a> { pub declared_symbols: RefCell, Vec>>, pub(crate) ops: RefCell, + pub renamed: FxHashSet, } impl<'a> Default for Scope<'a> { @@ -240,6 +244,7 @@ impl<'a> Scope<'a> { declared_symbols: Default::default(), // children: Default::default(), ops: Default::default(), + renamed: Default::default(), } } @@ -270,6 +275,10 @@ impl<'a> Scope<'a> { return false; } + if self.renamed.contains(&(&**sym).into()) { + return false; + } + if let Some(ctxts) = self.declared_symbols.borrow().get(sym) { ctxts.contains(&ctxt) } else { diff --git a/ecmascript/transforms/src/resolver/tests.rs b/ecmascript/transforms/src/resolver/tests.rs index 5759a57e73fc..e60c2fb10284 100644 --- a/ecmascript/transforms/src/resolver/tests.rs +++ b/ecmascript/transforms/src/resolver/tests.rs @@ -1827,3 +1827,26 @@ to_ts!( } " ); + +to!( + deno_issue_8620_1, + " + const b = 1; + const b1 = 2; + { + const b = 3; + const b1 = 4; + const b2 = 5; + } + ", + " + var b = 1; + var b1 = 2; + { + var b2 = 3; + var b11 = 4; + var b21 = 5; + } + + " +);