diff --git a/src/main/java/com/caoccao/javet/swc4j/ast/expr/Swc4jAstCallExpr.java b/src/main/java/com/caoccao/javet/swc4j/ast/expr/Swc4jAstCallExpr.java index 40f828be..6b56d207 100644 --- a/src/main/java/com/caoccao/javet/swc4j/ast/expr/Swc4jAstCallExpr.java +++ b/src/main/java/com/caoccao/javet/swc4j/ast/expr/Swc4jAstCallExpr.java @@ -70,6 +70,10 @@ public Swc4jAstCallExpr( this.args.forEach(node -> node.setParent(this)); } + public static Swc4jAstCallExpr create(ISwc4jAstCallee callee) { + return new Swc4jAstCallExpr(callee, SimpleList.of(), null, Swc4jSpan.DUMMY); + } + @Override public Optional eval() { switch (callee.getType()) { diff --git a/src/main/java/com/caoccao/javet/swc4j/ast/expr/Swc4jAstMemberExpr.java b/src/main/java/com/caoccao/javet/swc4j/ast/expr/Swc4jAstMemberExpr.java index a5773e5e..63d8760a 100644 --- a/src/main/java/com/caoccao/javet/swc4j/ast/expr/Swc4jAstMemberExpr.java +++ b/src/main/java/com/caoccao/javet/swc4j/ast/expr/Swc4jAstMemberExpr.java @@ -58,6 +58,12 @@ public Swc4jAstMemberExpr( setProp(prop); } + public static Swc4jAstMemberExpr create( + ISwc4jAstExpr obj, + ISwc4jAstMemberProp prop) { + return new Swc4jAstMemberExpr(obj, prop, Swc4jSpan.DUMMY); + } + @Override public Optional eval() { ISwc4jAstExpr obj = this.obj.unParenExpr(); diff --git a/src/main/java/com/caoccao/javet/swc4j/ast/expr/lit/Swc4jAstArrayLit.java b/src/main/java/com/caoccao/javet/swc4j/ast/expr/lit/Swc4jAstArrayLit.java index 9b958916..c92b0d82 100644 --- a/src/main/java/com/caoccao/javet/swc4j/ast/expr/lit/Swc4jAstArrayLit.java +++ b/src/main/java/com/caoccao/javet/swc4j/ast/expr/lit/Swc4jAstArrayLit.java @@ -205,6 +205,18 @@ public Swc4jAstType getType() { return Swc4jAstType.ArrayLit; } + public int indexOf(Swc4jAstExprOrSpread node) { + if (!elems.isEmpty()) { + final int length = elems.size(); + for (int i = 0; i < length; i++) { + if (elems.get(i).map(elem -> elem == node).orElse(false)) { + return i; + } + } + } + return -1; + } + public boolean isAllPrimitive() { return elems.stream() .filter(Optional::isPresent) diff --git a/src/main/java/com/caoccao/javet/swc4j/plugins/es2015/Swc4jPluginVisitorEs2015TransformSpread.java b/src/main/java/com/caoccao/javet/swc4j/plugins/es2015/Swc4jPluginVisitorEs2015TransformSpread.java new file mode 100644 index 00000000..0626006f --- /dev/null +++ b/src/main/java/com/caoccao/javet/swc4j/plugins/es2015/Swc4jPluginVisitorEs2015TransformSpread.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2024. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.swc4j.plugins.es2015; + +import com.caoccao.javet.swc4j.ast.expr.Swc4jAstCallExpr; +import com.caoccao.javet.swc4j.ast.expr.Swc4jAstExprOrSpread; +import com.caoccao.javet.swc4j.ast.expr.Swc4jAstIdent; +import com.caoccao.javet.swc4j.ast.expr.Swc4jAstMemberExpr; +import com.caoccao.javet.swc4j.ast.expr.lit.Swc4jAstArrayLit; +import com.caoccao.javet.swc4j.ast.interfaces.ISwc4jAst; +import com.caoccao.javet.swc4j.ast.visitors.Swc4jAstVisitor; +import com.caoccao.javet.swc4j.ast.visitors.Swc4jAstVisitorResponse; + +import java.util.Optional; + +/** + * The type Swc4j plugin visitor ES2015 transform spread. + * + * @since 0.8.0 + */ +public class Swc4jPluginVisitorEs2015TransformSpread extends Swc4jAstVisitor { + @Override + public Swc4jAstVisitorResponse visitExprOrSpread(Swc4jAstExprOrSpread node) { + if (node.getSpread().isPresent()) { + switch (node.getParent().getType()) { + case ArrayLit: + Swc4jAstArrayLit arrayLit = node.getParent().as(Swc4jAstArrayLit.class); + if (arrayLit.getElems().size() == 1) { + arrayLit.getParent().replaceNode(arrayLit, node.getExpr()); + } else { + Swc4jAstIdent identConcat = Swc4jAstIdent.create(Swc4jAstArrayLit.CONCAT); + int index = arrayLit.indexOf(node); + if (index == 0) { + Swc4jAstMemberExpr memberExpr = Swc4jAstMemberExpr.create(node.getExpr(), identConcat); + Swc4jAstCallExpr callExpr = Swc4jAstCallExpr.create(memberExpr); + ISwc4jAst newNode = callExpr; + final int length = arrayLit.getElems().size(); + for (int i = 1; i < length; i++) { + Optional elem = arrayLit.getElems().get(i); + if (elem.isPresent()) { + if (elem.get().getSpread().isPresent()) { + callExpr.getArgs().add(Swc4jAstExprOrSpread.create(elem.get().getExpr())); + newNode = callExpr; + memberExpr = Swc4jAstMemberExpr.create(callExpr, identConcat); + callExpr = Swc4jAstCallExpr.create(memberExpr); + } else { + // TODO + } + } else { + // TODO + } + } + arrayLit.getParent().replaceNode(arrayLit, newNode); + } + } + break; + case CallExpr: + break; + case NewExpr: + break; + case OptCall: + break; + default: + break; + } + } + return super.visitExprOrSpread(node); + } +} diff --git a/src/test/java/com/caoccao/javet/swc4j/plugins/BaseTestSuiteSwc4jPlugin.java b/src/test/java/com/caoccao/javet/swc4j/plugins/BaseTestSuiteSwc4jPlugin.java new file mode 100644 index 00000000..ea2c5955 --- /dev/null +++ b/src/test/java/com/caoccao/javet/swc4j/plugins/BaseTestSuiteSwc4jPlugin.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2024. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.swc4j.plugins; + +import com.caoccao.javet.interop.V8Host; +import com.caoccao.javet.interop.V8Runtime; +import com.caoccao.javet.swc4j.BaseTestSuite; +import com.caoccao.javet.swc4j.enums.Swc4jSourceMapOption; +import com.caoccao.javet.swc4j.utils.SimpleList; +import org.junit.jupiter.api.BeforeEach; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class BaseTestSuiteSwc4jPlugin extends BaseTestSuite { + protected void assertEvalAsString(String code, String expectedCode) { + assertEquals( + evalAsString(code), + evalAsString(expectedCode), + "Failed to evaluate.\nFrom:\n" + code + "\nTo:\n" + expectedCode); + } + + protected void assertTransform(String code, String expectedCode) { + try { + assertEquals(expectedCode, swc4j.transform(code, jsScriptTransformOptions).getCode()); + } catch (Throwable t) { + fail("Failed to transform.\nFrom:\n" + code + "\nTo:\n" + expectedCode, t); + } + } + + protected void assertTransformAndEvalAsString(Map testCaseMap) { + testCaseMap.forEach((code, expectedCode) -> { + assertTransform(code, expectedCode); + assertEvalAsString(code, expectedCode); + }); + } + + @BeforeEach + @Override + protected void beforeEach() { + super.beforeEach(); + SimpleList.of(jsModuleTransformOptions, + jsProgramTransformOptions, + jsScriptTransformOptions, + jsxModuleTransformOptions, + jsxProgramTransformOptions, + jsxScriptTransformOptions, + tsModuleTransformOptions, + tsProgramTransformOptions, + tsScriptTransformOptions) + .forEach(options -> options.setSourceMap(Swc4jSourceMapOption.None)); + } + + protected String evalAsString(String code) { + try (V8Runtime v8Runtime = V8Host.getV8Instance().createV8Runtime()) { + return v8Runtime.getExecutor(code).executeString(); + } catch (Throwable t) { + fail("Failed to execute " + code, t); + } + return null; + } +} diff --git a/src/test/java/com/caoccao/javet/swc4j/plugins/es2015/TestSwc4jPluginVisitorJsFuckDecoder.java b/src/test/java/com/caoccao/javet/swc4j/plugins/es2015/TestSwc4jPluginVisitorJsFuckDecoder.java new file mode 100644 index 00000000..14e85abc --- /dev/null +++ b/src/test/java/com/caoccao/javet/swc4j/plugins/es2015/TestSwc4jPluginVisitorJsFuckDecoder.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.swc4j.plugins.es2015; + +import com.caoccao.javet.swc4j.plugins.BaseTestSuiteSwc4jPlugin; +import com.caoccao.javet.swc4j.plugins.Swc4jPluginHost; +import com.caoccao.javet.swc4j.plugins.Swc4jPluginVisitors; +import com.caoccao.javet.swc4j.utils.SimpleMap; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class TestSwc4jPluginVisitorJsFuckDecoder extends BaseTestSuiteSwc4jPlugin { + + @BeforeEach + @Override + protected void beforeEach() { + super.beforeEach(); + Swc4jPluginVisitors pluginVisitors = new Swc4jPluginVisitors() + .add(new Swc4jPluginVisitorEs2015TransformSpread()); + Swc4jPluginHost pluginHost = new Swc4jPluginHost() + .add(pluginVisitors); + jsScriptTransformOptions.setPluginHost(pluginHost); + } + + @Test + public void testArrayLitFirst() { + assertTransformAndEvalAsString(SimpleMap.of( + "const a = [1,2]; const b = [3,4]; JSON.stringify([...a, ...b]);", + "const a=[1,2];const b=[3,4];JSON.stringify(a.concat(b));", + "const a = [1,2]; const b = [3,4]; JSON.stringify([...a, ...b, ...b, ...a]);", + "const a=[1,2];const b=[3,4];JSON.stringify(a.concat(b).concat(b).concat(a));")); + } + + @Test + public void testArrayLitOne() { + assertTransformAndEvalAsString(SimpleMap.of( + "const a = [1,2]; JSON.stringify([...a]);", + "const a=[1,2];JSON.stringify(a);")); + } +} diff --git a/src/test/java/com/caoccao/javet/swc4j/plugins/jsfuck/TestSwc4jPluginHostJsFuckDecoder.java b/src/test/java/com/caoccao/javet/swc4j/plugins/jsfuck/TestSwc4jPluginHostJsFuckDecoder.java index a38e87b2..953aa073 100644 --- a/src/test/java/com/caoccao/javet/swc4j/plugins/jsfuck/TestSwc4jPluginHostJsFuckDecoder.java +++ b/src/test/java/com/caoccao/javet/swc4j/plugins/jsfuck/TestSwc4jPluginHostJsFuckDecoder.java @@ -16,10 +16,9 @@ package com.caoccao.javet.swc4j.plugins.jsfuck; -import com.caoccao.javet.swc4j.BaseTestSuite; -import com.caoccao.javet.swc4j.enums.Swc4jSourceMapOption; import com.caoccao.javet.swc4j.exceptions.Swc4jCoreException; import com.caoccao.javet.swc4j.outputs.Swc4jTransformOutput; +import com.caoccao.javet.swc4j.plugins.BaseTestSuiteSwc4jPlugin; import com.caoccao.javet.swc4j.plugins.ISwc4jPluginHost; import org.junit.jupiter.api.Test; @@ -28,7 +27,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -public class TestSwc4jPluginHostJsFuckDecoder extends BaseTestSuite { +public class TestSwc4jPluginHostJsFuckDecoder extends BaseTestSuiteSwc4jPlugin { @Test public void test() throws Swc4jCoreException { Map testCaseMap = new LinkedHashMap<>(); @@ -68,8 +67,7 @@ public void test() throws Swc4jCoreException { ISwc4jPluginHost pluginHost = new Swc4jPluginHostJsFuckDecoder(); jsScriptTransformOptions .setPluginHost(pluginHost) - .setOmitLastSemi(true) - .setSourceMap(Swc4jSourceMapOption.None); + .setOmitLastSemi(true); for (Map.Entry entry : testCaseMap.entrySet()) { Swc4jTransformOutput output = swc4j.transform(entry.getKey(), jsScriptTransformOptions); assertEquals(entry.getValue(), output.getCode(), entry.getKey());