Skip to content

Commit

Permalink
✨ feat: Support bin expr inside bin expr
Browse files Browse the repository at this point in the history
  • Loading branch information
caoccao committed Oct 31, 2024
1 parent a46a25d commit 30dd4b5
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 77 deletions.
7 changes: 7 additions & 0 deletions scripts/ts/test/test.assignments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Original file line number Diff line number Diff line change
Expand Up @@ -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<Swc4jAstBinExpr> {
@Override
public Optional<TypeDescription> manipulate(JavaFunctionContext functionContext, Swc4jAstBinExpr ast) {
List<ISwc4jAstExpr> expressions = SimpleList.of(ast.getLeft(), ast.getRight());
List<JavaLocalVariable> 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:
Expand Down Expand Up @@ -88,7 +73,35 @@ public Optional<TypeDescription> 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<TypeDescription> 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())));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,10 @@ public Optional<TypeDescription> 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -39,46 +44,65 @@ public final class Ts2JavaAstVarDecl implements ITs2JavaAstStackManipulation<Swc
@Override
public Optional<TypeDescription> manipulate(JavaFunctionContext functionContext, Swc4jAstVarDecl ast) {
for (Swc4jAstVarDeclarator varDeclarator : ast.getDecls()) {
Optional<TypeDescription> 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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -126,6 +126,18 @@ public static Subtraction getSubtraction(TypeDescription type) {
SimpleMap.of("type", type.getName())));
}

public static Optional<StackManipulation> 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) {
Expand All @@ -149,18 +161,4 @@ public static TypeDescription getUpCastTypeForMathOp(TypeDescription... types) {
}
return returnType;
}

public static boolean upCast(
TypeDescription fromType,
TypeDescription toType,
Consumer<StackManipulation> 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -93,7 +89,7 @@ public TypeDescription getReturnType() {
}

public List<StackManipulation> getStackManipulations() {
return new ArrayList<>(stackManipulations);
return stackManipulations;
}

public boolean isStatic() {
Expand Down
50 changes: 50 additions & 0 deletions src/test/java/com/caoccao/javet/buddy/ts2java/TestAssignments.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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));
}
}

0 comments on commit 30dd4b5

Please sign in to comment.