diff --git a/scripts/ts/test/test.assignments.ts b/scripts/ts/test/test.assignments.ts index d96bb46..4dea9ca 100644 --- a/scripts/ts/test/test.assignments.ts +++ b/scripts/ts/test/test.assignments.ts @@ -14,7 +14,14 @@ class Test { let d: long = b; return c + d; } + + public assignConst(a: int, b: long): long { + const c: int = 100; + const d: long = 2; + return a + b + c + d; + } } // console.log(new Test().sum(10)); console.log(new Test().assignAndCast(1, 2)); +console.log(new Test().assignConst(1, 2)); diff --git a/src/main/java/com/caoccao/javet/buddy/ts2java/ast/Ts2JavaAstBinExpr.java b/src/main/java/com/caoccao/javet/buddy/ts2java/ast/Ts2JavaAstBinExpr.java index 3681c2c..6cc6353 100644 --- a/src/main/java/com/caoccao/javet/buddy/ts2java/ast/Ts2JavaAstBinExpr.java +++ b/src/main/java/com/caoccao/javet/buddy/ts2java/ast/Ts2JavaAstBinExpr.java @@ -24,41 +24,26 @@ import com.caoccao.javet.swc4j.ast.expr.Swc4jAstIdent; import com.caoccao.javet.swc4j.ast.interfaces.ISwc4jAstExpr; import com.caoccao.javet.utils.SimpleFreeMarkerFormat; -import com.caoccao.javet.utils.SimpleList; import com.caoccao.javet.utils.SimpleMap; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.implementation.bytecode.StackManipulation; import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess; -import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; public final class Ts2JavaAstBinExpr implements ITs2JavaAstStackManipulation { @Override public Optional manipulate(JavaFunctionContext functionContext, Swc4jAstBinExpr ast) { - List expressions = SimpleList.of(ast.getLeft(), ast.getRight()); - List localVariables = expressions.stream() - .map(expression -> { - switch (expression.getType()) { - case Ident: - String name = expression.as(Swc4jAstIdent.class).getSym(); - return functionContext.getLocalVariable(name); - default: - throw new Ts2JavaAstException( - expression, - SimpleFreeMarkerFormat.format("BinExpr expr type ${exprType} is not supported.", - SimpleMap.of("exprType", expression.getType().name()))); - } - }) - .collect(Collectors.toList()); - TypeDescription upCaseType = JavaClassCast.getUpCastTypeForMathOp( - localVariables.stream().map(JavaLocalVariable::getType).toArray(TypeDescription[]::new)); - localVariables.forEach(localVariable -> { - functionContext.addStackManipulation( - MethodVariableAccess.of(localVariable.getType()).loadFrom(localVariable.getOffset())); - JavaClassCast.upCast(localVariable.getType(), upCaseType, functionContext::addStackManipulation); - }); + final TypeDescription leftType = manipulateExpression(functionContext, ast.getLeft()); + final int stackManipulationSize = functionContext.getStackManipulations().size(); + final TypeDescription rightType = manipulateExpression(functionContext, ast.getRight()); + TypeDescription upCaseType = JavaClassCast.getUpCastTypeForMathOp(leftType, rightType); + // Insert the type cast. + JavaClassCast.getUpCastStackManipulation(leftType, upCaseType) + .ifPresent(stackManipulation -> + functionContext.getStackManipulations().add(stackManipulationSize, stackManipulation)); + JavaClassCast.getUpCastStackManipulation(rightType, upCaseType) + .ifPresent(functionContext.getStackManipulations()::add); StackManipulation stackManipulation; switch (ast.getOp()) { case Add: @@ -88,7 +73,35 @@ public Optional manipulate(JavaFunctionContext functionContext, SimpleFreeMarkerFormat.format("BinExpr op ${op} is not supported.", SimpleMap.of("op", ast.getOp().name()))); } - functionContext.addStackManipulation(stackManipulation); + functionContext.getStackManipulations().add(stackManipulation); return Optional.of(upCaseType); } + + private TypeDescription manipulateExpression(JavaFunctionContext functionContext, ISwc4jAstExpr expression) { + switch (expression.getType()) { + case BinExpr: + Optional optionalType = + new Ts2JavaAstBinExpr().manipulate(functionContext, expression.as(Swc4jAstBinExpr.class)); + if (optionalType.isPresent()) { + return optionalType.get(); + } else { + throw new Ts2JavaAstException( + expression, + SimpleFreeMarkerFormat.format("BinExpr left expr type ${exprType} is invalid.", + SimpleMap.of("exprType", expression.getType().name()))); + } + case Ident: { + String name = expression.as(Swc4jAstIdent.class).getSym(); + JavaLocalVariable localVariable = functionContext.getLocalVariable(name); + functionContext.getStackManipulations().add( + MethodVariableAccess.of(localVariable.getType()).loadFrom(localVariable.getOffset())); + return localVariable.getType(); + } + default: + throw new Ts2JavaAstException( + expression, + SimpleFreeMarkerFormat.format("BinExpr left expr type ${exprType} is not supported.", + SimpleMap.of("exprType", expression.getType().name()))); + } + } } diff --git a/src/main/java/com/caoccao/javet/buddy/ts2java/ast/Ts2JavaAstReturnStmt.java b/src/main/java/com/caoccao/javet/buddy/ts2java/ast/Ts2JavaAstReturnStmt.java index 4d87c52..e491261 100644 --- a/src/main/java/com/caoccao/javet/buddy/ts2java/ast/Ts2JavaAstReturnStmt.java +++ b/src/main/java/com/caoccao/javet/buddy/ts2java/ast/Ts2JavaAstReturnStmt.java @@ -50,9 +50,10 @@ public Optional manipulate(JavaFunctionContext functionContext, throw new Ts2JavaAstException(ast, "ReturnStmt type is unknown"); } TypeDescription returnType = functionContext.getReturnType(); - optionalFromType.ifPresent((fromType) -> - JavaClassCast.upCast(fromType, returnType, functionContext::addStackManipulation)); - functionContext.addStackManipulation(MethodReturn.of(returnType)); + optionalFromType + .flatMap(fromType -> JavaClassCast.getUpCastStackManipulation(fromType, returnType)) + .ifPresent(functionContext.getStackManipulations()::add); + functionContext.getStackManipulations().add(MethodReturn.of(returnType)); return Optional.of(returnType); } } diff --git a/src/main/java/com/caoccao/javet/buddy/ts2java/ast/Ts2JavaAstVarDecl.java b/src/main/java/com/caoccao/javet/buddy/ts2java/ast/Ts2JavaAstVarDecl.java index 9d5fe15..6d5a92b 100644 --- a/src/main/java/com/caoccao/javet/buddy/ts2java/ast/Ts2JavaAstVarDecl.java +++ b/src/main/java/com/caoccao/javet/buddy/ts2java/ast/Ts2JavaAstVarDecl.java @@ -21,6 +21,7 @@ import com.caoccao.javet.buddy.ts2java.compiler.JavaLocalVariable; import com.caoccao.javet.buddy.ts2java.exceptions.Ts2JavaAstException; import com.caoccao.javet.swc4j.ast.expr.Swc4jAstIdent; +import com.caoccao.javet.swc4j.ast.expr.lit.Swc4jAstNumber; import com.caoccao.javet.swc4j.ast.interfaces.ISwc4jAstExpr; import com.caoccao.javet.swc4j.ast.interfaces.ISwc4jAstPat; import com.caoccao.javet.swc4j.ast.pat.Swc4jAstBindingIdent; @@ -31,6 +32,10 @@ import com.caoccao.javet.utils.SimpleMap; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.implementation.bytecode.StackManipulation; +import net.bytebuddy.implementation.bytecode.constant.DoubleConstant; +import net.bytebuddy.implementation.bytecode.constant.FloatConstant; +import net.bytebuddy.implementation.bytecode.constant.IntegerConstant; +import net.bytebuddy.implementation.bytecode.constant.LongConstant; import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess; import java.util.Optional; @@ -39,46 +44,65 @@ public final class Ts2JavaAstVarDecl implements ITs2JavaAstStackManipulation manipulate(JavaFunctionContext functionContext, Swc4jAstVarDecl ast) { for (Swc4jAstVarDeclarator varDeclarator : ast.getDecls()) { - Optional optionalFromType = Optional.empty(); - if (varDeclarator.getInit().isPresent()) { - ISwc4jAstExpr expression = varDeclarator.getInit().get(); - switch (expression.getType()) { - case Ident: { - String name = expression.as(Swc4jAstIdent.class).getSym(); - JavaLocalVariable localVariable = functionContext.getLocalVariable(name); - MethodVariableAccess methodVariableAccess = MethodVariableAccess.of(localVariable.getType()); - StackManipulation stackManipulation = methodVariableAccess.loadFrom(localVariable.getOffset()); - functionContext.addStackManipulation(stackManipulation); - optionalFromType = Optional.of(localVariable.getType()); - break; - } - default: - throw new Ts2JavaAstException( - expression, - SimpleFreeMarkerFormat.format("VarDecl init type ${type} is not supported.", - SimpleMap.of("type", expression.getType().name()))); - } - } ISwc4jAstPat pat = varDeclarator.getName(); switch (pat.getType()) { case BindingIdent: { Swc4jAstBindingIdent bindingIdent = pat.as(Swc4jAstBindingIdent.class); - String name = bindingIdent.getId().getSym(); + String variableName = bindingIdent.getId().getSym(); if (bindingIdent.getTypeAnn().isPresent()) { Swc4jAstTsTypeAnn tsTypeAnn = bindingIdent.getTypeAnn().get(); - TypeDescription toType = Ts2JavaAstTsTypeAnn.getTypeDescription(tsTypeAnn); - optionalFromType.ifPresent(fromType -> - JavaClassCast.upCast(fromType, toType, functionContext::addStackManipulation)); - JavaLocalVariable localVariable = new JavaLocalVariable(name, toType); - MethodVariableAccess methodVariableAccess = MethodVariableAccess.of(toType); + TypeDescription variableType = Ts2JavaAstTsTypeAnn.getTypeDescription(tsTypeAnn); + if (varDeclarator.getInit().isPresent()) { + ISwc4jAstExpr expression = varDeclarator.getInit().get(); + switch (expression.getType()) { + case Number: { + Swc4jAstNumber value = expression.as(Swc4jAstNumber.class); + StackManipulation stackManipulation; + if (variableType.represents(int.class)) { + stackManipulation = IntegerConstant.forValue(value.asInt()); + } else if (variableType.represents(long.class)) { + stackManipulation = LongConstant.forValue(value.asLong()); + } else if (variableType.represents(float.class)) { + stackManipulation = FloatConstant.forValue(value.asFloat()); + } else if (variableType.represents(double.class)) { + stackManipulation = DoubleConstant.forValue(value.asDouble()); + } else { + throw new Ts2JavaAstException( + expression, + SimpleFreeMarkerFormat.format("VarDecl init type ${type} is not supported.", + SimpleMap.of("type", expression.getType().name()))); + } + functionContext.getStackManipulations().add(stackManipulation); + break; + } + case Ident: { + String valueName = expression.as(Swc4jAstIdent.class).getSym(); + JavaLocalVariable localVariable = functionContext.getLocalVariable(valueName); + MethodVariableAccess methodVariableAccess = MethodVariableAccess.of(localVariable.getType()); + StackManipulation stackManipulation = methodVariableAccess.loadFrom(localVariable.getOffset()); + functionContext.getStackManipulations().add(stackManipulation); + TypeDescription valueType = localVariable.getType(); + JavaClassCast.getUpCastStackManipulation(valueType, variableType) + .ifPresent(functionContext.getStackManipulations()::add); + break; + } + default: + throw new Ts2JavaAstException( + expression, + SimpleFreeMarkerFormat.format("VarDecl init type ${type} is not supported.", + SimpleMap.of("type", expression.getType().name()))); + } + } + JavaLocalVariable localVariable = new JavaLocalVariable(variableName, variableType); + MethodVariableAccess methodVariableAccess = MethodVariableAccess.of(variableType); StackManipulation stackManipulation = methodVariableAccess.storeAt(functionContext.getNextOffset()); - functionContext.addStackManipulation(stackManipulation); + functionContext.getStackManipulations().add(stackManipulation); functionContext.addLocalVariable(localVariable); } else { throw new Ts2JavaAstException( bindingIdent, SimpleFreeMarkerFormat.format("VarDecl name ${name} type annotation is missing.", - SimpleMap.of("name", name))); + SimpleMap.of("name", variableName))); } break; } diff --git a/src/main/java/com/caoccao/javet/buddy/ts2java/compiler/JavaClassCast.java b/src/main/java/com/caoccao/javet/buddy/ts2java/compiler/JavaClassCast.java index c730c94..d6c12cf 100644 --- a/src/main/java/com/caoccao/javet/buddy/ts2java/compiler/JavaClassCast.java +++ b/src/main/java/com/caoccao/javet/buddy/ts2java/compiler/JavaClassCast.java @@ -23,7 +23,7 @@ import net.bytebuddy.implementation.bytecode.*; import net.bytebuddy.implementation.bytecode.assign.primitive.PrimitiveWideningDelegate; -import java.util.function.Consumer; +import java.util.Optional; public final class JavaClassCast { private JavaClassCast() { @@ -126,6 +126,18 @@ public static Subtraction getSubtraction(TypeDescription type) { SimpleMap.of("type", type.getName()))); } + public static Optional getUpCastStackManipulation( + TypeDescription fromType, + TypeDescription toType) { + if (fromType.isPrimitive() && toType.isPrimitive()) { + StackManipulation stackManipulation = PrimitiveWideningDelegate.forPrimitive(fromType).widenTo(toType); + if (stackManipulation.isValid() && stackManipulation != StackManipulation.Trivial.INSTANCE) { + return Optional.of(stackManipulation); + } + } + return Optional.empty(); + } + public static TypeDescription getUpCastTypeForMathOp(TypeDescription... types) { final int length = types.length; if (length <= 1) { @@ -149,18 +161,4 @@ public static TypeDescription getUpCastTypeForMathOp(TypeDescription... types) { } return returnType; } - - public static boolean upCast( - TypeDescription fromType, - TypeDescription toType, - Consumer stackManipulationConsumer) { - if (fromType.isPrimitive() && toType.isPrimitive()) { - StackManipulation stackManipulation = PrimitiveWideningDelegate.forPrimitive(fromType).widenTo(toType); - if (stackManipulation.isValid() && stackManipulation != StackManipulation.Trivial.INSTANCE) { - stackManipulationConsumer.accept(stackManipulation); - return true; - } - } - return false; - } } diff --git a/src/main/java/com/caoccao/javet/buddy/ts2java/compiler/JavaFunctionContext.java b/src/main/java/com/caoccao/javet/buddy/ts2java/compiler/JavaFunctionContext.java index f9543a1..2d32429 100644 --- a/src/main/java/com/caoccao/javet/buddy/ts2java/compiler/JavaFunctionContext.java +++ b/src/main/java/com/caoccao/javet/buddy/ts2java/compiler/JavaFunctionContext.java @@ -56,10 +56,6 @@ public void addLocalVariable(JavaLocalVariable localVariable) { } } - public void addStackManipulation(StackManipulation stackManipulation) { - stackManipulations.add(stackManipulation); - } - public JavaLocalVariable getLocalVariable(String name) { for (int lexicalScopeIndex = lexicalScopes.size() - 1; lexicalScopeIndex >= 0; lexicalScopeIndex--) { JavaLexicalScope lexicalScope = lexicalScopes.get(lexicalScopeIndex); @@ -93,7 +89,7 @@ public TypeDescription getReturnType() { } public List getStackManipulations() { - return new ArrayList<>(stackManipulations); + return stackManipulations; } public boolean isStatic() { diff --git a/src/test/java/com/caoccao/javet/buddy/ts2java/TestAssignments.java b/src/test/java/com/caoccao/javet/buddy/ts2java/TestAssignments.java index 1b22bc6..b4feb8b 100644 --- a/src/test/java/com/caoccao/javet/buddy/ts2java/TestAssignments.java +++ b/src/test/java/com/caoccao/javet/buddy/ts2java/TestAssignments.java @@ -66,6 +66,43 @@ public double assignAndCast(int a, long b) { return c + d; } + /* + public assignConst(IJ)J + L0 + LINENUMBER 97 L0 + BIPUSH 100 + ISTORE 4 + L1 + LINENUMBER 98 L1 + LDC 2 + LSTORE 5 + L2 + LINENUMBER 99 L2 + ILOAD 1 + I2L + LLOAD 2 + LADD + ILOAD 4 + I2L + LADD + LLOAD 5 + LADD + LRETURN + L3 + LOCALVARIABLE this Lcom/caoccao/javet/buddy/ts2java/TestAssignments; L0 L3 0 + LOCALVARIABLE a I L0 L3 1 + LOCALVARIABLE b J L0 L3 2 + LOCALVARIABLE c I L1 L3 4 + LOCALVARIABLE d J L2 L3 5 + MAXSTACK = 4 + MAXLOCALS = 7 + */ + public long assignConst(int a, long b) { + int c = 100; + long d = 2; + return a + b + c + d; + } + protected void init() { if (clazz == null) { String tsCode = null; @@ -101,4 +138,17 @@ public void testAssignAndCast() throws Exception { Object object = clazz.getConstructor().newInstance(); assertEquals(3.0D, (double) method.invoke(object, 1, 2L), 0.001D); } + + @Test + public void testAssignConst() throws Exception { + assertEquals(105L, assignConst(1, 2L)); + Method method = clazz.getMethod("assignConst", int.class, long.class); + assertNotNull(method); + assertEquals(long.class, method.getReturnType()); + assertEquals(2, method.getParameterCount()); + assertEquals(int.class, method.getParameters()[0].getType()); + assertEquals(long.class, method.getParameters()[1].getType()); + Object object = clazz.getConstructor().newInstance(); + assertEquals(105L, method.invoke(object, 1, 2L)); + } }