Skip to content

Commit

Permalink
✨ feat: ES2015 transform spread v1
Browse files Browse the repository at this point in the history
  • Loading branch information
caoccao committed May 22, 2024
1 parent 556ca31 commit 618f5ca
Show file tree
Hide file tree
Showing 7 changed files with 240 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<ISwc4jAst> eval() {
switch (callee.getType()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ISwc4jAst> eval() {
ISwc4jAstExpr obj = this.obj.unParenExpr();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Swc4jAstExprOrSpread> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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<String, String> 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;
}
}
Original file line number Diff line number Diff line change
@@ -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);"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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<String, String> testCaseMap = new LinkedHashMap<>();
Expand Down Expand Up @@ -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<String, String> entry : testCaseMap.entrySet()) {
Swc4jTransformOutput output = swc4j.transform(entry.getKey(), jsScriptTransformOptions);
assertEquals(entry.getValue(), output.getCode(), entry.getKey());
Expand Down

0 comments on commit 618f5ca

Please sign in to comment.