From 74958f4b2df59ceb1c5833c181df9b60fde1bb8a Mon Sep 17 00:00:00 2001 From: Sam Cao Date: Thu, 16 May 2024 13:25:23 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20Add=20jsfuck=20decoder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../swc4j/ast/expr/Swc4jAstExprOrSpread.java | 7 + .../swc4j/ast/expr/lit/Swc4jAstArrayLit.java | 62 ++++++- .../swc4j/ast/expr/lit/Swc4jAstBool.java | 4 +- .../swc4j/ast/expr/lit/Swc4jAstNumber.java | 6 +- .../javet/swc4j/ast/expr/lit/Swc4jAstStr.java | 53 +++++- .../javet/swc4j/ast/interfaces/ISwc4jAst.java | 2 +- ...n.java => ISwc4jAstCoercionPrimitive.java} | 2 +- .../swc4j/ast/interfaces/ISwc4jAstExpr.java | 8 + .../jsfuck/Swc4jPluginHostJsFuckDecoder.java | 32 ++++ .../Swc4jPluginVisitorJsFuckDecoder.java | 170 ++++++++++++++++++ .../ast/expr/lit/TestSwc4jAstNumber.java | 10 ++ .../TestSwc4jPluginHostJsFuckDecoder.java | 49 +++++ .../swc4j/tutorials/Tutorial07Decode.java | 130 ++------------ 13 files changed, 409 insertions(+), 126 deletions(-) rename src/main/java/com/caoccao/javet/swc4j/ast/interfaces/{ISwc4jAstPrimitiveCoercion.java => ISwc4jAstCoercionPrimitive.java} (94%) create mode 100644 src/main/java/com/caoccao/javet/swc4j/plugins/jsfuck/Swc4jPluginHostJsFuckDecoder.java create mode 100644 src/main/java/com/caoccao/javet/swc4j/plugins/jsfuck/Swc4jPluginVisitorJsFuckDecoder.java create mode 100644 src/test/java/com/caoccao/javet/swc4j/plugins/jsfuck/TestSwc4jPluginHostJsFuckDecoder.java diff --git a/src/main/java/com/caoccao/javet/swc4j/ast/expr/Swc4jAstExprOrSpread.java b/src/main/java/com/caoccao/javet/swc4j/ast/expr/Swc4jAstExprOrSpread.java index 1a10cf10..13b54f89 100644 --- a/src/main/java/com/caoccao/javet/swc4j/ast/expr/Swc4jAstExprOrSpread.java +++ b/src/main/java/com/caoccao/javet/swc4j/ast/expr/Swc4jAstExprOrSpread.java @@ -87,6 +87,13 @@ public Swc4jAstExprOrSpread setSpread(Swc4jSpan spread) { return this; } + @Override + public String toString() { + String str = spread.map(span -> "...").orElse(""); + str += expr.toString(); + return str; + } + @Override public Swc4jAstVisitorResponse visit(ISwc4jAstVisitor visitor) { switch (visitor.visitExprOrSpread(this)) { 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 60477589..edd975ce 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 @@ -20,6 +20,7 @@ import com.caoccao.javet.swc4j.ast.enums.Swc4jAstType; import com.caoccao.javet.swc4j.ast.expr.Swc4jAstExprOrSpread; import com.caoccao.javet.swc4j.ast.interfaces.ISwc4jAst; +import com.caoccao.javet.swc4j.ast.interfaces.ISwc4jAstCoercionPrimitive; import com.caoccao.javet.swc4j.ast.interfaces.ISwc4jAstExpr; import com.caoccao.javet.swc4j.ast.visitors.ISwc4jAstVisitor; import com.caoccao.javet.swc4j.ast.visitors.Swc4jAstVisitorResponse; @@ -37,7 +38,7 @@ @Jni2RustClass(filePath = Jni2RustFilePath.AstUtils) public class Swc4jAstArrayLit extends Swc4jAst - implements ISwc4jAstExpr { + implements ISwc4jAstExpr, ISwc4jAstCoercionPrimitive { protected final List> elems; @Jni2RustMethod @@ -51,6 +52,56 @@ public Swc4jAstArrayLit( this.elems.stream().filter(Optional::isPresent).map(Optional::get).forEach(node -> node.setParent(this)); } + @Override + public boolean asBoolean() { + return false; + } + + @Override + public byte asByte() { + return Double.valueOf(asDouble()).byteValue(); + } + + @Override + public double asDouble() { + if (!elems.isEmpty()) { + if (elems.size() == 1) { + return elems.get(0) + .map(Swc4jAstExprOrSpread::getExpr) + .filter(n -> n.getType() == Swc4jAstType.Number) + .map(n -> (Swc4jAstNumber) n) + .map(Swc4jAstNumber::getValue) + .orElse(Double.NaN); + } + } + return 0; + } + + @Override + public float asFloat() { + return Double.valueOf(asDouble()).floatValue(); + } + + @Override + public int asInt() { + return Double.valueOf(asDouble()).intValue(); + } + + @Override + public long asLong() { + return Double.valueOf(asDouble()).longValue(); + } + + @Override + public short asShort() { + return Double.valueOf(asDouble()).shortValue(); + } + + @Override + public String asString() { + return toString(); + } + @Override public List getChildNodes() { List childNodes = SimpleList.of(); @@ -88,6 +139,15 @@ public boolean replaceNode(ISwc4jAst oldNode, ISwc4jAst newNode) { return false; } + @Override + public String toString() { + return elems.stream() + .map(optionalElem -> optionalElem + .map(Swc4jAstExprOrSpread::toString) + .orElse("")) + .collect(Collectors.joining(",")); + } + @Override public Swc4jAstVisitorResponse visit(ISwc4jAstVisitor visitor) { switch (visitor.visitArrayLit(this)) { diff --git a/src/main/java/com/caoccao/javet/swc4j/ast/expr/lit/Swc4jAstBool.java b/src/main/java/com/caoccao/javet/swc4j/ast/expr/lit/Swc4jAstBool.java index 47e0da3f..7c14db3d 100644 --- a/src/main/java/com/caoccao/javet/swc4j/ast/expr/lit/Swc4jAstBool.java +++ b/src/main/java/com/caoccao/javet/swc4j/ast/expr/lit/Swc4jAstBool.java @@ -20,7 +20,7 @@ import com.caoccao.javet.swc4j.ast.enums.Swc4jAstType; import com.caoccao.javet.swc4j.ast.interfaces.ISwc4jAst; import com.caoccao.javet.swc4j.ast.interfaces.ISwc4jAstLit; -import com.caoccao.javet.swc4j.ast.interfaces.ISwc4jAstPrimitiveCoercion; +import com.caoccao.javet.swc4j.ast.interfaces.ISwc4jAstCoercionPrimitive; import com.caoccao.javet.swc4j.ast.interfaces.ISwc4jAstTsLit; import com.caoccao.javet.swc4j.ast.visitors.ISwc4jAstVisitor; import com.caoccao.javet.swc4j.ast.visitors.Swc4jAstVisitorResponse; @@ -34,7 +34,7 @@ @Jni2RustClass(filePath = Jni2RustFilePath.AstUtils) public class Swc4jAstBool extends Swc4jAst - implements ISwc4jAstLit, ISwc4jAstTsLit, ISwc4jAstPrimitiveCoercion { + implements ISwc4jAstLit, ISwc4jAstTsLit, ISwc4jAstCoercionPrimitive { protected boolean value; @Jni2RustMethod diff --git a/src/main/java/com/caoccao/javet/swc4j/ast/expr/lit/Swc4jAstNumber.java b/src/main/java/com/caoccao/javet/swc4j/ast/expr/lit/Swc4jAstNumber.java index 6c0a7432..bdb8ce77 100644 --- a/src/main/java/com/caoccao/javet/swc4j/ast/expr/lit/Swc4jAstNumber.java +++ b/src/main/java/com/caoccao/javet/swc4j/ast/expr/lit/Swc4jAstNumber.java @@ -30,7 +30,7 @@ @Jni2RustClass(filePath = Jni2RustFilePath.AstUtils) public class Swc4jAstNumber extends Swc4jAst - implements ISwc4jAstLit, ISwc4jAstPropName, ISwc4jAstTsLit, ISwc4jAstPrimitiveCoercion { + implements ISwc4jAstLit, ISwc4jAstPropName, ISwc4jAstTsLit, ISwc4jAstCoercionPrimitive { @Jni2RustField(componentAtom = true) protected Optional raw; protected double value; @@ -53,6 +53,10 @@ public static Swc4jAstNumber create(double value) { return new Swc4jAstNumber(value, Double.toString(value), Swc4jSpan.DUMMY); } + public static Swc4jAstNumber create(double value, String raw) { + return new Swc4jAstNumber(value, raw, Swc4jSpan.DUMMY); + } + @Override public boolean asBoolean() { return value != 0; diff --git a/src/main/java/com/caoccao/javet/swc4j/ast/expr/lit/Swc4jAstStr.java b/src/main/java/com/caoccao/javet/swc4j/ast/expr/lit/Swc4jAstStr.java index 96f15de1..525993f9 100644 --- a/src/main/java/com/caoccao/javet/swc4j/ast/expr/lit/Swc4jAstStr.java +++ b/src/main/java/com/caoccao/javet/swc4j/ast/expr/lit/Swc4jAstStr.java @@ -24,6 +24,7 @@ import com.caoccao.javet.swc4j.jni2rust.*; import com.caoccao.javet.swc4j.span.Swc4jSpan; import com.caoccao.javet.swc4j.utils.AssertionUtils; +import com.caoccao.javet.swc4j.utils.StringUtils; import java.util.List; import java.util.Optional; @@ -32,7 +33,7 @@ public class Swc4jAstStr extends Swc4jAst implements ISwc4jAstLit, ISwc4jAstModuleExportName, ISwc4jAstPropName, ISwc4jAstTsModuleName, ISwc4jAstTsLit, - ISwc4jAstTsEnumMemberId { + ISwc4jAstTsEnumMemberId, ISwc4jAstCoercionPrimitive { @Jni2RustField(componentAtom = true) protected Optional raw; @Jni2RustField(atom = true) @@ -48,6 +49,54 @@ public Swc4jAstStr( setValue(value); } + public static Swc4jAstStr create(String value) { + return new Swc4jAstStr(value, "\"" + value + "\"", Swc4jSpan.DUMMY); + } + + @Override + public boolean asBoolean() { + return StringUtils.isNotEmpty(value); + } + + @Override + public byte asByte() { + return Double.valueOf(asDouble()).byteValue(); + } + + @Override + public double asDouble() { + try { + return Double.parseDouble(value); + } catch (Throwable t) { + return Double.NaN; + } + } + + @Override + public float asFloat() { + return Double.valueOf(asDouble()).floatValue(); + } + + @Override + public int asInt() { + return Double.valueOf(asDouble()).intValue(); + } + + @Override + public long asLong() { + return Double.valueOf(asDouble()).longValue(); + } + + @Override + public short asShort() { + return Double.valueOf(asDouble()).shortValue(); + } + + @Override + public String asString() { + return toString(); + } + @Override public List getChildNodes() { return EMPTY_CHILD_NODES; @@ -85,7 +134,7 @@ public Swc4jAstStr setValue(String value) { @Override public String toString() { - return raw.orElse(null); + return value; } @Override diff --git a/src/main/java/com/caoccao/javet/swc4j/ast/interfaces/ISwc4jAst.java b/src/main/java/com/caoccao/javet/swc4j/ast/interfaces/ISwc4jAst.java index b8a7d844..4685881e 100644 --- a/src/main/java/com/caoccao/javet/swc4j/ast/interfaces/ISwc4jAst.java +++ b/src/main/java/com/caoccao/javet/swc4j/ast/interfaces/ISwc4jAst.java @@ -40,7 +40,7 @@ public interface ISwc4jAst { * @since 0.4.0 */ @SuppressWarnings("unchecked") - default T as(Class clazz) { + default T as(Class clazz) { return clazz.isAssignableFrom(getClass()) ? (T) this : null; } diff --git a/src/main/java/com/caoccao/javet/swc4j/ast/interfaces/ISwc4jAstPrimitiveCoercion.java b/src/main/java/com/caoccao/javet/swc4j/ast/interfaces/ISwc4jAstCoercionPrimitive.java similarity index 94% rename from src/main/java/com/caoccao/javet/swc4j/ast/interfaces/ISwc4jAstPrimitiveCoercion.java rename to src/main/java/com/caoccao/javet/swc4j/ast/interfaces/ISwc4jAstCoercionPrimitive.java index 23527bf9..c3adc5e0 100644 --- a/src/main/java/com/caoccao/javet/swc4j/ast/interfaces/ISwc4jAstPrimitiveCoercion.java +++ b/src/main/java/com/caoccao/javet/swc4j/ast/interfaces/ISwc4jAstCoercionPrimitive.java @@ -16,7 +16,7 @@ package com.caoccao.javet.swc4j.ast.interfaces; -public interface ISwc4jAstPrimitiveCoercion { +public interface ISwc4jAstCoercionPrimitive { boolean asBoolean(); byte asByte(); diff --git a/src/main/java/com/caoccao/javet/swc4j/ast/interfaces/ISwc4jAstExpr.java b/src/main/java/com/caoccao/javet/swc4j/ast/interfaces/ISwc4jAstExpr.java index 298a64c3..1b9be8e2 100644 --- a/src/main/java/com/caoccao/javet/swc4j/ast/interfaces/ISwc4jAstExpr.java +++ b/src/main/java/com/caoccao/javet/swc4j/ast/interfaces/ISwc4jAstExpr.java @@ -17,6 +17,7 @@ package com.caoccao.javet.swc4j.ast.interfaces; import com.caoccao.javet.swc4j.ast.clazz.Swc4jAstPrivateName; +import com.caoccao.javet.swc4j.ast.enums.Swc4jAstType; import com.caoccao.javet.swc4j.ast.expr.*; import com.caoccao.javet.swc4j.ast.expr.lit.Swc4jAstArrayLit; import com.caoccao.javet.swc4j.ast.expr.lit.Swc4jAstObjectLit; @@ -69,4 +70,11 @@ public interface ISwc4jAstExpr extends ISwc4jAstVarDeclOrExpr, ISwc4jAstPat, ISwc4jAstJsxExpr, ISwc4jAstCallee, ISwc4jAstBlockStmtOrExpr, ISwc4jAstAssignTarget { + default ISwc4jAstExpr unParenExpr() { + ISwc4jAstExpr expr = this; + while (expr.getType() == Swc4jAstType.ParenExpr) { + expr = expr.as(Swc4jAstParenExpr.class).getExpr(); + } + return expr; + } } diff --git a/src/main/java/com/caoccao/javet/swc4j/plugins/jsfuck/Swc4jPluginHostJsFuckDecoder.java b/src/main/java/com/caoccao/javet/swc4j/plugins/jsfuck/Swc4jPluginHostJsFuckDecoder.java new file mode 100644 index 00000000..6febd2ba --- /dev/null +++ b/src/main/java/com/caoccao/javet/swc4j/plugins/jsfuck/Swc4jPluginHostJsFuckDecoder.java @@ -0,0 +1,32 @@ +/* + * 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.jsfuck; + +import com.caoccao.javet.swc4j.ast.interfaces.ISwc4jAstProgram; +import com.caoccao.javet.swc4j.plugins.ISwc4jPluginHost; + +public class Swc4jPluginHostJsFuckDecoder implements ISwc4jPluginHost { + @Override + public boolean process(ISwc4jAstProgram program) { + Swc4jPluginVisitorJsFuckDecoder jsFuckDecoder = new Swc4jPluginVisitorJsFuckDecoder(); + do { + jsFuckDecoder.reset(); + program.visit(jsFuckDecoder); + } while (jsFuckDecoder.getCount() > 0); + return true; + } +} diff --git a/src/main/java/com/caoccao/javet/swc4j/plugins/jsfuck/Swc4jPluginVisitorJsFuckDecoder.java b/src/main/java/com/caoccao/javet/swc4j/plugins/jsfuck/Swc4jPluginVisitorJsFuckDecoder.java new file mode 100644 index 00000000..30f02e7b --- /dev/null +++ b/src/main/java/com/caoccao/javet/swc4j/plugins/jsfuck/Swc4jPluginVisitorJsFuckDecoder.java @@ -0,0 +1,170 @@ +/* + * 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.jsfuck; + +import com.caoccao.javet.swc4j.ast.clazz.Swc4jAstComputedPropName; +import com.caoccao.javet.swc4j.ast.enums.Swc4jAstType; +import com.caoccao.javet.swc4j.ast.expr.Swc4jAstBinExpr; +import com.caoccao.javet.swc4j.ast.expr.Swc4jAstMemberExpr; +import com.caoccao.javet.swc4j.ast.expr.Swc4jAstUnaryExpr; +import com.caoccao.javet.swc4j.ast.expr.lit.Swc4jAstBool; +import com.caoccao.javet.swc4j.ast.expr.lit.Swc4jAstNumber; +import com.caoccao.javet.swc4j.ast.expr.lit.Swc4jAstStr; +import com.caoccao.javet.swc4j.ast.interfaces.ISwc4jAst; +import com.caoccao.javet.swc4j.ast.interfaces.ISwc4jAstCoercionPrimitive; +import com.caoccao.javet.swc4j.ast.interfaces.ISwc4jAstExpr; +import com.caoccao.javet.swc4j.ast.interfaces.ISwc4jAstMemberProp; +import com.caoccao.javet.swc4j.ast.visitors.Swc4jAstVisitor; +import com.caoccao.javet.swc4j.ast.visitors.Swc4jAstVisitorResponse; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Swc4jPluginVisitorJsFuckDecoder extends Swc4jAstVisitor { + protected static final Pattern PATTERN_SCIENTIFIC_NOTATION = + Pattern.compile("^(\\d+)e(\\d+)$", Pattern.CASE_INSENSITIVE); + protected int count; + + public Swc4jPluginVisitorJsFuckDecoder() { + count = 0; + } + + public int getCount() { + return count; + } + + public void reset() { + count = 0; + } + + @Override + public Swc4jAstVisitorResponse visitBinExpr(Swc4jAstBinExpr node) { + ISwc4jAst newNode = null; + ISwc4jAstExpr left = node.getLeft().unParenExpr(); + ISwc4jAstExpr right = node.getRight().unParenExpr(); + Swc4jAstType leftType = left.getType(); + Swc4jAstType rightType = right.getType(); + switch (node.getOp()) { + case Add: + if ((leftType == Swc4jAstType.Bool || leftType == Swc4jAstType.Number) && + (rightType == Swc4jAstType.Bool || rightType == Swc4jAstType.Number)) { + int value = left.as(ISwc4jAstCoercionPrimitive.class).asInt() + right.as(ISwc4jAstCoercionPrimitive.class).asInt(); + newNode = Swc4jAstNumber.create(value); + } else if ((leftType == Swc4jAstType.Bool && rightType == Swc4jAstType.ArrayLit) || + (leftType == Swc4jAstType.ArrayLit && rightType == Swc4jAstType.Bool) || + (leftType == Swc4jAstType.ArrayLit && rightType == Swc4jAstType.ArrayLit) || + (leftType == Swc4jAstType.Str && rightType == Swc4jAstType.ArrayLit) || + (leftType == Swc4jAstType.ArrayLit && rightType == Swc4jAstType.Str) || + (leftType == Swc4jAstType.Number && rightType == Swc4jAstType.ArrayLit) || + (leftType == Swc4jAstType.ArrayLit && rightType == Swc4jAstType.Number) || + (leftType == Swc4jAstType.Str && rightType == Swc4jAstType.Str) || + (leftType == Swc4jAstType.Number && rightType == Swc4jAstType.Str) || + (leftType == Swc4jAstType.Str && rightType == Swc4jAstType.Number)) { + String value = left.as(ISwc4jAstCoercionPrimitive.class).asString() + right.as(ISwc4jAstCoercionPrimitive.class).asString(); + newNode = Swc4jAstStr.create(value); + } + break; + default: + break; + } + if (newNode != null) { + ++count; + node.getParent().replaceNode(node, newNode); + } + return super.visitBinExpr(node); + } + + @Override + public Swc4jAstVisitorResponse visitMemberExpr(Swc4jAstMemberExpr node) { + ISwc4jAstExpr obj = node.getObj().unParenExpr(); + ISwc4jAstMemberProp prop = node.getProp(); + if (obj.getType() == Swc4jAstType.Str) { + if (prop.getType() == Swc4jAstType.ComputedPropName) { + Swc4jAstComputedPropName computedPropName = prop.as(Swc4jAstComputedPropName.class); + ISwc4jAstExpr expr = computedPropName.getExpr(); + if (expr.getType() == Swc4jAstType.Number) { + String value = obj.as(Swc4jAstStr.class).getValue(); + int index = expr.as(Swc4jAstNumber.class).asInt(); + if (index >= 0 && index < value.length()) { + value = value.substring(index, index + 1); + ++count; + node.getParent().replaceNode(node, Swc4jAstStr.create(value)); + } + } + } + } + return super.visitMemberExpr(node); + } + + @Override + public Swc4jAstVisitorResponse visitUnaryExpr(Swc4jAstUnaryExpr node) { + ISwc4jAst newNode = null; + ISwc4jAstExpr arg = node.getArg().unParenExpr(); + switch (node.getOp()) { + case Bang: + switch (arg.getType()) { + case ArrayLit: + newNode = Swc4jAstBool.create(false); + break; + case Bool: + case Number: + newNode = Swc4jAstBool.create(!arg.as(ISwc4jAstCoercionPrimitive.class).asBoolean()); + break; + default: + break; + } + break; + case Plus: + switch (arg.getType()) { + case ArrayLit: + case Bool: + newNode = Swc4jAstNumber.create(arg.as(ISwc4jAstCoercionPrimitive.class).asInt()); + break; + case Number: { + Swc4jAstNumber number = arg.as(Swc4jAstNumber.class); + newNode = Swc4jAstNumber.create(number.getValue(), number.getRaw().orElse(null)); + } + break; + case Str: { + Swc4jAstStr str = arg.as(Swc4jAstStr.class); + try { + double value = Double.parseDouble(str.getValue()); + String raw = str.getValue(); + Matcher matcher = PATTERN_SCIENTIFIC_NOTATION.matcher(raw); + if (matcher.matches()) { + raw = matcher.group(1) + "e+" + matcher.group(2); + } + newNode = Swc4jAstNumber.create(value, raw); + } catch (Throwable t) { + newNode = Swc4jAstNumber.create(Double.NaN); + } + } + break; + default: + break; + } + break; + default: + break; + } + if (newNode != null) { + ++count; + node.getParent().replaceNode(node, newNode); + } + return super.visitUnaryExpr(node); + } +} diff --git a/src/test/java/com/caoccao/javet/swc4j/ast/expr/lit/TestSwc4jAstNumber.java b/src/test/java/com/caoccao/javet/swc4j/ast/expr/lit/TestSwc4jAstNumber.java index ff7d5812..da70b8b7 100644 --- a/src/test/java/com/caoccao/javet/swc4j/ast/expr/lit/TestSwc4jAstNumber.java +++ b/src/test/java/com/caoccao/javet/swc4j/ast/expr/lit/TestSwc4jAstNumber.java @@ -27,6 +27,16 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class TestSwc4jAstNumber extends BaseTestSuiteSwc4jAst { + @Test + public void testCoercion() { + assertEquals("0", Swc4jAstNumber.create(0).getRaw().get()); + assertEquals("1", Swc4jAstNumber.create(1).getRaw().get()); + assertEquals("1.1", Swc4jAstNumber.create(1.1D).getRaw().get()); + assertEquals(1, Swc4jAstNumber.create(1.1D).asInt()); + assertEquals("NaN", Swc4jAstNumber.create(Double.NaN).getRaw().get()); + assertEquals(0, Swc4jAstNumber.create(Double.NaN).asInt()); + } + @Test public void testDouble() throws Swc4jCoreException { String code = "12.34"; 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 new file mode 100644 index 00000000..6a17f692 --- /dev/null +++ b/src/test/java/com/caoccao/javet/swc4j/plugins/jsfuck/TestSwc4jPluginHostJsFuckDecoder.java @@ -0,0 +1,49 @@ +/* + * 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.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.ISwc4jPluginHost; +import org.junit.jupiter.api.Test; + +import java.util.LinkedHashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TestSwc4jPluginHostJsFuckDecoder extends BaseTestSuite { + @Test + public void test() throws Swc4jCoreException { + Map testCaseMap = new LinkedHashMap<>(); + testCaseMap.put("\"a\";", "(![]+[])[+!+[]]"); + testCaseMap.put("\"1+1\";", "[+!+[]]+(+(+!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]])+[])[!+[]+!+[]]+[+!+[]]"); + // TODO + // testCaseMap.put("\"1-1\";", "[+!+[]]+(+((+(+!+[]+[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]]+[+[]])+[])[+!+[]]+[+[]+[+[]]+[+[]]+[+[]]+[+[]]+[+[]]+[+!+[]]])+[])[!+[]+!+[]]+[+!+[]]"); + ISwc4jPluginHost pluginHost = new Swc4jPluginHostJsFuckDecoder(); + jsScriptTransformOptions + .setPluginHost(pluginHost) + .setInlineSources(false) + .setSourceMap(Swc4jSourceMapOption.None); + for (Map.Entry entry : testCaseMap.entrySet()) { + Swc4jTransformOutput output = swc4j.transform(entry.getValue(), jsScriptTransformOptions); + assertEquals(entry.getKey(), output.getCode()); + } + } +} diff --git a/src/test/java/com/caoccao/javet/swc4j/tutorials/Tutorial07Decode.java b/src/test/java/com/caoccao/javet/swc4j/tutorials/Tutorial07Decode.java index 96a6812d..4e575ab7 100644 --- a/src/test/java/com/caoccao/javet/swc4j/tutorials/Tutorial07Decode.java +++ b/src/test/java/com/caoccao/javet/swc4j/tutorials/Tutorial07Decode.java @@ -16,38 +16,31 @@ package com.caoccao.javet.swc4j.tutorials; +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.interop.V8Host; +import com.caoccao.javet.interop.V8Runtime; import com.caoccao.javet.swc4j.Swc4j; -import com.caoccao.javet.swc4j.ast.enums.Swc4jAstType; -import com.caoccao.javet.swc4j.ast.expr.Swc4jAstBinExpr; -import com.caoccao.javet.swc4j.ast.expr.Swc4jAstUnaryExpr; -import com.caoccao.javet.swc4j.ast.expr.lit.Swc4jAstArrayLit; -import com.caoccao.javet.swc4j.ast.expr.lit.Swc4jAstBool; -import com.caoccao.javet.swc4j.ast.expr.lit.Swc4jAstNumber; -import com.caoccao.javet.swc4j.ast.interfaces.ISwc4jAst; -import com.caoccao.javet.swc4j.ast.interfaces.ISwc4jAstExpr; -import com.caoccao.javet.swc4j.ast.interfaces.ISwc4jAstProgram; -import com.caoccao.javet.swc4j.ast.visitors.Swc4jAstVisitor; -import com.caoccao.javet.swc4j.ast.visitors.Swc4jAstVisitorResponse; import com.caoccao.javet.swc4j.enums.Swc4jMediaType; import com.caoccao.javet.swc4j.enums.Swc4jSourceMapOption; import com.caoccao.javet.swc4j.exceptions.Swc4jCoreException; import com.caoccao.javet.swc4j.options.Swc4jTransformOptions; import com.caoccao.javet.swc4j.outputs.Swc4jTransformOutput; import com.caoccao.javet.swc4j.plugins.ISwc4jPluginHost; +import com.caoccao.javet.swc4j.plugins.jsfuck.Swc4jPluginHostJsFuckDecoder; import java.net.MalformedURLException; import java.net.URL; public class Tutorial07Decode { - public static void main(String[] args) throws Swc4jCoreException, MalformedURLException { + public static void main(String[] args) throws Swc4jCoreException, MalformedURLException, JavetException { // Create an instance of swc4j. Swc4j swc4j = new Swc4j(); // Prepare a JavaScript code snippet. - String code = "[+!+[]]+(+(+!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]])+[])[!+[]+!+[]]+[+!+[]]"; + String code = "[+!+[]]+(+(+!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]])+[])[!+[]+!+[]]+[+!+[]]"; // 1+1 // Prepare a script name. URL specifier = new URL("file:///abc.ts"); // Create a plugin host. - ISwc4jPluginHost pluginHost = new DecoderPluginHost(); + ISwc4jPluginHost pluginHost = new Swc4jPluginHostJsFuckDecoder(); Swc4jTransformOptions options = new Swc4jTransformOptions() .setSpecifier(specifier) .setPluginHost(pluginHost) @@ -60,110 +53,11 @@ public static void main(String[] args) throws Swc4jCoreException, MalformedURLEx System.out.println(" The transformed code is as follows."); System.out.println("*********************************************/"); System.out.println(output.getCode()); - } - - static class DecoderPluginHost implements ISwc4jPluginHost { - @Override - public boolean process(ISwc4jAstProgram program) { - JsFuckVisitor jsFuckVisitor = new JsFuckVisitor(); - do { - jsFuckVisitor.reset(); - program.visit(jsFuckVisitor); - } while (jsFuckVisitor.getCount() > 0); - return true; - } - } - - static class JsFuckVisitor extends Swc4jAstVisitor { - protected int count; - - public JsFuckVisitor() { - count = 0; - } - - public int getCount() { - return count; - } - - public void reset() { - count = 0; - } - - @Override - public Swc4jAstVisitorResponse visitBinExpr(Swc4jAstBinExpr node) { - ISwc4jAst newNode = null; - ISwc4jAstExpr left = node.getLeft(); - ISwc4jAstExpr right = node.getRight(); - switch (node.getOp()) { - case Add: - if (left.getType() == Swc4jAstType.Bool && right.getType() == Swc4jAstType.Bool) { - int value = ((Swc4jAstBool) left).asInt() + ((Swc4jAstBool) right).asInt(); - newNode = Swc4jAstNumber.create(value); - } else if (left.getType() == Swc4jAstType.Bool && right.getType() == Swc4jAstType.Number) { - int value = ((Swc4jAstBool) left).asInt() + ((Swc4jAstNumber) right).asInt(); - newNode = Swc4jAstNumber.create(value); - } else if (left.getType() == Swc4jAstType.Number && right.getType() == Swc4jAstType.Bool) { - int value = ((Swc4jAstNumber) left).asInt() + ((Swc4jAstBool) right).asInt(); - newNode = Swc4jAstNumber.create(value); - } - break; - default: - break; - } - if (newNode != null) { - ++count; - node.getParent().replaceNode(node, newNode); - } - return super.visitBinExpr(node); - } - - @Override - public Swc4jAstVisitorResponse visitUnaryExpr(Swc4jAstUnaryExpr node) { - ISwc4jAst newNode = null; - ISwc4jAstExpr arg = node.getArg(); - switch (node.getOp()) { - case Bang: - switch (arg.getType()) { - case ArrayLit: - newNode = Swc4jAstBool.create(false); - break; - case Number: - newNode = Swc4jAstBool.create(!((Swc4jAstNumber) arg).asBoolean()); - break; - default: - break; - } - break; - case Plus: - switch (arg.getType()) { - case ArrayLit: - Swc4jAstArrayLit arrayLit = (Swc4jAstArrayLit) arg; - if (arrayLit.getElems().isEmpty()) { - newNode = Swc4jAstNumber.create(0); - } - break; - case Number: { - int value = ((Swc4jAstNumber) arg).asInt(); - newNode = Swc4jAstNumber.create(value); - } - break; - case Bool: { - int value = ((Swc4jAstBool) arg).asInt(); - newNode = Swc4jAstNumber.create(value); - } - break; - default: - break; - } - break; - default: - break; - } - if (newNode != null) { - ++count; - node.getParent().replaceNode(node, newNode); - } - return super.visitUnaryExpr(node); + System.out.println("/*********************************************"); + System.out.println(" The evaluated result in V8."); + System.out.println("*********************************************/"); + try (V8Runtime v8Runtime = V8Host.getV8Instance().createV8Runtime()) { + System.out.println(v8Runtime.getExecutor(output.getCode()).executeString()); } } }