mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
jso: implement vararg support for method calls
This commit is contained in:
parent
ccfe19994b
commit
582fcf904c
|
@ -125,7 +125,7 @@ public class Decompiler {
|
||||||
methodNode.getVariables().add(variable);
|
methodNode.getVariables().add(variable);
|
||||||
}
|
}
|
||||||
|
|
||||||
Optimizer optimizer = new Optimizer();
|
Optimizer optimizer = new Optimizer(classSource);
|
||||||
optimizer.optimize(methodNode, method.getProgram(), friendlyToDebugger);
|
optimizer.optimize(methodNode, method.getProgram(), friendlyToDebugger);
|
||||||
methodNode.getModifiers().addAll(method.getModifiers());
|
methodNode.getModifiers().addAll(method.getModifiers());
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ public class Decompiler {
|
||||||
node.getVariables().add(variable);
|
node.getVariables().add(variable);
|
||||||
}
|
}
|
||||||
|
|
||||||
Optimizer optimizer = new Optimizer();
|
Optimizer optimizer = new Optimizer(classSource);
|
||||||
optimizer.optimize(node, splitter, friendlyToDebugger);
|
optimizer.optimize(node, splitter, friendlyToDebugger);
|
||||||
node.getModifiers().addAll(method.getModifiers());
|
node.getModifiers().addAll(method.getModifiers());
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.teavm.ast.AsyncMethodPart;
|
||||||
import org.teavm.ast.RegularMethodNode;
|
import org.teavm.ast.RegularMethodNode;
|
||||||
import org.teavm.common.Graph;
|
import org.teavm.common.Graph;
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.Instruction;
|
import org.teavm.model.Instruction;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.Program;
|
import org.teavm.model.Program;
|
||||||
|
@ -32,6 +33,12 @@ import org.teavm.model.util.ProgramUtils;
|
||||||
import org.teavm.model.util.UsageExtractor;
|
import org.teavm.model.util.UsageExtractor;
|
||||||
|
|
||||||
public class Optimizer {
|
public class Optimizer {
|
||||||
|
private ClassReaderSource classes;
|
||||||
|
|
||||||
|
public Optimizer(ClassReaderSource classes) {
|
||||||
|
this.classes = classes;
|
||||||
|
}
|
||||||
|
|
||||||
public void optimize(RegularMethodNode method, Program program, boolean friendlyToDebugger) {
|
public void optimize(RegularMethodNode method, Program program, boolean friendlyToDebugger) {
|
||||||
ReadWriteStatsBuilder stats = new ReadWriteStatsBuilder(method.getVariables().size());
|
ReadWriteStatsBuilder stats = new ReadWriteStatsBuilder(method.getVariables().size());
|
||||||
stats.analyze(program);
|
stats.analyze(program);
|
||||||
|
@ -48,7 +55,7 @@ public class Optimizer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OptimizingVisitor optimizer = new OptimizingVisitor(preservedVars, stats.writes, stats.reads,
|
OptimizingVisitor optimizer = new OptimizingVisitor(preservedVars, stats.writes, stats.reads,
|
||||||
stats.constants, friendlyToDebugger);
|
stats.constants, friendlyToDebugger, classes);
|
||||||
method.getBody().acceptVisitor(optimizer);
|
method.getBody().acceptVisitor(optimizer);
|
||||||
method.setBody(optimizer.resultStmt);
|
method.setBody(optimizer.resultStmt);
|
||||||
int paramCount = method.getReference().parameterCount();
|
int paramCount = method.getReference().parameterCount();
|
||||||
|
@ -85,7 +92,7 @@ public class Optimizer {
|
||||||
BreakEliminator breakEliminator = new BreakEliminator();
|
BreakEliminator breakEliminator = new BreakEliminator();
|
||||||
breakEliminator.eliminate(part.getStatement());
|
breakEliminator.eliminate(part.getStatement());
|
||||||
OptimizingVisitor optimizer = new OptimizingVisitor(preservedVars, stats.writes, stats.reads,
|
OptimizingVisitor optimizer = new OptimizingVisitor(preservedVars, stats.writes, stats.reads,
|
||||||
stats.constants, friendlyToDebugger);
|
stats.constants, friendlyToDebugger, classes);
|
||||||
part.getStatement().acceptVisitor(optimizer);
|
part.getStatement().acceptVisitor(optimizer);
|
||||||
part.setStatement(optimizer.resultStmt);
|
part.setStatement(optimizer.resultStmt);
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ import org.teavm.ast.IdentifiedStatement;
|
||||||
import org.teavm.ast.InitClassStatement;
|
import org.teavm.ast.InitClassStatement;
|
||||||
import org.teavm.ast.InstanceOfExpr;
|
import org.teavm.ast.InstanceOfExpr;
|
||||||
import org.teavm.ast.InvocationExpr;
|
import org.teavm.ast.InvocationExpr;
|
||||||
|
import org.teavm.ast.InvocationType;
|
||||||
import org.teavm.ast.MonitorEnterStatement;
|
import org.teavm.ast.MonitorEnterStatement;
|
||||||
import org.teavm.ast.MonitorExitStatement;
|
import org.teavm.ast.MonitorExitStatement;
|
||||||
import org.teavm.ast.NewArrayExpr;
|
import org.teavm.ast.NewArrayExpr;
|
||||||
|
@ -64,6 +65,9 @@ import org.teavm.ast.UnaryOperation;
|
||||||
import org.teavm.ast.UnwrapArrayExpr;
|
import org.teavm.ast.UnwrapArrayExpr;
|
||||||
import org.teavm.ast.VariableExpr;
|
import org.teavm.ast.VariableExpr;
|
||||||
import org.teavm.ast.WhileStatement;
|
import org.teavm.ast.WhileStatement;
|
||||||
|
import org.teavm.interop.NoSideEffects;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.TextLocation;
|
import org.teavm.model.TextLocation;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
|
|
||||||
|
@ -82,15 +86,17 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
|
||||||
private Deque<TextLocation> locationStack = new LinkedList<>();
|
private Deque<TextLocation> locationStack = new LinkedList<>();
|
||||||
private Deque<TextLocation> notNullLocationStack = new ArrayDeque<>();
|
private Deque<TextLocation> notNullLocationStack = new ArrayDeque<>();
|
||||||
private List<ArrayOptimization> pendingArrayOptimizations;
|
private List<ArrayOptimization> pendingArrayOptimizations;
|
||||||
|
private ClassReaderSource classes;
|
||||||
|
|
||||||
OptimizingVisitor(boolean[] preservedVars, int[] writeFrequencies, int[] readFrequencies, Object[] constants,
|
OptimizingVisitor(boolean[] preservedVars, int[] writeFrequencies, int[] readFrequencies, Object[] constants,
|
||||||
boolean friendlyToDebugger) {
|
boolean friendlyToDebugger, ClassReaderSource classes) {
|
||||||
this.preservedVars = preservedVars;
|
this.preservedVars = preservedVars;
|
||||||
this.writeFrequencies = writeFrequencies;
|
this.writeFrequencies = writeFrequencies;
|
||||||
this.initialWriteFrequencies = writeFrequencies.clone();
|
this.initialWriteFrequencies = writeFrequencies.clone();
|
||||||
this.readFrequencies = readFrequencies;
|
this.readFrequencies = readFrequencies;
|
||||||
this.constants = constants;
|
this.constants = constants;
|
||||||
this.friendlyToDebugger = friendlyToDebugger;
|
this.friendlyToDebugger = friendlyToDebugger;
|
||||||
|
this.classes = classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isZero(Expr expr) {
|
private static boolean isZero(Expr expr) {
|
||||||
|
@ -385,6 +391,23 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isSideEffectFree(MethodReference method) {
|
||||||
|
var cls = classes.get(method.getClassName());
|
||||||
|
if (cls == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var methodReader = cls.getMethod(method.getDescriptor());
|
||||||
|
if (methodReader == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return methodReader.getAnnotations().get(NoSideEffects.class.getName()) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSideEffectFreeCall(InvocationExpr expr) {
|
||||||
|
return (expr.getType() == InvocationType.SPECIAL || expr.getType() == InvocationType.STATIC)
|
||||||
|
&& isSideEffectFree(expr.getMethod());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(InvocationExpr expr) {
|
public void visit(InvocationExpr expr) {
|
||||||
pushLocation(expr.getLocation());
|
pushLocation(expr.getLocation());
|
||||||
|
@ -392,9 +415,13 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
|
||||||
Expr[] args = expr.getArguments().toArray(new Expr[0]);
|
Expr[] args = expr.getArguments().toArray(new Expr[0]);
|
||||||
int barrierPos;
|
int barrierPos;
|
||||||
|
|
||||||
for (barrierPos = 0; barrierPos < args.length; ++barrierPos) {
|
if (isSideEffectFreeCall(expr)) {
|
||||||
if (!isSideEffectFree(args[barrierPos])) {
|
barrierPos = args.length;
|
||||||
break;
|
} else {
|
||||||
|
for (barrierPos = 0; barrierPos < args.length; ++barrierPos) {
|
||||||
|
if (!isSideEffectFree(args[barrierPos])) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -639,8 +666,9 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
|
||||||
while (!pendingArrayOptimizations.isEmpty()) {
|
while (!pendingArrayOptimizations.isEmpty()) {
|
||||||
statement = resultSequence.get(resultSequence.size() - 1);
|
statement = resultSequence.get(resultSequence.size() - 1);
|
||||||
int i = pendingArrayOptimizations.size() - 1;
|
int i = pendingArrayOptimizations.size() - 1;
|
||||||
if (!tryArrayUnwrap(pendingArrayOptimizations.get(i), statement)
|
var opt = pendingArrayOptimizations.get(i);
|
||||||
|| tryArraySet(pendingArrayOptimizations.get(i), statement)) {
|
var result = opt.isSet ? tryArraySet(opt, statement) : tryArrayUnwrap(opt, statement);
|
||||||
|
if (!result) {
|
||||||
pendingArrayOptimizations.remove(i);
|
pendingArrayOptimizations.remove(i);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
@ -721,11 +749,9 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (optimization.arraySize != readFrequencies[optimization.unwrappedArrayVariable]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
optimization.arrayElementIndex = 0;
|
optimization.arrayElementIndex = 0;
|
||||||
|
optimization.remainingUseCount = readFrequencies[optimization.unwrappedArrayVariable];
|
||||||
|
optimization.isSet = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -756,10 +782,22 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(subscript.getIndex() instanceof ConstantExpr)) {
|
var index = subscript.getIndex();
|
||||||
|
if (index instanceof BoundCheckExpr) {
|
||||||
|
var boundCheck = (BoundCheckExpr) index;
|
||||||
|
if (!(boundCheck.getArray() instanceof VariableExpr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (((VariableExpr) boundCheck.getArray()).getIndex() != optimization.unwrappedArrayVariable) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
index = boundCheck.getIndex();
|
||||||
|
optimization.remainingUseCount--;
|
||||||
|
}
|
||||||
|
if (!(index instanceof ConstantExpr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Object constantValue = ((ConstantExpr) subscript.getIndex()).getValue();
|
Object constantValue = ((ConstantExpr) index).getValue();
|
||||||
if (!Integer.valueOf(optimization.arrayElementIndex).equals(constantValue)) {
|
if (!Integer.valueOf(optimization.arrayElementIndex).equals(constantValue)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -772,11 +810,14 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
optimization.elements.add(assign.getRightValue());
|
optimization.elements.add(assign.getRightValue());
|
||||||
|
--optimization.remainingUseCount;
|
||||||
if (++optimization.arrayElementIndex == optimization.arraySize) {
|
if (++optimization.arrayElementIndex == optimization.arraySize) {
|
||||||
applyArrayOptimization(optimization);
|
if (optimization.remainingUseCount == 0) {
|
||||||
return true;
|
applyArrayOptimization(optimization);
|
||||||
|
}
|
||||||
|
pendingArrayOptimizations.remove(pendingArrayOptimizations.size() - 1);
|
||||||
}
|
}
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyArrayOptimization(ArrayOptimization optimization) {
|
private void applyArrayOptimization(ArrayOptimization optimization) {
|
||||||
|
@ -1303,6 +1344,29 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
|
||||||
return isSideEffectFree(((PrimitiveCastExpr) expr).getValue());
|
return isSideEffectFree(((PrimitiveCastExpr) expr).getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (expr instanceof ArrayFromDataExpr) {
|
||||||
|
var list = ((ArrayFromDataExpr) expr).getData();
|
||||||
|
for (var element : list) {
|
||||||
|
if (!isSideEffectFree(element)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expr instanceof InvocationExpr) {
|
||||||
|
var invocation = (InvocationExpr) expr;
|
||||||
|
if (isSideEffectFreeCall(invocation)) {
|
||||||
|
for (var arg : invocation.getArguments()) {
|
||||||
|
if (!isSideEffectFree(arg)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (expr instanceof NewExpr) {
|
if (expr instanceof NewExpr) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1326,6 +1390,7 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ArrayOptimization {
|
static class ArrayOptimization {
|
||||||
|
boolean isSet;
|
||||||
int index;
|
int index;
|
||||||
NewArrayExpr array;
|
NewArrayExpr array;
|
||||||
int arrayVariable;
|
int arrayVariable;
|
||||||
|
@ -1333,6 +1398,7 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
|
||||||
int unwrappedArrayVariable;
|
int unwrappedArrayVariable;
|
||||||
int arrayElementIndex;
|
int arrayElementIndex;
|
||||||
int arraySize;
|
int arraySize;
|
||||||
|
int remainingUseCount;
|
||||||
List<Expr> elements = new ArrayList<>();
|
List<Expr> elements = new ArrayList<>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,7 @@ public class InvokeInstruction extends Instruction {
|
||||||
private List<? extends Variable> argumentList = new AbstractList<Variable>() {
|
private List<? extends Variable> argumentList = new AbstractList<Variable>() {
|
||||||
@Override
|
@Override
|
||||||
public Variable get(int index) {
|
public Variable get(int index) {
|
||||||
if (arguments == null) {
|
if (arguments == null || index >= arguments.length) {
|
||||||
throw new IndexOutOfBoundsException();
|
throw new IndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
return arguments[index];
|
return arguments[index];
|
||||||
|
|
|
@ -37,6 +37,7 @@ import org.teavm.model.instructions.ConstructArrayInstruction;
|
||||||
import org.teavm.model.instructions.GetElementInstruction;
|
import org.teavm.model.instructions.GetElementInstruction;
|
||||||
import org.teavm.model.instructions.IntegerConstantInstruction;
|
import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||||
import org.teavm.model.instructions.JumpInstruction;
|
import org.teavm.model.instructions.JumpInstruction;
|
||||||
|
import org.teavm.model.instructions.NullCheckInstruction;
|
||||||
import org.teavm.model.instructions.NumericOperandType;
|
import org.teavm.model.instructions.NumericOperandType;
|
||||||
import org.teavm.model.instructions.PutElementInstruction;
|
import org.teavm.model.instructions.PutElementInstruction;
|
||||||
import org.teavm.model.instructions.UnwrapArrayInstruction;
|
import org.teavm.model.instructions.UnwrapArrayInstruction;
|
||||||
|
@ -344,6 +345,11 @@ public class BoundCheckInsertion {
|
||||||
assign(insn.getArray(), insn.getReceiver());
|
assign(insn.getArray(), insn.getReceiver());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(NullCheckInstruction insn) {
|
||||||
|
isConstantSizedArray[index(insn.getReceiver())] = isConstantSizedArray[index(insn.getValue())];
|
||||||
|
}
|
||||||
|
|
||||||
private void assign(Variable from, Variable to) {
|
private void assign(Variable from, Variable to) {
|
||||||
map[to.getIndex()] = map[from.getIndex()];
|
map[to.getIndex()] = map[from.getIndex()];
|
||||||
}
|
}
|
||||||
|
|
|
@ -496,7 +496,7 @@ public class Parser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((access & Opcodes.ACC_VARARGS) != 0) {
|
if ((access & Opcodes.ACC_VARARGS) != 0) {
|
||||||
if (type == DECL_FIELD) {
|
if (type == DECL_METHOD) {
|
||||||
member.getModifiers().add(ElementModifier.VARARGS);
|
member.getModifiers().add(ElementModifier.VARARGS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -280,3 +280,19 @@ let $rt_createMultiArrayImpl = (cls, arrays, dimensions, start) => {
|
||||||
}
|
}
|
||||||
return arrays[0];
|
return arrays[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let $rt_concatArrays = (a, b) => {
|
||||||
|
if (a.length === 0) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
if (b.length === 0) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
if (!teavm_globals.Array.isArray(a)) {
|
||||||
|
a = teavm_globals.Array.from(a);
|
||||||
|
}
|
||||||
|
if (!teavm_globals.Array.isArray(b)) {
|
||||||
|
b = teavm_globals.Array.from(b);
|
||||||
|
}
|
||||||
|
return a.concat(b);
|
||||||
|
}
|
|
@ -107,3 +107,5 @@ let $rt_setThread = t => {
|
||||||
return teavm_javaMethod("java.lang.Thread", "setCurrentThread(Ljava/lang/Thread;)V")(t);
|
return teavm_javaMethod("java.lang.Thread", "setCurrentThread(Ljava/lang/Thread;)V")(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let $rt_apply = (instance, method, args) => instance[method].apply(instance, args);
|
|
@ -21,6 +21,6 @@ import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
@Target({ ElementType.METHOD, ElementType.TYPE, ElementType.CONSTRUCTOR })
|
||||||
public @interface NoSideEffects {
|
public @interface NoSideEffects {
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,9 +23,11 @@ import org.teavm.jso.JSProperty;
|
||||||
|
|
||||||
@JSClass(name = "Array")
|
@JSClass(name = "Array")
|
||||||
public class JSArray<T> implements JSArrayReader<T> {
|
public class JSArray<T> implements JSArrayReader<T> {
|
||||||
|
@NoSideEffects
|
||||||
public JSArray(int size) {
|
public JSArray(int size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NoSideEffects
|
||||||
public JSArray() {
|
public JSArray() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,10 @@ final class JS {
|
||||||
@NoSideEffects
|
@NoSideEffects
|
||||||
public static native JSObject arrayData(Object array);
|
public static native JSObject arrayData(Object array);
|
||||||
|
|
||||||
|
@InjectedBy(JSNativeInjector.class)
|
||||||
|
@NoSideEffects
|
||||||
|
public static native JSObject concatArray(JSObject a, JSObject b);
|
||||||
|
|
||||||
@InjectedBy(JSNativeInjector.class)
|
@InjectedBy(JSNativeInjector.class)
|
||||||
@PluggableDependency(JSNativeInjector.class)
|
@PluggableDependency(JSNativeInjector.class)
|
||||||
@NoSideEffects
|
@NoSideEffects
|
||||||
|
@ -249,6 +253,7 @@ final class JS {
|
||||||
return JS::wrap;
|
return JS::wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NoSideEffects
|
||||||
public static JSArray<JSString> wrap(String[] array) {
|
public static JSArray<JSString> wrap(String[] array) {
|
||||||
if (array == null) {
|
if (array == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -516,6 +521,80 @@ final class JS {
|
||||||
JSObject d, JSObject e, JSObject f, JSObject g, JSObject h, JSObject i, JSObject j, JSObject k,
|
JSObject d, JSObject e, JSObject f, JSObject g, JSObject h, JSObject i, JSObject j, JSObject k,
|
||||||
JSObject l, JSObject m);
|
JSObject l, JSObject m);
|
||||||
|
|
||||||
|
@InjectedBy(JSNativeInjector.class)
|
||||||
|
public static native JSObject apply(JSObject instance, JSObject method, JSArray<JSObject> v);
|
||||||
|
|
||||||
|
@InjectedBy(JSNativeInjector.class)
|
||||||
|
@PluggableDependency(JSNativeInjector.class)
|
||||||
|
@NoSideEffects
|
||||||
|
public static native JSObject arrayOf(JSObject a);
|
||||||
|
|
||||||
|
@InjectedBy(JSNativeInjector.class)
|
||||||
|
@PluggableDependency(JSNativeInjector.class)
|
||||||
|
@NoSideEffects
|
||||||
|
public static native JSObject arrayOf(JSObject a, JSObject b);
|
||||||
|
|
||||||
|
@InjectedBy(JSNativeInjector.class)
|
||||||
|
@PluggableDependency(JSNativeInjector.class)
|
||||||
|
@NoSideEffects
|
||||||
|
public static native JSObject arrayOf(JSObject a, JSObject b, JSObject c);
|
||||||
|
|
||||||
|
@InjectedBy(JSNativeInjector.class)
|
||||||
|
@PluggableDependency(JSNativeInjector.class)
|
||||||
|
@NoSideEffects
|
||||||
|
public static native JSObject arrayOf(JSObject a, JSObject b, JSObject c, JSObject d);
|
||||||
|
|
||||||
|
@InjectedBy(JSNativeInjector.class)
|
||||||
|
@PluggableDependency(JSNativeInjector.class)
|
||||||
|
@NoSideEffects
|
||||||
|
public static native JSObject arrayOf(JSObject a, JSObject b, JSObject c, JSObject d, JSObject e);
|
||||||
|
|
||||||
|
@InjectedBy(JSNativeInjector.class)
|
||||||
|
@PluggableDependency(JSNativeInjector.class)
|
||||||
|
@NoSideEffects
|
||||||
|
public static native JSObject arrayOf(JSObject a, JSObject b, JSObject c, JSObject d, JSObject e, JSObject f);
|
||||||
|
|
||||||
|
@InjectedBy(JSNativeInjector.class)
|
||||||
|
@PluggableDependency(JSNativeInjector.class)
|
||||||
|
@NoSideEffects
|
||||||
|
public static native JSObject arrayOf(JSObject a, JSObject b, JSObject c, JSObject d, JSObject e, JSObject f,
|
||||||
|
JSObject g);
|
||||||
|
|
||||||
|
@InjectedBy(JSNativeInjector.class)
|
||||||
|
@PluggableDependency(JSNativeInjector.class)
|
||||||
|
@NoSideEffects
|
||||||
|
public static native JSObject arrayOf(JSObject a, JSObject b, JSObject c, JSObject d, JSObject e, JSObject f,
|
||||||
|
JSObject g, JSObject h);
|
||||||
|
|
||||||
|
@InjectedBy(JSNativeInjector.class)
|
||||||
|
@PluggableDependency(JSNativeInjector.class)
|
||||||
|
public static native JSObject arrayOf(JSObject a, JSObject b, JSObject c, JSObject d, JSObject e, JSObject f,
|
||||||
|
JSObject g, JSObject h, JSObject i);
|
||||||
|
|
||||||
|
@InjectedBy(JSNativeInjector.class)
|
||||||
|
@PluggableDependency(JSNativeInjector.class)
|
||||||
|
@NoSideEffects
|
||||||
|
public static native JSObject arrayOf(JSObject a, JSObject b, JSObject c, JSObject d, JSObject e, JSObject f,
|
||||||
|
JSObject g, JSObject h, JSObject i, JSObject j);
|
||||||
|
|
||||||
|
@InjectedBy(JSNativeInjector.class)
|
||||||
|
@PluggableDependency(JSNativeInjector.class)
|
||||||
|
@NoSideEffects
|
||||||
|
public static native JSObject arrayOf(JSObject a, JSObject b, JSObject c, JSObject d, JSObject e, JSObject f,
|
||||||
|
JSObject g, JSObject h, JSObject i, JSObject j, JSObject k);
|
||||||
|
|
||||||
|
@InjectedBy(JSNativeInjector.class)
|
||||||
|
@PluggableDependency(JSNativeInjector.class)
|
||||||
|
@NoSideEffects
|
||||||
|
public static native JSObject arrayOf(JSObject a, JSObject b, JSObject c, JSObject d, JSObject e, JSObject f,
|
||||||
|
JSObject g, JSObject h, JSObject i, JSObject j, JSObject k, JSObject l);
|
||||||
|
|
||||||
|
@InjectedBy(JSNativeInjector.class)
|
||||||
|
@PluggableDependency(JSNativeInjector.class)
|
||||||
|
@NoSideEffects
|
||||||
|
public static native JSObject arrayOf(JSObject a, JSObject b, JSObject c, JSObject d, JSObject e, JSObject f,
|
||||||
|
JSObject g, JSObject h, JSObject i, JSObject j, JSObject k, JSObject l, JSObject m);
|
||||||
|
|
||||||
@InjectedBy(JSNativeInjector.class)
|
@InjectedBy(JSNativeInjector.class)
|
||||||
@PluggableDependency(JSNativeInjector.class)
|
@PluggableDependency(JSNativeInjector.class)
|
||||||
public static native JSObject construct(JSObject cls);
|
public static native JSObject construct(JSObject cls);
|
||||||
|
|
|
@ -769,23 +769,64 @@ class JSClassProcessor {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var vararg = method.hasModifier(ElementModifier.VARARGS);
|
||||||
Variable result = invoke.getReceiver() != null ? program.createVariable() : null;
|
Variable result = invoke.getReceiver() != null ? program.createVariable() : null;
|
||||||
InvokeInstruction newInvoke = new InvokeInstruction();
|
InvokeInstruction newInvoke = new InvokeInstruction();
|
||||||
newInvoke.setMethod(JSMethods.invoke(method.parameterCount()));
|
newInvoke.setMethod(vararg ? JSMethods.APPLY : JSMethods.invoke(method.parameterCount()));
|
||||||
newInvoke.setType(InvocationType.SPECIAL);
|
newInvoke.setType(InvocationType.SPECIAL);
|
||||||
newInvoke.setReceiver(result);
|
newInvoke.setReceiver(result);
|
||||||
|
|
||||||
List<Variable> newArguments = new ArrayList<>();
|
List<Variable> newArguments = new ArrayList<>();
|
||||||
newArguments.add(getCallTarget(invoke));
|
newArguments.add(getCallTarget(invoke));
|
||||||
newArguments.add(marshaller.addStringWrap(marshaller.addString(name, invoke.getLocation()),
|
newArguments.add(marshaller.addStringWrap(marshaller.addString(name, invoke.getLocation()),
|
||||||
invoke.getLocation()));
|
invoke.getLocation()));
|
||||||
newInvoke.setLocation(invoke.getLocation());
|
newInvoke.setLocation(invoke.getLocation());
|
||||||
|
|
||||||
|
var callArguments = new ArrayList<Variable>();
|
||||||
for (int i = 0; i < invoke.getArguments().size(); ++i) {
|
for (int i = 0; i < invoke.getArguments().size(); ++i) {
|
||||||
var arg = invoke.getArguments().get(i);
|
var arg = invoke.getArguments().get(i);
|
||||||
|
var byRef = byRefParams[i];
|
||||||
|
if (vararg && i == invoke.getArguments().size() - 1
|
||||||
|
&& typeHelper.isSupportedByRefType(method.parameterType(i))) {
|
||||||
|
byRef = true;
|
||||||
|
}
|
||||||
arg = marshaller.wrapArgument(callLocation, arg,
|
arg = marshaller.wrapArgument(callLocation, arg,
|
||||||
method.parameterType(i), types.typeOf(arg), byRefParams[i]);
|
method.parameterType(i), types.typeOf(arg), byRef);
|
||||||
newArguments.add(arg);
|
callArguments.add(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vararg) {
|
||||||
|
Variable prefixArg = null;
|
||||||
|
if (callArguments.size() > 1) {
|
||||||
|
var arrayOfInvocation = new InvokeInstruction();
|
||||||
|
arrayOfInvocation.setType(InvocationType.SPECIAL);
|
||||||
|
arrayOfInvocation.setArguments(callArguments.subList(0, callArguments.size() - 1)
|
||||||
|
.toArray(new Variable[0]));
|
||||||
|
arrayOfInvocation.setMethod(JSMethods.arrayOf(callArguments.size() - 1));
|
||||||
|
arrayOfInvocation.setReceiver(program.createVariable());
|
||||||
|
arrayOfInvocation.setLocation(invoke.getLocation());
|
||||||
|
replacement.add(arrayOfInvocation);
|
||||||
|
prefixArg = arrayOfInvocation.getReceiver();
|
||||||
|
}
|
||||||
|
|
||||||
|
var arrayArg = callArguments.get(callArguments.size() - 1);
|
||||||
|
|
||||||
|
if (prefixArg != null) {
|
||||||
|
var concat = new InvokeInstruction();
|
||||||
|
concat.setType(InvocationType.SPECIAL);
|
||||||
|
concat.setArguments(prefixArg, arrayArg);
|
||||||
|
concat.setMethod(JSMethods.CONCAT_ARRAY);
|
||||||
|
concat.setReceiver(program.createVariable());
|
||||||
|
concat.setLocation(invoke.getLocation());
|
||||||
|
replacement.add(concat);
|
||||||
|
arrayArg = concat.getReceiver();
|
||||||
|
}
|
||||||
|
newArguments.add(arrayArg);
|
||||||
|
} else {
|
||||||
|
newArguments.addAll(callArguments);
|
||||||
}
|
}
|
||||||
newInvoke.setArguments(newArguments.toArray(new Variable[0]));
|
newInvoke.setArguments(newArguments.toArray(new Variable[0]));
|
||||||
|
|
||||||
replacement.add(newInvoke);
|
replacement.add(newInvoke);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType(), false,
|
result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType(), false,
|
||||||
|
|
|
@ -31,10 +31,14 @@ final class JSMethods {
|
||||||
JSObject.class, void.class);
|
JSObject.class, void.class);
|
||||||
public static final MethodReference SET_PURE = new MethodReference(JS.class, "setPure", JSObject.class,
|
public static final MethodReference SET_PURE = new MethodReference(JS.class, "setPure", JSObject.class,
|
||||||
JSObject.class, JSObject.class, void.class);
|
JSObject.class, JSObject.class, void.class);
|
||||||
|
public static final MethodReference APPLY = new MethodReference(JS.class, "apply", JSObject.class, JSObject.class,
|
||||||
|
JSArray.class, JSObject.class);
|
||||||
public static final MethodReference FUNCTION = new MethodReference(JS.class, "function", JSObject.class,
|
public static final MethodReference FUNCTION = new MethodReference(JS.class, "function", JSObject.class,
|
||||||
JSObject.class, JSObject.class);
|
JSObject.class, JSObject.class);
|
||||||
public static final MethodReference ARRAY_DATA = new MethodReference(JS.class, "arrayData",
|
public static final MethodReference ARRAY_DATA = new MethodReference(JS.class, "arrayData",
|
||||||
Object.class, JSObject.class);
|
Object.class, JSObject.class);
|
||||||
|
public static final MethodReference CONCAT_ARRAY = new MethodReference(JS.class, "concatArray",
|
||||||
|
JSObject.class, JSObject.class, JSObject.class);
|
||||||
public static final MethodReference ARRAY_MAPPER = new MethodReference(JS.class, "arrayMapper",
|
public static final MethodReference ARRAY_MAPPER = new MethodReference(JS.class, "arrayMapper",
|
||||||
JS.WrapFunction.class, JS.WrapFunction.class);
|
JS.WrapFunction.class, JS.WrapFunction.class);
|
||||||
public static final MethodReference BOOLEAN_ARRAY_WRAPPER = new MethodReference(JS.class, "booleanArrayWrapper",
|
public static final MethodReference BOOLEAN_ARRAY_WRAPPER = new MethodReference(JS.class, "booleanArrayWrapper",
|
||||||
|
@ -111,6 +115,9 @@ final class JSMethods {
|
||||||
public static final MethodReference DATA_TO_ARRAY = new MethodReference(JS.class,
|
public static final MethodReference DATA_TO_ARRAY = new MethodReference(JS.class,
|
||||||
"dataToArray", JSObject.class, JSObject[].class);
|
"dataToArray", JSObject.class, JSObject[].class);
|
||||||
|
|
||||||
|
public static final MethodReference WRAP_STRING = new MethodReference(JS.class, "wrap",
|
||||||
|
String.class, JSObject.class);
|
||||||
|
|
||||||
public static final MethodReference FUNCTION_AS_OBJECT = new MethodReference(JS.class, "functionAsObject",
|
public static final MethodReference FUNCTION_AS_OBJECT = new MethodReference(JS.class, "functionAsObject",
|
||||||
JSObject.class, JSObject.class, JSObject.class);
|
JSObject.class, JSObject.class, JSObject.class);
|
||||||
|
|
||||||
|
@ -122,6 +129,7 @@ final class JSMethods {
|
||||||
public static final ValueType JS_ARRAY = ValueType.object(JSArray.class.getName());
|
public static final ValueType JS_ARRAY = ValueType.object(JSArray.class.getName());
|
||||||
private static final MethodReference[] INVOKE_METHODS = new MethodReference[13];
|
private static final MethodReference[] INVOKE_METHODS = new MethodReference[13];
|
||||||
private static final MethodReference[] CONSTRUCT_METHODS = new MethodReference[13];
|
private static final MethodReference[] CONSTRUCT_METHODS = new MethodReference[13];
|
||||||
|
private static final MethodReference[] ARRAY_OF_METHODS = new MethodReference[13];
|
||||||
|
|
||||||
static {
|
static {
|
||||||
for (int i = 0; i < INVOKE_METHODS.length; ++i) {
|
for (int i = 0; i < INVOKE_METHODS.length; ++i) {
|
||||||
|
@ -132,6 +140,10 @@ final class JSMethods {
|
||||||
var constructSignature = new ValueType[i + 2];
|
var constructSignature = new ValueType[i + 2];
|
||||||
Arrays.fill(constructSignature, JS_OBJECT);
|
Arrays.fill(constructSignature, JS_OBJECT);
|
||||||
CONSTRUCT_METHODS[i] = new MethodReference(JS.class.getName(), "construct", constructSignature);
|
CONSTRUCT_METHODS[i] = new MethodReference(JS.class.getName(), "construct", constructSignature);
|
||||||
|
|
||||||
|
var arrayOfSignature = new ValueType[i + 1];
|
||||||
|
Arrays.fill(arrayOfSignature, JS_OBJECT);
|
||||||
|
ARRAY_OF_METHODS[i] = new MethodReference(JS.class.getName(), "arrayOf", arrayOfSignature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,4 +157,8 @@ final class JSMethods {
|
||||||
public static MethodReference construct(int parameterCount) {
|
public static MethodReference construct(int parameterCount) {
|
||||||
return CONSTRUCT_METHODS[parameterCount];
|
return CONSTRUCT_METHODS[parameterCount];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static MethodReference arrayOf(int parameterCount) {
|
||||||
|
return ARRAY_OF_METHODS[parameterCount];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,17 @@
|
||||||
package org.teavm.jso.impl;
|
package org.teavm.jso.impl;
|
||||||
|
|
||||||
import static org.teavm.backend.javascript.rendering.RenderingUtil.escapeString;
|
import static org.teavm.backend.javascript.rendering.RenderingUtil.escapeString;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import org.teavm.ast.ArrayFromDataExpr;
|
||||||
import org.teavm.ast.ConstantExpr;
|
import org.teavm.ast.ConstantExpr;
|
||||||
import org.teavm.ast.Expr;
|
import org.teavm.ast.Expr;
|
||||||
import org.teavm.ast.InvocationExpr;
|
import org.teavm.ast.InvocationExpr;
|
||||||
|
import org.teavm.ast.InvocationType;
|
||||||
|
import org.teavm.ast.NewArrayExpr;
|
||||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||||
import org.teavm.backend.javascript.rendering.Precedence;
|
import org.teavm.backend.javascript.rendering.Precedence;
|
||||||
import org.teavm.backend.javascript.spi.Injector;
|
import org.teavm.backend.javascript.spi.Injector;
|
||||||
|
@ -38,6 +44,7 @@ import org.teavm.model.ValueType;
|
||||||
public class JSNativeInjector implements Injector, DependencyPlugin {
|
public class JSNativeInjector implements Injector, DependencyPlugin {
|
||||||
private Set<MethodReference> reachedFunctorMethods = new HashSet<>();
|
private Set<MethodReference> reachedFunctorMethods = new HashSet<>();
|
||||||
private Set<DependencyNode> functorParamNodes = new HashSet<>();
|
private Set<DependencyNode> functorParamNodes = new HashSet<>();
|
||||||
|
private static final ValueType STRING_ARRAY = ValueType.arrayOf(ValueType.object("java.lang.String"));
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void generate(InjectorContext context, MethodReference methodRef) {
|
public void generate(InjectorContext context, MethodReference methodRef) {
|
||||||
|
@ -47,6 +54,13 @@ public class JSNativeInjector implements Injector, DependencyPlugin {
|
||||||
context.writeExpr(context.getArgument(0));
|
context.writeExpr(context.getArgument(0));
|
||||||
writer.append(".data");
|
writer.append(".data");
|
||||||
break;
|
break;
|
||||||
|
case "concatArray":
|
||||||
|
writer.appendFunction("$rt_concatArrays").append("(");
|
||||||
|
context.writeExpr(context.getArgument(0), Precedence.ASSIGNMENT);
|
||||||
|
writer.append(",").ws();
|
||||||
|
context.writeExpr(context.getArgument(1), Precedence.ASSIGNMENT);
|
||||||
|
writer.append(")");
|
||||||
|
break;
|
||||||
case "get":
|
case "get":
|
||||||
case "getPure":
|
case "getPure":
|
||||||
context.writeExpr(context.getArgument(0), Precedence.MEMBER_ACCESS);
|
context.writeExpr(context.getArgument(0), Precedence.MEMBER_ACCESS);
|
||||||
|
@ -71,6 +85,19 @@ public class JSNativeInjector implements Injector, DependencyPlugin {
|
||||||
}
|
}
|
||||||
writer.append(')');
|
writer.append(')');
|
||||||
break;
|
break;
|
||||||
|
case "apply":
|
||||||
|
applyFunction(context);
|
||||||
|
break;
|
||||||
|
case "arrayOf":
|
||||||
|
writer.append('[');
|
||||||
|
for (int i = 0; i < context.argumentCount(); ++i) {
|
||||||
|
if (i > 0) {
|
||||||
|
writer.append(',').ws();
|
||||||
|
}
|
||||||
|
context.writeExpr(context.getArgument(i), Precedence.min());
|
||||||
|
}
|
||||||
|
writer.append(']');
|
||||||
|
break;
|
||||||
case "construct":
|
case "construct":
|
||||||
if (context.getPrecedence().ordinal() >= Precedence.FUNCTION_CALL.ordinal()) {
|
if (context.getPrecedence().ordinal() >= Precedence.FUNCTION_CALL.ordinal()) {
|
||||||
writer.append("(");
|
writer.append("(");
|
||||||
|
@ -172,6 +199,107 @@ public class JSNativeInjector implements Injector, DependencyPlugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applyFunction(InjectorContext context) {
|
||||||
|
if (tryApplyFunctionOptimized(context)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var writer = context.getWriter();
|
||||||
|
writer.appendFunction("$rt_apply").append("(");
|
||||||
|
context.writeExpr(context.getArgument(0), Precedence.ASSIGNMENT);
|
||||||
|
writer.append(",").ws();
|
||||||
|
context.writeExpr(context.getArgument(1), Precedence.ASSIGNMENT);
|
||||||
|
writer.append(",").ws();
|
||||||
|
context.writeExpr(context.getArgument(2), Precedence.ASSIGNMENT);
|
||||||
|
writer.append(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tryApplyFunctionOptimized(InjectorContext context) {
|
||||||
|
var paramList = new ArrayList<Expr>();
|
||||||
|
if (!extractConstantArgList(context.getArgument(2), paramList) || paramList.size() >= 13) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyFunctionOptimized(context, paramList);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean extractConstantArgList(Expr expr, List<Expr> target) {
|
||||||
|
if (!(expr instanceof InvocationExpr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var invocation = (InvocationExpr) expr;
|
||||||
|
if (!invocation.getMethod().getClassName().equals(JS.class.getName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (invocation.getMethod().getName()) {
|
||||||
|
case "arrayOf":
|
||||||
|
target.addAll(invocation.getArguments());
|
||||||
|
return true;
|
||||||
|
case "concatArray":
|
||||||
|
return extractConstantArgList(invocation.getArguments().get(0), target)
|
||||||
|
&& extractConstantArgList(invocation.getArguments().get(1), target);
|
||||||
|
case "arrayData": {
|
||||||
|
var arg = invocation.getArguments().get(0);
|
||||||
|
if (arg instanceof ArrayFromDataExpr) {
|
||||||
|
target.addAll(((ArrayFromDataExpr) arg).getData());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (arg instanceof NewArrayExpr && isEmptyArrayConstructor((NewArrayExpr) arg)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "wrap": {
|
||||||
|
if (invocation.getMethod().parameterType(0).equals(STRING_ARRAY)) {
|
||||||
|
var arg = invocation.getArguments().get(0);
|
||||||
|
if (arg instanceof ArrayFromDataExpr) {
|
||||||
|
extractConstantStringArgList(((ArrayFromDataExpr) arg).getData(), target);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (arg instanceof NewArrayExpr && isEmptyArrayConstructor((NewArrayExpr) arg)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEmptyArrayConstructor(NewArrayExpr expr) {
|
||||||
|
var length = expr.getLength();
|
||||||
|
if (!(length instanceof ConstantExpr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Objects.equals(((ConstantExpr) length).getValue(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void extractConstantStringArgList(List<Expr> source, List<Expr> target) {
|
||||||
|
for (var element : source) {
|
||||||
|
var invocation = new InvocationExpr();
|
||||||
|
invocation.setType(InvocationType.STATIC);
|
||||||
|
invocation.setMethod(JSMethods.WRAP_STRING);
|
||||||
|
invocation.setLocation(element.getLocation());
|
||||||
|
invocation.getArguments().add(element);
|
||||||
|
target.add(invocation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyFunctionOptimized(InjectorContext context, List<Expr> paramList) {
|
||||||
|
var writer = context.getWriter();
|
||||||
|
context.writeExpr(context.getArgument(0), Precedence.GROUPING);
|
||||||
|
renderProperty(context.getArgument(1), context);
|
||||||
|
writer.append('(');
|
||||||
|
for (int i = 0; i < paramList.size(); ++i) {
|
||||||
|
if (i > 0) {
|
||||||
|
writer.append(',').ws();
|
||||||
|
}
|
||||||
|
context.writeExpr(paramList.get(i), Precedence.min());
|
||||||
|
}
|
||||||
|
writer.append(')');
|
||||||
|
}
|
||||||
|
|
||||||
private void dataToArray(InjectorContext context, String className) {
|
private void dataToArray(InjectorContext context, String className) {
|
||||||
var writer = context.getWriter();
|
var writer = context.getWriter();
|
||||||
writer.appendFunction("$rt_wrapArray").append("(").appendFunction(className).append(",").ws();
|
writer.appendFunction("$rt_wrapArray").append("(").appendFunction(className).append(",").ws();
|
||||||
|
|
91
tests/src/test/java/org/teavm/jso/test/CallTest.java
Normal file
91
tests/src/test/java/org/teavm/jso/test/CallTest.java
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* 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 org.teavm.jso.test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.teavm.jso.JSClass;
|
||||||
|
import org.teavm.jso.JSMethod;
|
||||||
|
import org.teavm.jso.JSObject;
|
||||||
|
import org.teavm.junit.AttachJavaScript;
|
||||||
|
import org.teavm.junit.EachTestCompiledSeparately;
|
||||||
|
import org.teavm.junit.OnlyPlatform;
|
||||||
|
import org.teavm.junit.SkipJVM;
|
||||||
|
import org.teavm.junit.TeaVMTestRunner;
|
||||||
|
import org.teavm.junit.TestPlatform;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
@RunWith(TeaVMTestRunner.class)
|
||||||
|
@SkipJVM
|
||||||
|
@OnlyPlatform(TestPlatform.JAVASCRIPT)
|
||||||
|
@EachTestCompiledSeparately
|
||||||
|
public class CallTest {
|
||||||
|
@Test
|
||||||
|
@AttachJavaScript("org/teavm/jso/test/vararg.js")
|
||||||
|
public void simpleVararg() {
|
||||||
|
assertEquals("va:q:w", TestClass.allVararg("q", "w"));
|
||||||
|
assertEquals("va:23:42", TestClass.allVarargInt(23, 42));
|
||||||
|
|
||||||
|
var array = new String[3];
|
||||||
|
for (var i = 0; i < array.length; ++i) {
|
||||||
|
array[i] = String.valueOf((char) ('A' + i));
|
||||||
|
}
|
||||||
|
assertEquals("va:A:B:C", TestClass.allVararg(array));
|
||||||
|
|
||||||
|
var intArray = new int[3];
|
||||||
|
for (var i = 0; i < array.length; ++i) {
|
||||||
|
intArray[i] = 6 + i;
|
||||||
|
}
|
||||||
|
assertEquals("va:6:7:8", TestClass.allVarargInt(intArray));
|
||||||
|
|
||||||
|
assertEquals("va", TestClass.allVararg());
|
||||||
|
assertEquals("va", TestClass.allVarargInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@AttachJavaScript("org/teavm/jso/test/vararg.js")
|
||||||
|
public void restVararg() {
|
||||||
|
assertEquals("a:q,b:23,va:w:e", TestClass.restVararg("q", 23, "w", "e"));
|
||||||
|
assertEquals("a:23,b:q,va:5:7", TestClass.restVararg(23, "q", 5, 7));
|
||||||
|
|
||||||
|
assertEquals("a:q,b:23,va", TestClass.restVararg("q", 23));
|
||||||
|
assertEquals("a:23,b:q,va", TestClass.restVararg(23, "q"));
|
||||||
|
|
||||||
|
var array = new String[3];
|
||||||
|
for (var i = 0; i < array.length; ++i) {
|
||||||
|
array[i] = String.valueOf((char) ('A' + i));
|
||||||
|
}
|
||||||
|
assertEquals("a:q,b:23,va:A:B:C", TestClass.restVararg("q", 23, array));
|
||||||
|
|
||||||
|
var intArray = new int[3];
|
||||||
|
for (var i = 0; i < array.length; ++i) {
|
||||||
|
intArray[i] = 6 + i;
|
||||||
|
}
|
||||||
|
assertEquals("a:23,b:q,va:6:7:8", TestClass.restVararg(23, "q", intArray));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSClass
|
||||||
|
public static class TestClass implements JSObject {
|
||||||
|
public static native String allVararg(String... args);
|
||||||
|
|
||||||
|
@JSMethod("allVararg")
|
||||||
|
public static native String allVarargInt(int... args);
|
||||||
|
|
||||||
|
public static native String restVararg(String a, int b, String... args);
|
||||||
|
|
||||||
|
public static native String restVararg(int a, String b, int... args);
|
||||||
|
}
|
||||||
|
}
|
33
tests/src/test/resources/org/teavm/jso/test/vararg.js
Normal file
33
tests/src/test/resources/org/teavm/jso/test/vararg.js
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class TestClass {
|
||||||
|
static allVararg(...args) {
|
||||||
|
let result = "va";
|
||||||
|
for (const arg of args) {
|
||||||
|
result += ":" + arg;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static restVararg(a, b, ...args) {
|
||||||
|
let result = `a:${a},b:${b},va`;
|
||||||
|
for (const arg of args) {
|
||||||
|
result += ":" + arg;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user