mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
JS: marshall JS object to Java in a wrapper
This commit is contained in:
parent
9438380716
commit
948244cbf4
|
@ -47,6 +47,7 @@ import org.teavm.interop.Unmanaged;
|
||||||
import org.teavm.jso.core.JSArray;
|
import org.teavm.jso.core.JSArray;
|
||||||
import org.teavm.platform.Platform;
|
import org.teavm.platform.Platform;
|
||||||
import org.teavm.platform.PlatformClass;
|
import org.teavm.platform.PlatformClass;
|
||||||
|
import org.teavm.platform.PlatformObject;
|
||||||
import org.teavm.platform.PlatformSequence;
|
import org.teavm.platform.PlatformSequence;
|
||||||
import org.teavm.runtime.RuntimeClass;
|
import org.teavm.runtime.RuntimeClass;
|
||||||
import org.teavm.runtime.RuntimeObject;
|
import org.teavm.runtime.RuntimeObject;
|
||||||
|
@ -402,7 +403,7 @@ public class TClass<T> extends TObject implements TAnnotatedElement, TType {
|
||||||
|
|
||||||
@InjectedBy(ClassGenerator.class)
|
@InjectedBy(ClassGenerator.class)
|
||||||
@PluggableDependency(ClassGenerator.class)
|
@PluggableDependency(ClassGenerator.class)
|
||||||
public native <T> T newEmptyInstance();
|
public native PlatformObject newEmptyInstance();
|
||||||
|
|
||||||
@SuppressWarnings({ "raw", "unchecked" })
|
@SuppressWarnings({ "raw", "unchecked" })
|
||||||
public TConstructor<?>[] getDeclaredConstructors() throws TSecurityException {
|
public TConstructor<?>[] getDeclaredConstructors() throws TSecurityException {
|
||||||
|
|
|
@ -23,8 +23,6 @@ import org.teavm.classlib.java.lang.TIllegalAccessException;
|
||||||
import org.teavm.classlib.java.lang.TIllegalArgumentException;
|
import org.teavm.classlib.java.lang.TIllegalArgumentException;
|
||||||
import org.teavm.classlib.java.lang.TInstantiationException;
|
import org.teavm.classlib.java.lang.TInstantiationException;
|
||||||
import org.teavm.classlib.java.lang.TObject;
|
import org.teavm.classlib.java.lang.TObject;
|
||||||
import org.teavm.platform.PlatformObject;
|
|
||||||
import org.teavm.platform.PlatformSequence;
|
|
||||||
|
|
||||||
public class TConstructor<T> extends TAccessibleObject implements TMember {
|
public class TConstructor<T> extends TAccessibleObject implements TMember {
|
||||||
private TClass<T> declaringClass;
|
private TClass<T> declaringClass;
|
||||||
|
@ -113,8 +111,8 @@ public class TConstructor<T> extends TAccessibleObject implements TMember {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PlatformSequence<PlatformObject> jsArgs = Converter.arrayFromJava(initargs);
|
var jsArgs = Converter.arrayFromJava(initargs);
|
||||||
PlatformObject instance = declaringClass.newEmptyInstance();
|
var instance = declaringClass.newEmptyInstance();
|
||||||
callable.call(instance, jsArgs);
|
callable.call(instance, jsArgs);
|
||||||
return (T) Converter.toJava(instance);
|
return (T) Converter.toJava(instance);
|
||||||
}
|
}
|
||||||
|
|
|
@ -233,6 +233,7 @@ abstract class AbstractInstructionAnalyzer extends AbstractInstructionReader {
|
||||||
if (className != null) {
|
if (className != null) {
|
||||||
getAnalyzer().linkClass(className);
|
getAnalyzer().linkClass(className);
|
||||||
}
|
}
|
||||||
|
getAnalyzer().linkClass("java.lang.ClassCastException");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -223,6 +223,7 @@ class DependencyGraphBuilder {
|
||||||
@Override
|
@Override
|
||||||
public void cast(VariableReader receiver, VariableReader value, ValueType targetType) {
|
public void cast(VariableReader receiver, VariableReader value, ValueType targetType) {
|
||||||
super.cast(receiver, value, targetType);
|
super.cast(receiver, value, targetType);
|
||||||
|
currentExceptionConsumer.consume(dependencyAnalyzer.getType("java.lang.ClassCastException"));
|
||||||
DependencyNode valueNode = nodes[value.getIndex()];
|
DependencyNode valueNode = nodes[value.getIndex()];
|
||||||
DependencyNode receiverNode = nodes[receiver.getIndex()];
|
DependencyNode receiverNode = nodes[receiver.getIndex()];
|
||||||
ClassReaderSource classSource = dependencyAnalyzer.getClassSource();
|
ClassReaderSource classSource = dependencyAnalyzer.getClassSource();
|
||||||
|
|
|
@ -23,7 +23,6 @@ import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.teavm.common.ServiceRepository;
|
import org.teavm.common.ServiceRepository;
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
import org.teavm.model.BasicBlockReader;
|
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
|
@ -32,7 +31,6 @@ import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ProgramReader;
|
import org.teavm.model.ProgramReader;
|
||||||
import org.teavm.model.ReferenceCache;
|
import org.teavm.model.ReferenceCache;
|
||||||
import org.teavm.model.TryCatchBlockReader;
|
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
|
|
||||||
public class FastDependencyAnalyzer extends DependencyAnalyzer {
|
public class FastDependencyAnalyzer extends DependencyAnalyzer {
|
||||||
|
@ -59,12 +57,12 @@ public class FastDependencyAnalyzer extends DependencyAnalyzer {
|
||||||
ProgramReader program = method.getProgram();
|
ProgramReader program = method.getProgram();
|
||||||
|
|
||||||
if (program != null) {
|
if (program != null) {
|
||||||
FastInstructionAnalyzer instructionAnalyzer = new FastInstructionAnalyzer(this);
|
var instructionAnalyzer = new FastInstructionAnalyzer(this);
|
||||||
instructionAnalyzer.setCaller(method.getReference());
|
instructionAnalyzer.setCaller(method.getReference());
|
||||||
for (BasicBlockReader block : program.getBasicBlocks()) {
|
for (var block : program.getBasicBlocks()) {
|
||||||
block.readAllInstructions(instructionAnalyzer);
|
block.readAllInstructions(instructionAnalyzer);
|
||||||
|
|
||||||
for (TryCatchBlockReader tryCatch : block.readTryCatchBlocks()) {
|
for (var tryCatch : block.readTryCatchBlocks()) {
|
||||||
if (tryCatch.getExceptionType() != null) {
|
if (tryCatch.getExceptionType() != null) {
|
||||||
linkClass(tryCatch.getExceptionType());
|
linkClass(tryCatch.getExceptionType());
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,342 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.model.analysis;
|
||||||
|
|
||||||
|
import com.carrotsearch.hppc.IntStack;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.teavm.common.Graph;
|
||||||
|
import org.teavm.common.GraphBuilder;
|
||||||
|
import org.teavm.model.InvokeDynamicInstruction;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.Variable;
|
||||||
|
import org.teavm.model.instructions.AbstractInstructionVisitor;
|
||||||
|
import org.teavm.model.instructions.ArrayLengthInstruction;
|
||||||
|
import org.teavm.model.instructions.AssignInstruction;
|
||||||
|
import org.teavm.model.instructions.BinaryInstruction;
|
||||||
|
import org.teavm.model.instructions.BoundCheckInstruction;
|
||||||
|
import org.teavm.model.instructions.CastInstruction;
|
||||||
|
import org.teavm.model.instructions.CastIntegerInstruction;
|
||||||
|
import org.teavm.model.instructions.CastNumberInstruction;
|
||||||
|
import org.teavm.model.instructions.ClassConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.CloneArrayInstruction;
|
||||||
|
import org.teavm.model.instructions.ConstructArrayInstruction;
|
||||||
|
import org.teavm.model.instructions.ConstructInstruction;
|
||||||
|
import org.teavm.model.instructions.ConstructMultiArrayInstruction;
|
||||||
|
import org.teavm.model.instructions.DoubleConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.FloatConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.GetElementInstruction;
|
||||||
|
import org.teavm.model.instructions.GetFieldInstruction;
|
||||||
|
import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.instructions.IsInstanceInstruction;
|
||||||
|
import org.teavm.model.instructions.LongConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.NegateInstruction;
|
||||||
|
import org.teavm.model.instructions.NullCheckInstruction;
|
||||||
|
import org.teavm.model.instructions.NullConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.NumericOperandType;
|
||||||
|
import org.teavm.model.instructions.StringConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.UnwrapArrayInstruction;
|
||||||
|
|
||||||
|
public abstract class BaseTypeInference<T> {
|
||||||
|
private Program program;
|
||||||
|
private MethodReference reference;
|
||||||
|
private Object[] types;
|
||||||
|
private Graph graph;
|
||||||
|
private Graph arrayGraph;
|
||||||
|
|
||||||
|
public BaseTypeInference(Program program, MethodReference reference) {
|
||||||
|
this.program = program;
|
||||||
|
this.reference = reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepare() {
|
||||||
|
types = new Object[program.variableCount()];
|
||||||
|
var visitor = new InitialTypeVisitor(program.variableCount());
|
||||||
|
var params = Math.min(reference.parameterCount(), program.variableCount() - 1);
|
||||||
|
for (var i = 0; i < params; ++i) {
|
||||||
|
visitor.type(program.variableAt(i + 1), reference.parameterType(i));
|
||||||
|
}
|
||||||
|
visitor.type(program.variableAt(0), ValueType.object(reference.getClassName()));
|
||||||
|
for (var block : program.getBasicBlocks()) {
|
||||||
|
for (var insn : block) {
|
||||||
|
insn.acceptVisitor(visitor);
|
||||||
|
}
|
||||||
|
for (var phi : block.getPhis()) {
|
||||||
|
for (var incoming : phi.getIncomings()) {
|
||||||
|
visitor.graphBuilder.addEdge(incoming.getValue().getIndex(), phi.getReceiver().getIndex());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
graph = visitor.graphBuilder.build();
|
||||||
|
arrayGraph = visitor.arrayGraphBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void propagate() {
|
||||||
|
var stack = new IntStack();
|
||||||
|
var typeStack = new ArrayDeque<T>();
|
||||||
|
for (var i = 0; i < types.length; ++i) {
|
||||||
|
if (types[i] != null) {
|
||||||
|
stack.push(i);
|
||||||
|
typeStack.push((T) types[i]);
|
||||||
|
types[i] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (!stack.isEmpty()) {
|
||||||
|
var variable = stack.pop();
|
||||||
|
var type = typeStack.pop();
|
||||||
|
var formerType = (T) types[variable];
|
||||||
|
if (Objects.equals(formerType, type)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
type = doMerge(type, formerType);
|
||||||
|
if (Objects.equals(type, formerType)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
types[variable] = type;
|
||||||
|
for (var succ : graph.outgoingEdges(variable)) {
|
||||||
|
if (!Objects.equals(types[succ], type)) {
|
||||||
|
stack.push(succ);
|
||||||
|
typeStack.push(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (arrayGraph.outgoingEdgesCount(variable) > 0) {
|
||||||
|
var elementType = elementType(type);
|
||||||
|
for (var succ : arrayGraph.outgoingEdges(variable)) {
|
||||||
|
if (!Objects.equals(types[succ], elementType)) {
|
||||||
|
stack.push(succ);
|
||||||
|
typeStack.push(elementType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ensure() {
|
||||||
|
if (types == null) {
|
||||||
|
prepare();
|
||||||
|
propagate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public T typeOf(Variable variable) {
|
||||||
|
ensure();
|
||||||
|
return (T) types[variable.getIndex()];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected abstract T mapType(ValueType type);
|
||||||
|
|
||||||
|
protected abstract T nullType();
|
||||||
|
|
||||||
|
private T doMerge(T a, T b) {
|
||||||
|
if (a == null) {
|
||||||
|
return b;
|
||||||
|
} else if (b == null) {
|
||||||
|
return a;
|
||||||
|
} else {
|
||||||
|
return merge(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract T merge(T a, T b);
|
||||||
|
|
||||||
|
protected abstract T elementType(T t);
|
||||||
|
|
||||||
|
private class InitialTypeVisitor extends AbstractInstructionVisitor {
|
||||||
|
private GraphBuilder graphBuilder;
|
||||||
|
private GraphBuilder arrayGraphBuilder;
|
||||||
|
|
||||||
|
InitialTypeVisitor(int size) {
|
||||||
|
graphBuilder = new GraphBuilder(size);
|
||||||
|
arrayGraphBuilder = new GraphBuilder(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(NullConstantInstruction insn) {
|
||||||
|
types[insn.getReceiver().getIndex()] = nullType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(IntegerConstantInstruction insn) {
|
||||||
|
type(insn.getReceiver(), ValueType.INTEGER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(LongConstantInstruction insn) {
|
||||||
|
type(insn.getReceiver(), ValueType.LONG);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(FloatConstantInstruction insn) {
|
||||||
|
type(insn.getReceiver(), ValueType.FLOAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(DoubleConstantInstruction insn) {
|
||||||
|
type(insn.getReceiver(), ValueType.DOUBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ClassConstantInstruction insn) {
|
||||||
|
type(insn.getReceiver(), ValueType.object("java/lang/Class"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(StringConstantInstruction insn) {
|
||||||
|
type(insn.getReceiver(), ValueType.object("java/lang/String"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ConstructInstruction insn) {
|
||||||
|
type(insn.getReceiver(), ValueType.object(insn.getType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ConstructArrayInstruction insn) {
|
||||||
|
type(insn.getReceiver(), ValueType.arrayOf(insn.getItemType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ConstructMultiArrayInstruction insn) {
|
||||||
|
var type = insn.getItemType();
|
||||||
|
for (var i = 0; i < insn.getDimensions().size(); ++i) {
|
||||||
|
type = ValueType.arrayOf(type);
|
||||||
|
}
|
||||||
|
type(insn.getReceiver(), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(IsInstanceInstruction insn) {
|
||||||
|
type(insn.getReceiver(), ValueType.BOOLEAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(CastInstruction insn) {
|
||||||
|
type(insn.getReceiver(), insn.getTargetType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(NegateInstruction insn) {
|
||||||
|
type(insn.getReceiver(), insn.getOperandType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(CastNumberInstruction insn) {
|
||||||
|
type(insn.getReceiver(), insn.getTargetType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(BinaryInstruction insn) {
|
||||||
|
type(insn.getReceiver(), insn.getOperandType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(CastIntegerInstruction insn) {
|
||||||
|
switch (insn.getTargetType()) {
|
||||||
|
case BYTE:
|
||||||
|
type(insn.getReceiver(), ValueType.BYTE);
|
||||||
|
break;
|
||||||
|
case CHAR:
|
||||||
|
type(insn.getReceiver(), ValueType.CHARACTER);
|
||||||
|
break;
|
||||||
|
case SHORT:
|
||||||
|
type(insn.getReceiver(), ValueType.SHORT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ArrayLengthInstruction insn) {
|
||||||
|
type(insn.getReceiver(), ValueType.INTEGER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(CloneArrayInstruction insn) {
|
||||||
|
graphBuilder.addEdge(insn.getArray().getIndex(), insn.getReceiver().getIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(BoundCheckInstruction insn) {
|
||||||
|
type(insn.getReceiver(), ValueType.INTEGER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(InvokeInstruction insn) {
|
||||||
|
type(insn.getReceiver(), insn.getMethod().getReturnType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(InvokeDynamicInstruction insn) {
|
||||||
|
type(insn.getReceiver(), insn.getMethod().getResultType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(GetFieldInstruction insn) {
|
||||||
|
type(insn.getReceiver(), insn.getFieldType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(UnwrapArrayInstruction insn) {
|
||||||
|
graphBuilder.addEdge(insn.getArray().getIndex(), insn.getReceiver().getIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(GetElementInstruction insn) {
|
||||||
|
arrayGraphBuilder.addEdge(insn.getArray().getIndex(), insn.getReceiver().getIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(AssignInstruction insn) {
|
||||||
|
graphBuilder.addEdge(insn.getAssignee().getIndex(), insn.getReceiver().getIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(NullCheckInstruction insn) {
|
||||||
|
graphBuilder.addEdge(insn.getValue().getIndex(), insn.getReceiver().getIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
void type(Variable target, NumericOperandType type) {
|
||||||
|
switch (type) {
|
||||||
|
case INT:
|
||||||
|
type(target, ValueType.INTEGER);
|
||||||
|
break;
|
||||||
|
case LONG:
|
||||||
|
type(target, ValueType.LONG);
|
||||||
|
break;
|
||||||
|
case FLOAT:
|
||||||
|
type(target, ValueType.FLOAT);
|
||||||
|
break;
|
||||||
|
case DOUBLE:
|
||||||
|
type(target, ValueType.DOUBLE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void type(Variable target, ValueType type) {
|
||||||
|
if (target != null) {
|
||||||
|
var t = mapType(type);
|
||||||
|
if (t != null) {
|
||||||
|
types[target.getIndex()] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -111,7 +111,7 @@ public class ValueEmitter {
|
||||||
String className = ((ValueType.Object) type).getClassName();
|
String className = ((ValueType.Object) type).getClassName();
|
||||||
PutFieldInstruction insn = new PutFieldInstruction();
|
PutFieldInstruction insn = new PutFieldInstruction();
|
||||||
insn.setField(new FieldReference(className, name));
|
insn.setField(new FieldReference(className, name));
|
||||||
insn.setFieldType(type);
|
insn.setFieldType(value.type);
|
||||||
insn.setInstance(variable);
|
insn.setInstance(variable);
|
||||||
insn.setValue(value.getVariable());
|
insn.setValue(value.getVariable());
|
||||||
pe.addInstruction(insn);
|
pe.addInstruction(insn);
|
||||||
|
|
|
@ -15,24 +15,12 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.model.optimization;
|
package org.teavm.model.optimization;
|
||||||
|
|
||||||
import com.carrotsearch.hppc.IntHashSet;
|
import java.util.Objects;
|
||||||
import com.carrotsearch.hppc.IntStack;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import org.teavm.model.MethodDescriptor;
|
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.Program;
|
import org.teavm.model.Program;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
import org.teavm.model.Variable;
|
import org.teavm.model.analysis.BaseTypeInference;
|
||||||
import org.teavm.model.instructions.AbstractInstructionVisitor;
|
|
||||||
import org.teavm.model.instructions.AssignInstruction;
|
|
||||||
import org.teavm.model.instructions.CastInstruction;
|
|
||||||
import org.teavm.model.instructions.ConstructArrayInstruction;
|
|
||||||
import org.teavm.model.instructions.ConstructMultiArrayInstruction;
|
|
||||||
import org.teavm.model.instructions.GetElementInstruction;
|
|
||||||
import org.teavm.model.instructions.GetFieldInstruction;
|
|
||||||
import org.teavm.model.instructions.InvokeInstruction;
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
import org.teavm.model.instructions.NullCheckInstruction;
|
|
||||||
import org.teavm.model.instructions.UnwrapArrayInstruction;
|
|
||||||
|
|
||||||
public class SystemArrayCopyOptimization implements MethodOptimization {
|
public class SystemArrayCopyOptimization implements MethodOptimization {
|
||||||
private static final MethodReference ARRAY_COPY_METHOD = new MethodReference(System.class,
|
private static final MethodReference ARRAY_COPY_METHOD = new MethodReference(System.class,
|
||||||
|
@ -51,7 +39,7 @@ public class SystemArrayCopyOptimization implements MethodOptimization {
|
||||||
var method = invoke.getMethod();
|
var method = invoke.getMethod();
|
||||||
if (method.equals(ARRAY_COPY_METHOD)) {
|
if (method.equals(ARRAY_COPY_METHOD)) {
|
||||||
if (typeInference == null) {
|
if (typeInference == null) {
|
||||||
typeInference = new TypeInference(program, context.getMethod().getDescriptor());
|
typeInference = new TypeInference(program, context.getMethod().getReference());
|
||||||
}
|
}
|
||||||
var sourceType = typeInference.typeOf(invoke.getArguments().get(0));
|
var sourceType = typeInference.typeOf(invoke.getArguments().get(0));
|
||||||
var destType = typeInference.typeOf(invoke.getArguments().get(2));
|
var destType = typeInference.typeOf(invoke.getArguments().get(2));
|
||||||
|
@ -69,182 +57,32 @@ public class SystemArrayCopyOptimization implements MethodOptimization {
|
||||||
return somethingChanged;
|
return somethingChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class TypeInference {
|
private static class TypeInference extends BaseTypeInference<ValueType> {
|
||||||
private ValueType[] types;
|
TypeInference(Program program, MethodReference reference) {
|
||||||
private int[] assignments;
|
super(program, reference);
|
||||||
private int[] elementAssignments;
|
|
||||||
private int[][] phis;
|
|
||||||
private boolean[] present;
|
|
||||||
private boolean[] calculating;
|
|
||||||
|
|
||||||
TypeInference(Program program, MethodDescriptor descriptor) {
|
|
||||||
types = new ValueType[program.variableCount()];
|
|
||||||
assignments = new int[program.variableCount()];
|
|
||||||
elementAssignments = new int[program.variableCount()];
|
|
||||||
phis = new int[program.variableCount()][];
|
|
||||||
Arrays.fill(assignments, -1);
|
|
||||||
Arrays.fill(elementAssignments, -1);
|
|
||||||
present = new boolean[program.variableCount()];
|
|
||||||
calculating = new boolean[program.variableCount()];
|
|
||||||
|
|
||||||
var visitor = new InitialTypeVisitor(types, assignments, elementAssignments);
|
|
||||||
var params = Math.min(descriptor.parameterCount(), program.variableCount() - 1);
|
|
||||||
for (var i = 0; i < params; ++i) {
|
|
||||||
visitor.type(program.variableAt(i + 1), descriptor.parameterType(i));
|
|
||||||
}
|
|
||||||
for (var block : program.getBasicBlocks()) {
|
|
||||||
for (var insn : block) {
|
|
||||||
insn.acceptVisitor(visitor);
|
|
||||||
}
|
|
||||||
for (var phi : block.getPhis()) {
|
|
||||||
var sourceIndexes = new IntHashSet();
|
|
||||||
for (var incoming : phi.getIncomings()) {
|
|
||||||
sourceIndexes.add(incoming.getValue().getIndex());
|
|
||||||
}
|
|
||||||
var inputs = sourceIndexes.toArray();
|
|
||||||
Arrays.sort(inputs);
|
|
||||||
phis[phi.getReceiver().getIndex()] = inputs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < types.length; ++i) {
|
|
||||||
if (types[i] != null) {
|
|
||||||
present[i] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ValueType typeOf(Variable variable) {
|
|
||||||
if (!present[variable.getIndex()]) {
|
|
||||||
calculate(variable.getIndex());
|
|
||||||
}
|
|
||||||
return types[variable.getIndex()];
|
|
||||||
}
|
|
||||||
|
|
||||||
private void calculate(int initialVariable) {
|
|
||||||
var stack = new IntStack();
|
|
||||||
stack.push(initialVariable);
|
|
||||||
while (!stack.isEmpty()) {
|
|
||||||
var variable = stack.pop();
|
|
||||||
if (calculating[variable]) {
|
|
||||||
calculating[variable] = false;
|
|
||||||
present[variable] = true;
|
|
||||||
var inputs = phis[variable];
|
|
||||||
ValueType type;
|
|
||||||
if (inputs != null) {
|
|
||||||
type = null;
|
|
||||||
var initialized = false;
|
|
||||||
for (var input : inputs) {
|
|
||||||
if (calculating[input]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (initialized) {
|
|
||||||
if (!type.equals(types[input])) {
|
|
||||||
type = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
type = types[input];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (assignments[variable] >= 0) {
|
|
||||||
type = types[assignments[variable]];
|
|
||||||
} else if (elementAssignments[variable] >= 0) {
|
|
||||||
type = types[elementAssignments[variable]];
|
|
||||||
type = type instanceof ValueType.Array ? ((ValueType.Array) type).getItemType() : null;
|
|
||||||
} else {
|
|
||||||
type = null;
|
|
||||||
}
|
|
||||||
types[variable] = type;
|
|
||||||
} else {
|
|
||||||
calculating[variable] = true;
|
|
||||||
stack.push(variable);
|
|
||||||
var inputs = phis[variable];
|
|
||||||
if (inputs != null) {
|
|
||||||
for (var input : inputs) {
|
|
||||||
if (!calculating[input] && !present[input]) {
|
|
||||||
stack.push(input);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var assign = assignments[variable];
|
|
||||||
if (assign >= 0 && !present[assign]) {
|
|
||||||
stack.push(assign);
|
|
||||||
}
|
|
||||||
var elemAssign = elementAssignments[variable];
|
|
||||||
if (elemAssign >= 0 && !present[elemAssign]) {
|
|
||||||
stack.push(elemAssign);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class InitialTypeVisitor extends AbstractInstructionVisitor {
|
|
||||||
private ValueType[] types;
|
|
||||||
private int[] assignments;
|
|
||||||
private int[] elementAssignments;
|
|
||||||
|
|
||||||
InitialTypeVisitor(ValueType[] types, int[] assignments, int[] elementAssignments) {
|
|
||||||
this.types = types;
|
|
||||||
this.assignments = assignments;
|
|
||||||
this.elementAssignments = elementAssignments;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(ConstructArrayInstruction insn) {
|
public ValueType merge(ValueType a, ValueType b) {
|
||||||
types[insn.getReceiver().getIndex()] = insn.getItemType();
|
if (!Objects.equals(a, b)) {
|
||||||
super.visit(insn);
|
return null;
|
||||||
|
}
|
||||||
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(ConstructMultiArrayInstruction insn) {
|
public ValueType elementType(ValueType valueType) {
|
||||||
var type = insn.getItemType();
|
return valueType instanceof ValueType.Array ? ((ValueType.Array) valueType).getItemType() : null;
|
||||||
for (var i = 1; i < insn.getDimensions().size(); ++i) {
|
|
||||||
type = ValueType.arrayOf(type);
|
|
||||||
}
|
|
||||||
types[insn.getReceiver().getIndex()] = insn.getItemType();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(CastInstruction insn) {
|
public ValueType nullType() {
|
||||||
type(insn.getReceiver(), insn.getTargetType());
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(InvokeInstruction insn) {
|
public ValueType mapType(ValueType type) {
|
||||||
type(insn.getReceiver(), insn.getMethod().getReturnType());
|
return type instanceof ValueType.Array ? ((ValueType.Array) type).getItemType() : null;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(GetFieldInstruction insn) {
|
|
||||||
type(insn.getReceiver(), insn.getFieldType());
|
|
||||||
}
|
|
||||||
|
|
||||||
void type(Variable target, ValueType type) {
|
|
||||||
if (target != null && type instanceof ValueType.Array) {
|
|
||||||
types[target.getIndex()] = ((ValueType.Array) type).getItemType();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(UnwrapArrayInstruction insn) {
|
|
||||||
assignments[insn.getReceiver().getIndex()] = insn.getArray().getIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(GetElementInstruction insn) {
|
|
||||||
elementAssignments[insn.getReceiver().getIndex()] = insn.getArray().getIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(AssignInstruction insn) {
|
|
||||||
assignments[insn.getReceiver().getIndex()] = insn.getAssignee().getIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(NullCheckInstruction insn) {
|
|
||||||
assignments[insn.getReceiver().getIndex()] = insn.getValue().getIndex();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ function $rt_compare(a, b) {
|
||||||
return a > b ? 1 : a < b ? -1 : a === b ? 0 : 1;
|
return a > b ? 1 : a < b ? -1 : a === b ? 0 : 1;
|
||||||
}
|
}
|
||||||
function $rt_isInstance(obj, cls) {
|
function $rt_isInstance(obj, cls) {
|
||||||
return obj !== null && !!obj.constructor.$meta && $rt_isAssignable(obj.constructor, cls);
|
return obj instanceof $rt_objcls() && !!obj.constructor.$meta && $rt_isAssignable(obj.constructor, cls);
|
||||||
}
|
}
|
||||||
function $rt_isAssignable(from, to) {
|
function $rt_isAssignable(from, to) {
|
||||||
if (from === to) {
|
if (from === to) {
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.core;
|
||||||
|
|
||||||
|
import org.teavm.jso.JSBody;
|
||||||
|
import org.teavm.jso.JSObject;
|
||||||
|
|
||||||
|
public abstract class JSFinalizationRegistry implements JSObject {
|
||||||
|
public abstract void register(JSObject obj, JSObject token);
|
||||||
|
|
||||||
|
@JSBody(params = "consumer", script = "return new FinalizationRegistry(consumer);")
|
||||||
|
public static native JSFinalizationRegistry create(JSFinalizationRegistryConsumer consumer);
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.core;
|
||||||
|
|
||||||
|
import org.teavm.jso.JSFunctor;
|
||||||
|
import org.teavm.jso.JSObject;
|
||||||
|
|
||||||
|
@JSFunctor
|
||||||
|
public interface JSFinalizationRegistryConsumer extends JSObject {
|
||||||
|
void accept(JSObject obj);
|
||||||
|
}
|
36
jso/apis/src/main/java/org/teavm/jso/core/JSMap.java
Normal file
36
jso/apis/src/main/java/org/teavm/jso/core/JSMap.java
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 konsoletyper.
|
||||||
|
*
|
||||||
|
* 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.core;
|
||||||
|
|
||||||
|
import org.teavm.interop.NoSideEffects;
|
||||||
|
import org.teavm.jso.JSBody;
|
||||||
|
import org.teavm.jso.JSObject;
|
||||||
|
|
||||||
|
public abstract class JSMap<K extends JSObject, V extends JSObject> implements JSObject {
|
||||||
|
public abstract V get(K key);
|
||||||
|
|
||||||
|
public abstract boolean has(K key);
|
||||||
|
|
||||||
|
public abstract JSMap<K, V> set(K key, V value);
|
||||||
|
|
||||||
|
public abstract boolean delete(K key);
|
||||||
|
|
||||||
|
public abstract void clear();
|
||||||
|
|
||||||
|
@JSBody(script = "return new Map();")
|
||||||
|
@NoSideEffects
|
||||||
|
public static native <K extends JSObject, V extends JSObject> JSMap<K, V> create();
|
||||||
|
}
|
|
@ -61,4 +61,8 @@ public final class JSObjects {
|
||||||
@JSBody(params = { "object", "name" }, script = "return name in object;")
|
@JSBody(params = { "object", "name" }, script = "return name in object;")
|
||||||
@NoSideEffects
|
@NoSideEffects
|
||||||
public static native boolean hasProperty(JSObject object, String name);
|
public static native boolean hasProperty(JSObject object, String name);
|
||||||
|
|
||||||
|
@JSBody(params = "object", script = "return Object.getPrototypeOf(object);")
|
||||||
|
@NoSideEffects
|
||||||
|
public static native JSObject getPrototypeOf(JSObject object);
|
||||||
}
|
}
|
||||||
|
|
32
jso/apis/src/main/java/org/teavm/jso/core/JSSymbol.java
Normal file
32
jso/apis/src/main/java/org/teavm/jso/core/JSSymbol.java
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 konsoletyper.
|
||||||
|
*
|
||||||
|
* 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.core;
|
||||||
|
|
||||||
|
import org.teavm.interop.NoSideEffects;
|
||||||
|
import org.teavm.jso.JSBody;
|
||||||
|
import org.teavm.jso.JSObject;
|
||||||
|
|
||||||
|
public abstract class JSSymbol<T extends JSObject> implements JSObject {
|
||||||
|
@JSBody(params = "name", script = "return Symbol(name);")
|
||||||
|
public static native <T extends JSObject> JSSymbol<T> create(String name);
|
||||||
|
|
||||||
|
@JSBody(params = "obj", script = "return obj[this];")
|
||||||
|
public native T get(JSObject obj);
|
||||||
|
|
||||||
|
@JSBody(params = { "obj", "value" }, script = "obj[this] = value;")
|
||||||
|
@NoSideEffects
|
||||||
|
public native void set(JSObject obj, T value);
|
||||||
|
}
|
34
jso/apis/src/main/java/org/teavm/jso/core/JSWeakMap.java
Normal file
34
jso/apis/src/main/java/org/teavm/jso/core/JSWeakMap.java
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 konsoletyper.
|
||||||
|
*
|
||||||
|
* 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.core;
|
||||||
|
|
||||||
|
import org.teavm.interop.NoSideEffects;
|
||||||
|
import org.teavm.jso.JSBody;
|
||||||
|
import org.teavm.jso.JSObject;
|
||||||
|
|
||||||
|
public abstract class JSWeakMap<K extends JSObject, V extends JSObject> implements JSObject {
|
||||||
|
public abstract V get(K key);
|
||||||
|
|
||||||
|
public abstract boolean has(K key);
|
||||||
|
|
||||||
|
public abstract JSWeakMap<K, V> set(K key, V value);
|
||||||
|
|
||||||
|
public abstract boolean remove(K key);
|
||||||
|
|
||||||
|
@JSBody(script = "return new WeakMap();")
|
||||||
|
@NoSideEffects
|
||||||
|
public static native <K extends JSObject, V extends JSObject> JSWeakMap<K, V> create();
|
||||||
|
}
|
32
jso/apis/src/main/java/org/teavm/jso/core/JSWeakRef.java
Normal file
32
jso/apis/src/main/java/org/teavm/jso/core/JSWeakRef.java
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 konsoletyper.
|
||||||
|
*
|
||||||
|
* 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.core;
|
||||||
|
|
||||||
|
import org.teavm.interop.NoSideEffects;
|
||||||
|
import org.teavm.jso.JSBody;
|
||||||
|
import org.teavm.jso.JSObject;
|
||||||
|
|
||||||
|
public abstract class JSWeakRef<T extends JSObject> implements JSObject {
|
||||||
|
public abstract T deref();
|
||||||
|
|
||||||
|
@JSBody(params = "value", script = "return new WeakRef(value);")
|
||||||
|
@NoSideEffects
|
||||||
|
public static native <T extends JSObject> JSWeakRef<T> create(T value);
|
||||||
|
|
||||||
|
@JSBody(script = "return typeof WeakRef !== 'undefined';")
|
||||||
|
@NoSideEffects
|
||||||
|
public static native boolean isSupported();
|
||||||
|
}
|
|
@ -16,7 +16,6 @@
|
||||||
package org.teavm.jso.impl;
|
package org.teavm.jso.impl;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.util.function.Function;
|
|
||||||
import org.teavm.backend.javascript.spi.GeneratedBy;
|
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||||
import org.teavm.backend.javascript.spi.InjectedBy;
|
import org.teavm.backend.javascript.spi.InjectedBy;
|
||||||
import org.teavm.dependency.PluggableDependency;
|
import org.teavm.dependency.PluggableDependency;
|
||||||
|
@ -148,11 +147,11 @@ final class JS {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends JSObject> Function<T[], JSArray<T>> arrayWrapper() {
|
public static <T extends JSObject> WrapFunction<T[], JSArray<T>> arrayWrapper() {
|
||||||
return JS::wrap;
|
return JS::wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends JSObject, S> JSArray<T> map(S[] array, Function<S, T> f) {
|
public static <T extends JSObject, S> JSArray<T> map(S[] array, WrapFunction<S, T> f) {
|
||||||
if (array == null) {
|
if (array == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -163,7 +162,15 @@ final class JS {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends JSObject, S> Function<S[], JSArray<T>> arrayMapper(Function<S, T> f) {
|
public interface WrapFunction<S, T extends JSObject> {
|
||||||
|
T apply(S obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface UnwrapFunction<S extends JSObject, T> {
|
||||||
|
T apply(S obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends JSObject, S> WrapFunction<S[], JSArray<T>> arrayMapper(WrapFunction<S, T> f) {
|
||||||
return array -> map(array, f);
|
return array -> map(array, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,7 +185,7 @@ final class JS {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Function<boolean[], JSArray<JSBoolean>> booleanArrayWrapper() {
|
public static WrapFunction<boolean[], JSArray<JSBoolean>> booleanArrayWrapper() {
|
||||||
return JS::wrap;
|
return JS::wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +200,7 @@ final class JS {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Function<byte[], JSArray<JSNumber>> byteArrayWrapper() {
|
public static WrapFunction<byte[], JSArray<JSNumber>> byteArrayWrapper() {
|
||||||
return JS::wrap;
|
return JS::wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,7 +215,7 @@ final class JS {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Function<short[], JSArray<JSNumber>> shortArrayWrapper() {
|
public static WrapFunction<short[], JSArray<JSNumber>> shortArrayWrapper() {
|
||||||
return JS::wrap;
|
return JS::wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,7 +230,7 @@ final class JS {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Function<char[], JSArray<JSNumber>> charArrayWrapper() {
|
public static WrapFunction<char[], JSArray<JSNumber>> charArrayWrapper() {
|
||||||
return JS::wrap;
|
return JS::wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,7 +245,7 @@ final class JS {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Function<int[], JSArray<JSNumber>> intArrayWrapper() {
|
public static WrapFunction<int[], JSArray<JSNumber>> intArrayWrapper() {
|
||||||
return JS::wrap;
|
return JS::wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,7 +260,7 @@ final class JS {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Function<String[], JSArray<JSString>> stringArrayWrapper() {
|
public static WrapFunction<String[], JSArray<JSString>> stringArrayWrapper() {
|
||||||
return JS::wrap;
|
return JS::wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,7 +275,7 @@ final class JS {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Function<float[], JSArray<JSNumber>> floatArrayWrapper() {
|
public static WrapFunction<float[], JSArray<JSNumber>> floatArrayWrapper() {
|
||||||
return JS::wrap;
|
return JS::wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,7 +290,7 @@ final class JS {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Function<double[], JSArray<JSNumber>> doubleArrayWrapper() {
|
public static WrapFunction<double[], JSArray<JSNumber>> doubleArrayWrapper() {
|
||||||
return JS::wrap;
|
return JS::wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,11 +306,12 @@ final class JS {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends JSObject> Function<JSArrayReader<T>, T[]> arrayUnwrapper(Class<T> type) {
|
public static <T extends JSObject> UnwrapFunction<JSArrayReader<T>, T[]> arrayUnwrapper(Class<T> type) {
|
||||||
return array -> unwrapArray(type, array);
|
return array -> unwrapArray(type, array);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <S extends JSObject, T> T[] unmapArray(Class<T> type, JSArrayReader<S> array, Function<S, T> f) {
|
public static <S extends JSObject, T> T[] unmapArray(Class<T> type, JSArrayReader<S> array,
|
||||||
|
UnwrapFunction<S, T> f) {
|
||||||
if (array == null) {
|
if (array == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -315,7 +323,8 @@ final class JS {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T, S extends JSObject> Function<JSArray<S>, T[]> arrayUnmapper(Class<T> type, Function<S, T> f) {
|
public static <T, S extends JSObject> UnwrapFunction<JSArray<S>, T[]> arrayUnmapper(Class<T> type,
|
||||||
|
UnwrapFunction<S, T> f) {
|
||||||
return array -> unmapArray(type, array, f);
|
return array -> unmapArray(type, array, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,7 +339,7 @@ final class JS {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Function<JSArrayReader<JSBoolean>, boolean[]> booleanArrayUnwrapper() {
|
public static UnwrapFunction<JSArrayReader<JSBoolean>, boolean[]> booleanArrayUnwrapper() {
|
||||||
return JS::unwrapBooleanArray;
|
return JS::unwrapBooleanArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,7 +354,7 @@ final class JS {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Function<JSArrayReader<JSNumber>, byte[]> byteArrayUnwrapper() {
|
public static UnwrapFunction<JSArrayReader<JSNumber>, byte[]> byteArrayUnwrapper() {
|
||||||
return JS::unwrapByteArray;
|
return JS::unwrapByteArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,7 +369,7 @@ final class JS {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Function<JSArrayReader<JSNumber>, short[]> shortArrayUnwrapper() {
|
public static UnwrapFunction<JSArrayReader<JSNumber>, short[]> shortArrayUnwrapper() {
|
||||||
return JS::unwrapShortArray;
|
return JS::unwrapShortArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,7 +384,7 @@ final class JS {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Function<JSArrayReader<JSNumber>, int[]> intArrayUnwrapper() {
|
public static UnwrapFunction<JSArrayReader<JSNumber>, int[]> intArrayUnwrapper() {
|
||||||
return JS::unwrapIntArray;
|
return JS::unwrapIntArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,7 +399,7 @@ final class JS {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Function<JSArrayReader<JSNumber>, char[]> charArrayUnwrapper() {
|
public static UnwrapFunction<JSArrayReader<JSNumber>, char[]> charArrayUnwrapper() {
|
||||||
return JS::unwrapCharArray;
|
return JS::unwrapCharArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,7 +414,7 @@ final class JS {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Function<JSArrayReader<JSNumber>, float[]> floatArrayUnwrapper() {
|
public static UnwrapFunction<JSArrayReader<JSNumber>, float[]> floatArrayUnwrapper() {
|
||||||
return JS::unwrapFloatArray;
|
return JS::unwrapFloatArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,7 +429,7 @@ final class JS {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Function<JSArrayReader<JSNumber>, double[]> doubleArrayUnwrapper() {
|
public static UnwrapFunction<JSArrayReader<JSNumber>, double[]> doubleArrayUnwrapper() {
|
||||||
return JS::unwrapDoubleArray;
|
return JS::unwrapDoubleArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,7 +444,7 @@ final class JS {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Function<JSArrayReader<JSString>, String[]> stringArrayUnwrapper() {
|
public static UnwrapFunction<JSArrayReader<JSString>, String[]> stringArrayUnwrapper() {
|
||||||
return JS::unwrapStringArray;
|
return JS::unwrapStringArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,21 +58,40 @@ import org.teavm.model.Program;
|
||||||
import org.teavm.model.TextLocation;
|
import org.teavm.model.TextLocation;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
import org.teavm.model.Variable;
|
import org.teavm.model.Variable;
|
||||||
|
import org.teavm.model.instructions.ArrayElementType;
|
||||||
import org.teavm.model.instructions.AssignInstruction;
|
import org.teavm.model.instructions.AssignInstruction;
|
||||||
import org.teavm.model.instructions.CastInstruction;
|
import org.teavm.model.instructions.CastInstruction;
|
||||||
|
import org.teavm.model.instructions.ClassConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.ConstructArrayInstruction;
|
||||||
import org.teavm.model.instructions.ExitInstruction;
|
import org.teavm.model.instructions.ExitInstruction;
|
||||||
|
import org.teavm.model.instructions.GetElementInstruction;
|
||||||
|
import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||||
import org.teavm.model.instructions.InvocationType;
|
import org.teavm.model.instructions.InvocationType;
|
||||||
import org.teavm.model.instructions.InvokeInstruction;
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.instructions.IsInstanceInstruction;
|
||||||
|
import org.teavm.model.instructions.PutElementInstruction;
|
||||||
|
import org.teavm.model.instructions.PutFieldInstruction;
|
||||||
import org.teavm.model.util.InstructionVariableMapper;
|
import org.teavm.model.util.InstructionVariableMapper;
|
||||||
import org.teavm.model.util.ModelUtils;
|
import org.teavm.model.util.ModelUtils;
|
||||||
import org.teavm.model.util.ProgramUtils;
|
import org.teavm.model.util.ProgramUtils;
|
||||||
|
|
||||||
class JSClassProcessor {
|
class JSClassProcessor {
|
||||||
private static final String NO_SIDE_EFFECTS = NoSideEffects.class.getName();
|
private static final String NO_SIDE_EFFECTS = NoSideEffects.class.getName();
|
||||||
|
private static final MethodReference WRAP = new MethodReference(JSWrapper.class, "wrap", Object.class,
|
||||||
|
Object.class);
|
||||||
|
private static final MethodReference MAYBE_WRAP = new MethodReference(JSWrapper.class, "maybeWrap", Object.class,
|
||||||
|
Object.class);
|
||||||
|
private static final MethodReference UNWRAP = new MethodReference(JSWrapper.class, "unwrap", Object.class,
|
||||||
|
JSObject.class);
|
||||||
|
private static final MethodReference MAYBE_UNWRAP = new MethodReference(JSWrapper.class, "maybeUnwrap",
|
||||||
|
Object.class, JSObject.class);
|
||||||
|
private static final MethodReference IS_JS = new MethodReference(JSWrapper.class, "isJs",
|
||||||
|
Object.class, boolean.class);
|
||||||
private final ClassReaderSource classSource;
|
private final ClassReaderSource classSource;
|
||||||
private final JSBodyRepository repository;
|
private final JSBodyRepository repository;
|
||||||
private final JavaInvocationProcessor javaInvocationProcessor;
|
private final JavaInvocationProcessor javaInvocationProcessor;
|
||||||
private Program program;
|
private Program program;
|
||||||
|
private JSTypeInference types;
|
||||||
private final List<Instruction> replacement = new ArrayList<>();
|
private final List<Instruction> replacement = new ArrayList<>();
|
||||||
private final JSTypeHelper typeHelper;
|
private final JSTypeHelper typeHelper;
|
||||||
private final Diagnostics diagnostics;
|
private final Diagnostics diagnostics;
|
||||||
|
@ -206,37 +225,157 @@ class JSClassProcessor {
|
||||||
|
|
||||||
void processProgram(MethodHolder methodToProcess) {
|
void processProgram(MethodHolder methodToProcess) {
|
||||||
setCurrentProgram(methodToProcess.getProgram());
|
setCurrentProgram(methodToProcess.getProgram());
|
||||||
|
types = new JSTypeInference(typeHelper, program, methodToProcess.getReference());
|
||||||
|
types.ensure();
|
||||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||||
BasicBlock block = program.basicBlockAt(i);
|
var block = program.basicBlockAt(i);
|
||||||
for (Instruction insn : block) {
|
for (var insn : block) {
|
||||||
if (insn instanceof CastInstruction) {
|
if (insn instanceof CastInstruction) {
|
||||||
replacement.clear();
|
replacement.clear();
|
||||||
CallLocation callLocation = new CallLocation(methodToProcess.getReference(), insn.getLocation());
|
var callLocation = new CallLocation(methodToProcess.getReference(), insn.getLocation());
|
||||||
if (processCast((CastInstruction) insn, callLocation)) {
|
if (processCast((CastInstruction) insn, callLocation)) {
|
||||||
insn.insertNextAll(replacement);
|
insn.insertNextAll(replacement);
|
||||||
insn.delete();
|
insn.delete();
|
||||||
}
|
}
|
||||||
|
} else if (insn instanceof IsInstanceInstruction) {
|
||||||
|
processIsInstance((IsInstanceInstruction) insn);
|
||||||
} else if (insn instanceof InvokeInstruction) {
|
} else if (insn instanceof InvokeInstruction) {
|
||||||
InvokeInstruction invoke = (InvokeInstruction) insn;
|
var invoke = (InvokeInstruction) insn;
|
||||||
|
processInvokeArgs(invoke);
|
||||||
|
|
||||||
MethodReader method = getMethod(invoke.getMethod().getClassName(),
|
var method = getMethod(invoke.getMethod().getClassName(), invoke.getMethod().getDescriptor());
|
||||||
invoke.getMethod().getDescriptor());
|
|
||||||
if (method == null) {
|
if (method == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
CallLocation callLocation = new CallLocation(methodToProcess.getReference(), insn.getLocation());
|
var callLocation = new CallLocation(methodToProcess.getReference(), insn.getLocation());
|
||||||
replacement.clear();
|
replacement.clear();
|
||||||
if (processInvocation(method, callLocation, invoke, methodToProcess)) {
|
if (processInvocation(method, callLocation, invoke, methodToProcess)) {
|
||||||
insn.insertNextAll(replacement);
|
insn.insertNextAll(replacement);
|
||||||
insn.delete();
|
insn.delete();
|
||||||
}
|
}
|
||||||
|
} else if (insn instanceof PutFieldInstruction) {
|
||||||
|
processPutField((PutFieldInstruction) insn);
|
||||||
|
} else if (insn instanceof GetElementInstruction) {
|
||||||
|
processGetFromArray((GetElementInstruction) insn);
|
||||||
|
} else if (insn instanceof PutElementInstruction) {
|
||||||
|
processPutIntoArray((PutElementInstruction) insn);
|
||||||
|
} else if (insn instanceof ConstructArrayInstruction) {
|
||||||
|
processConstructArray((ConstructArrayInstruction) insn);
|
||||||
|
} else if (insn instanceof ExitInstruction) {
|
||||||
|
var exit = (ExitInstruction) insn;
|
||||||
|
exit.setValueToReturn(wrapJsAsJava(insn, exit.getValueToReturn(),
|
||||||
|
methodToProcess.getResultType()));
|
||||||
|
} else if (insn instanceof ClassConstantInstruction) {
|
||||||
|
processClassConstant((ClassConstantInstruction) insn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processInvokeArgs(InvokeInstruction invoke) {
|
||||||
|
if (typeHelper.isJavaScriptClass(invoke.getMethod().getClassName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Variable[] newArgs = null;
|
||||||
|
for (var i = 0; i < invoke.getArguments().size(); ++i) {
|
||||||
|
var type = invoke.getMethod().parameterType(i);
|
||||||
|
var arg = invoke.getArguments().get(i);
|
||||||
|
var newArg = wrapJsAsJava(invoke, arg, type);
|
||||||
|
if (newArg != arg) {
|
||||||
|
if (newArgs == null) {
|
||||||
|
newArgs = invoke.getArguments().toArray(new Variable[0]);
|
||||||
|
}
|
||||||
|
newArgs[i] = newArg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newArgs != null) {
|
||||||
|
invoke.setArguments(newArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invoke.getInstance() != null) {
|
||||||
|
invoke.setInstance(wrapJsAsJava(invoke, invoke.getInstance(),
|
||||||
|
ValueType.object(invoke.getMethod().getClassName())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processPutField(PutFieldInstruction putField) {
|
||||||
|
putField.setValue(wrapJsAsJava(putField, putField.getValue(), putField.getFieldType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processGetFromArray(GetElementInstruction insn) {
|
||||||
|
if (insn.getType() != ArrayElementType.OBJECT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var type = types.typeOf(insn.getReceiver());
|
||||||
|
if (type == JSType.JS || type == JSType.MIXED) {
|
||||||
|
var unwrap = new InvokeInstruction();
|
||||||
|
unwrap.setType(InvocationType.SPECIAL);
|
||||||
|
unwrap.setMethod(type == JSType.MIXED ? MAYBE_UNWRAP : UNWRAP);
|
||||||
|
unwrap.setArguments(program.createVariable());
|
||||||
|
unwrap.setReceiver(insn.getReceiver());
|
||||||
|
unwrap.setLocation(insn.getLocation());
|
||||||
|
insn.setReceiver(unwrap.getArguments().get(0));
|
||||||
|
insn.insertNext(unwrap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processPutIntoArray(PutElementInstruction insn) {
|
||||||
|
if (insn.getType() != ArrayElementType.OBJECT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var type = types.typeOf(insn.getValue());
|
||||||
|
if (type == JSType.JS || type == JSType.MIXED) {
|
||||||
|
var wrap = new InvokeInstruction();
|
||||||
|
wrap.setType(InvocationType.SPECIAL);
|
||||||
|
wrap.setMethod(type == JSType.MIXED ? MAYBE_WRAP : WRAP);
|
||||||
|
wrap.setArguments(insn.getValue());
|
||||||
|
wrap.setReceiver(program.createVariable());
|
||||||
|
wrap.setLocation(insn.getLocation());
|
||||||
|
insn.setValue(wrap.getReceiver());
|
||||||
|
insn.insertPrevious(wrap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processConstructArray(ConstructArrayInstruction insn) {
|
||||||
|
insn.setItemType(processType(insn.getItemType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processClassConstant(ClassConstantInstruction insn) {
|
||||||
|
insn.setConstant(processType(insn.getConstant()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ValueType processType(ValueType type) {
|
||||||
|
return processType(typeHelper, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ValueType processType(JSTypeHelper typeHelper, ValueType type) {
|
||||||
|
var originalType = type;
|
||||||
|
var degree = 0;
|
||||||
|
while (type instanceof ValueType.Array) {
|
||||||
|
degree++;
|
||||||
|
type = ((ValueType.Array) type).getItemType();
|
||||||
|
}
|
||||||
|
if (!(type instanceof ValueType.Object)) {
|
||||||
|
return originalType;
|
||||||
|
}
|
||||||
|
|
||||||
|
var className = ((ValueType.Object) type).getClassName();
|
||||||
|
if (!typeHelper.isJavaScriptClass(className)) {
|
||||||
|
return originalType;
|
||||||
|
}
|
||||||
|
|
||||||
|
type = ValueType.object(JSWrapper.class.getName());
|
||||||
|
while (degree-- > 0) {
|
||||||
|
type = ValueType.arrayOf(type);
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean processCast(CastInstruction cast, CallLocation location) {
|
private boolean processCast(CastInstruction cast, CallLocation location) {
|
||||||
if (!(cast.getTargetType() instanceof ValueType.Object)) {
|
if (!(cast.getTargetType() instanceof ValueType.Object)) {
|
||||||
|
cast.setTargetType(processType(cast.getTargetType()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,6 +383,9 @@ class JSClassProcessor {
|
||||||
if (!typeHelper.isJavaScriptClass(targetClassName)) {
|
if (!typeHelper.isJavaScriptClass(targetClassName)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cast.setValue(unwrapJavaToJs(cast, cast.getValue()));
|
||||||
|
|
||||||
ClassReader targetClass = classSource.get(targetClassName);
|
ClassReader targetClass = classSource.get(targetClassName);
|
||||||
if (targetClass.getAnnotations().get(JSFunctor.class.getName()) == null) {
|
if (targetClass.getAnnotations().get(JSFunctor.class.getName()) == null) {
|
||||||
AssignInstruction assign = new AssignInstruction();
|
AssignInstruction assign = new AssignInstruction();
|
||||||
|
@ -255,7 +397,7 @@ class JSClassProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
Variable result = marshaller.unwrapFunctor(location, cast.getValue(), targetClass);
|
Variable result = marshaller.unwrapFunctor(location, cast.getValue(), targetClass);
|
||||||
AssignInstruction assign = new AssignInstruction();
|
var assign = new AssignInstruction();
|
||||||
assign.setLocation(location.getSourceLocation());
|
assign.setLocation(location.getSourceLocation());
|
||||||
assign.setAssignee(result);
|
assign.setAssignee(result);
|
||||||
assign.setReceiver(cast.getReceiver());
|
assign.setReceiver(cast.getReceiver());
|
||||||
|
@ -264,6 +406,75 @@ class JSClassProcessor {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processIsInstance(IsInstanceInstruction isInstance) {
|
||||||
|
if (!(isInstance.getType() instanceof ValueType.Object)) {
|
||||||
|
isInstance.setType(processType(isInstance.getType()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String targetClassName = ((ValueType.Object) isInstance.getType()).getClassName();
|
||||||
|
if (!typeHelper.isJavaScriptClass(targetClassName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var type = types.typeOf(isInstance.getValue());
|
||||||
|
if (type == JSType.JS) {
|
||||||
|
var replacement = new IntegerConstantInstruction();
|
||||||
|
replacement.setConstant(1);
|
||||||
|
replacement.setReceiver(isInstance.getReceiver());
|
||||||
|
replacement.setLocation(isInstance.getLocation());
|
||||||
|
isInstance.replace(replacement);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var replacement = new InvokeInstruction();
|
||||||
|
replacement.setType(InvocationType.SPECIAL);
|
||||||
|
replacement.setMethod(IS_JS);
|
||||||
|
replacement.setArguments(isInstance.getValue());
|
||||||
|
replacement.setReceiver(isInstance.getReceiver());
|
||||||
|
replacement.setLocation(isInstance.getLocation());
|
||||||
|
isInstance.replace(replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Variable wrapJsAsJava(Instruction instruction, Variable var, ValueType type) {
|
||||||
|
if (!(type instanceof ValueType.Object)) {
|
||||||
|
return var;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cls = ((ValueType.Object) type).getClassName();
|
||||||
|
if (typeHelper.isJavaScriptClass(cls)) {
|
||||||
|
return var;
|
||||||
|
}
|
||||||
|
|
||||||
|
var varType = types.typeOf(var);
|
||||||
|
if (varType != JSType.JS && varType != JSType.MIXED) {
|
||||||
|
return var;
|
||||||
|
}
|
||||||
|
var wrap = new InvokeInstruction();
|
||||||
|
wrap.setType(InvocationType.SPECIAL);
|
||||||
|
wrap.setMethod(varType == JSType.JS ? WRAP : MAYBE_WRAP);
|
||||||
|
wrap.setArguments(var);
|
||||||
|
wrap.setReceiver(program.createVariable());
|
||||||
|
wrap.setLocation(instruction.getLocation());
|
||||||
|
instruction.insertPrevious(wrap);
|
||||||
|
return wrap.getReceiver();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Variable unwrapJavaToJs(Instruction instruction, Variable var) {
|
||||||
|
var varType = types.typeOf(var);
|
||||||
|
if (varType != JSType.JAVA && varType != JSType.MIXED) {
|
||||||
|
return var;
|
||||||
|
}
|
||||||
|
var unwrap = new InvokeInstruction();
|
||||||
|
unwrap.setType(InvocationType.SPECIAL);
|
||||||
|
unwrap.setMethod(varType == JSType.JAVA ? UNWRAP : MAYBE_UNWRAP);
|
||||||
|
unwrap.setArguments(var);
|
||||||
|
unwrap.setReceiver(program.createVariable());
|
||||||
|
unwrap.setLocation(instruction.getLocation());
|
||||||
|
instruction.insertPrevious(unwrap);
|
||||||
|
return unwrap.getReceiver();
|
||||||
|
}
|
||||||
|
|
||||||
private boolean processInvocation(MethodReader method, CallLocation callLocation, InvokeInstruction invoke,
|
private boolean processInvocation(MethodReader method, CallLocation callLocation, InvokeInstruction invoke,
|
||||||
MethodHolder methodToProcess) {
|
MethodHolder methodToProcess) {
|
||||||
if (method.getAnnotations().get(JSBody.class.getName()) != null) {
|
if (method.getAnnotations().get(JSBody.class.getName()) != null) {
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
package org.teavm.jso.impl;
|
package org.teavm.jso.impl;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.function.Function;
|
|
||||||
import org.teavm.jso.JSObject;
|
import org.teavm.jso.JSObject;
|
||||||
import org.teavm.jso.core.JSArray;
|
import org.teavm.jso.core.JSArray;
|
||||||
import org.teavm.jso.core.JSArrayReader;
|
import org.teavm.jso.core.JSArrayReader;
|
||||||
|
@ -37,29 +36,29 @@ final class JSMethods {
|
||||||
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 ARRAY_MAPPER = new MethodReference(JS.class, "arrayMapper",
|
public static final MethodReference ARRAY_MAPPER = new MethodReference(JS.class, "arrayMapper",
|
||||||
Function.class, Function.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",
|
||||||
Function.class);
|
JS.WrapFunction.class);
|
||||||
public static final MethodReference BYTE_ARRAY_WRAPPER = new MethodReference(JS.class, "byteArrayWrapper",
|
public static final MethodReference BYTE_ARRAY_WRAPPER = new MethodReference(JS.class, "byteArrayWrapper",
|
||||||
Function.class);
|
JS.WrapFunction.class);
|
||||||
public static final MethodReference SHORT_ARRAY_WRAPPER = new MethodReference(JS.class, "shortArrayWrapper",
|
public static final MethodReference SHORT_ARRAY_WRAPPER = new MethodReference(JS.class, "shortArrayWrapper",
|
||||||
Function.class);
|
JS.WrapFunction.class);
|
||||||
public static final MethodReference CHAR_ARRAY_WRAPPER = new MethodReference(JS.class, "charArrayWrapper",
|
public static final MethodReference CHAR_ARRAY_WRAPPER = new MethodReference(JS.class, "charArrayWrapper",
|
||||||
Function.class);
|
JS.WrapFunction.class);
|
||||||
public static final MethodReference INT_ARRAY_WRAPPER = new MethodReference(JS.class, "intArrayWrapper",
|
public static final MethodReference INT_ARRAY_WRAPPER = new MethodReference(JS.class, "intArrayWrapper",
|
||||||
Function.class);
|
JS.WrapFunction.class);
|
||||||
public static final MethodReference FLOAT_ARRAY_WRAPPER = new MethodReference(JS.class, "floatArrayWrapper",
|
public static final MethodReference FLOAT_ARRAY_WRAPPER = new MethodReference(JS.class, "floatArrayWrapper",
|
||||||
Function.class);
|
JS.WrapFunction.class);
|
||||||
public static final MethodReference DOUBLE_ARRAY_WRAPPER = new MethodReference(JS.class, "doubleArrayWrapper",
|
public static final MethodReference DOUBLE_ARRAY_WRAPPER = new MethodReference(JS.class, "doubleArrayWrapper",
|
||||||
Function.class);
|
JS.WrapFunction.class);
|
||||||
public static final MethodReference STRING_ARRAY_WRAPPER = new MethodReference(JS.class, "stringArrayWrapper",
|
public static final MethodReference STRING_ARRAY_WRAPPER = new MethodReference(JS.class, "stringArrayWrapper",
|
||||||
Function.class);
|
JS.WrapFunction.class);
|
||||||
public static final MethodReference ARRAY_WRAPPER = new MethodReference(JS.class, "arrayWrapper",
|
public static final MethodReference ARRAY_WRAPPER = new MethodReference(JS.class, "arrayWrapper",
|
||||||
Function.class);
|
JS.WrapFunction.class);
|
||||||
public static final MethodReference ARRAY_UNMAPPER = new MethodReference(JS.class, "arrayUnmapper",
|
public static final MethodReference ARRAY_UNMAPPER = new MethodReference(JS.class, "arrayUnmapper",
|
||||||
Class.class, Function.class, Function.class);
|
Class.class, JS.UnwrapFunction.class, JS.UnwrapFunction.class);
|
||||||
public static final MethodReference UNMAP_ARRAY = new MethodReference(JS.class, "unmapArray", Class.class,
|
public static final MethodReference UNMAP_ARRAY = new MethodReference(JS.class, "unmapArray", Class.class,
|
||||||
JSArrayReader.class, Function.class, Object[].class);
|
JSArrayReader.class, JS.UnwrapFunction.class, Object[].class);
|
||||||
public static final MethodReference UNWRAP_BOOLEAN_ARRAY = new MethodReference(JS.class, "unwrapBooleanArray",
|
public static final MethodReference UNWRAP_BOOLEAN_ARRAY = new MethodReference(JS.class, "unwrapBooleanArray",
|
||||||
JSArrayReader.class, boolean[].class);
|
JSArrayReader.class, boolean[].class);
|
||||||
public static final MethodReference UNWRAP_BYTE_ARRAY = new MethodReference(JS.class, "unwrapByteArray",
|
public static final MethodReference UNWRAP_BYTE_ARRAY = new MethodReference(JS.class, "unwrapByteArray",
|
||||||
|
@ -79,23 +78,23 @@ final class JSMethods {
|
||||||
public static final MethodReference UNWRAP_ARRAY = new MethodReference(JS.class, "unwrapArray", Class.class,
|
public static final MethodReference UNWRAP_ARRAY = new MethodReference(JS.class, "unwrapArray", Class.class,
|
||||||
JSArrayReader.class, JSObject[].class);
|
JSArrayReader.class, JSObject[].class);
|
||||||
public static final MethodReference BOOLEAN_ARRAY_UNWRAPPER = new MethodReference(JS.class,
|
public static final MethodReference BOOLEAN_ARRAY_UNWRAPPER = new MethodReference(JS.class,
|
||||||
"booleanArrayUnwrapper", Function.class);
|
"booleanArrayUnwrapper", JS.UnwrapFunction.class);
|
||||||
public static final MethodReference BYTE_ARRAY_UNWRAPPER = new MethodReference(JS.class,
|
public static final MethodReference BYTE_ARRAY_UNWRAPPER = new MethodReference(JS.class,
|
||||||
"byteArrayUnwrapper", Function.class);
|
"byteArrayUnwrapper", JS.UnwrapFunction.class);
|
||||||
public static final MethodReference SHORT_ARRAY_UNWRAPPER = new MethodReference(JS.class,
|
public static final MethodReference SHORT_ARRAY_UNWRAPPER = new MethodReference(JS.class,
|
||||||
"shortArrayUnwrapper", Function.class);
|
"shortArrayUnwrapper", JS.UnwrapFunction.class);
|
||||||
public static final MethodReference CHAR_ARRAY_UNWRAPPER = new MethodReference(JS.class,
|
public static final MethodReference CHAR_ARRAY_UNWRAPPER = new MethodReference(JS.class,
|
||||||
"charArrayUnwrapper", Function.class);
|
"charArrayUnwrapper", JS.UnwrapFunction.class);
|
||||||
public static final MethodReference INT_ARRAY_UNWRAPPER = new MethodReference(JS.class,
|
public static final MethodReference INT_ARRAY_UNWRAPPER = new MethodReference(JS.class,
|
||||||
"intArrayUnwrapper", Function.class);
|
"intArrayUnwrapper", JS.UnwrapFunction.class);
|
||||||
public static final MethodReference FLOAT_ARRAY_UNWRAPPER = new MethodReference(JS.class,
|
public static final MethodReference FLOAT_ARRAY_UNWRAPPER = new MethodReference(JS.class,
|
||||||
"floatArrayUnwrapper", Function.class);
|
"floatArrayUnwrapper", JS.UnwrapFunction.class);
|
||||||
public static final MethodReference DOUBLE_ARRAY_UNWRAPPER = new MethodReference(JS.class,
|
public static final MethodReference DOUBLE_ARRAY_UNWRAPPER = new MethodReference(JS.class,
|
||||||
"doubleArrayUnwrapper", Function.class);
|
"doubleArrayUnwrapper", JS.UnwrapFunction.class);
|
||||||
public static final MethodReference STRING_ARRAY_UNWRAPPER = new MethodReference(JS.class,
|
public static final MethodReference STRING_ARRAY_UNWRAPPER = new MethodReference(JS.class,
|
||||||
"stringArrayUnwrapper", Function.class);
|
"stringArrayUnwrapper", JS.UnwrapFunction.class);
|
||||||
public static final MethodReference ARRAY_UNWRAPPER = new MethodReference(JS.class,
|
public static final MethodReference ARRAY_UNWRAPPER = new MethodReference(JS.class,
|
||||||
"arrayUnwrapper", Class.class, Function.class);
|
"arrayUnwrapper", Class.class, JS.UnwrapFunction.class);
|
||||||
|
|
||||||
public static final MethodReference DATA_TO_BYTE_ARRAY = new MethodReference(JS.class,
|
public static final MethodReference DATA_TO_BYTE_ARRAY = new MethodReference(JS.class,
|
||||||
"dataToByteArray", JSObject.class, byte[].class);
|
"dataToByteArray", JSObject.class, byte[].class);
|
||||||
|
|
|
@ -52,6 +52,18 @@ public class JSOPlugin implements TeaVMPlugin {
|
||||||
jsHost.add(new MethodReference(JSExceptions.class, "getJSException", Throwable.class, JSObject.class),
|
jsHost.add(new MethodReference(JSExceptions.class, "getJSException", Throwable.class, JSObject.class),
|
||||||
exceptionsGenerator);
|
exceptionsGenerator);
|
||||||
|
|
||||||
|
var wrapperGenerator = new JSWrapperGenerator();
|
||||||
|
jsHost.add(new MethodReference(JSWrapper.class, "directJavaToJs", Object.class, JSObject.class),
|
||||||
|
wrapperGenerator);
|
||||||
|
jsHost.add(new MethodReference(JSWrapper.class, "isJava", Object.class, boolean.class),
|
||||||
|
wrapperGenerator);
|
||||||
|
jsHost.add(new MethodReference(JSWrapper.class, "wrapperToJs", JSWrapper.class, JSObject.class),
|
||||||
|
wrapperGenerator);
|
||||||
|
jsHost.add(new MethodReference(JSWrapper.class, "jsToWrapper", JSObject.class, JSWrapper.class),
|
||||||
|
wrapperGenerator);
|
||||||
|
host.add(new MethodReference(JSWrapper.class, "jsToWrapper", JSObject.class, JSWrapper.class),
|
||||||
|
wrapperGenerator);
|
||||||
|
|
||||||
TeaVMPluginUtil.handleNatives(host, JS.class);
|
TeaVMPluginUtil.handleNatives(host, JS.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
57
jso/impl/src/main/java/org/teavm/jso/impl/JSType.java
Normal file
57
jso/impl/src/main/java/org/teavm/jso/impl/JSType.java
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.impl;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class JSType {
|
||||||
|
private JSType() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final JSType NULL = new JSType();
|
||||||
|
public static final JSType JS = new JSType();
|
||||||
|
public static final JSType JAVA = new JSType();
|
||||||
|
public static final JSType MIXED = new JSType();
|
||||||
|
|
||||||
|
public static final class ArrayType extends JSType {
|
||||||
|
public final JSType elementType;
|
||||||
|
|
||||||
|
private ArrayType(JSType elementType) {
|
||||||
|
this.elementType = elementType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ArrayType arrayType = (ArrayType) o;
|
||||||
|
return Objects.equals(elementType, arrayType.elementType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(elementType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JSType arrayOf(JSType elementType) {
|
||||||
|
return new ArrayType(elementType);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.impl;
|
||||||
|
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.analysis.BaseTypeInference;
|
||||||
|
|
||||||
|
public class JSTypeInference extends BaseTypeInference<JSType> {
|
||||||
|
private JSTypeHelper typeHelper;
|
||||||
|
|
||||||
|
public JSTypeInference(JSTypeHelper typeHelper, Program program, MethodReference reference) {
|
||||||
|
super(program, reference);
|
||||||
|
this.typeHelper = typeHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JSType mapType(ValueType type) {
|
||||||
|
if (type instanceof ValueType.Object) {
|
||||||
|
var className = ((ValueType.Object) type).getClassName();
|
||||||
|
if (typeHelper.isJavaScriptClass(className)) {
|
||||||
|
return JSType.JS;
|
||||||
|
}
|
||||||
|
} else if (type instanceof ValueType.Array) {
|
||||||
|
var elementType = mapType(((ValueType.Array) type).getItemType());
|
||||||
|
return JSType.arrayOf(elementType);
|
||||||
|
}
|
||||||
|
return JSType.JAVA;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JSType nullType() {
|
||||||
|
return JSType.NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JSType merge(JSType a, JSType b) {
|
||||||
|
if (a == JSType.NULL) {
|
||||||
|
return b;
|
||||||
|
} else if (b == JSType.NULL) {
|
||||||
|
return a;
|
||||||
|
} else if (a == b) {
|
||||||
|
return a;
|
||||||
|
} else if (a instanceof JSType.ArrayType) {
|
||||||
|
if (b instanceof JSType.ArrayType) {
|
||||||
|
var elementType = merge(((JSType.ArrayType) a).elementType, ((JSType.ArrayType) b).elementType);
|
||||||
|
return JSType.arrayOf(elementType);
|
||||||
|
} else if (b == JSType.JAVA) {
|
||||||
|
return JSType.JAVA;
|
||||||
|
} else {
|
||||||
|
return JSType.MIXED;
|
||||||
|
}
|
||||||
|
} else if (b instanceof JSType.ArrayType) {
|
||||||
|
if (a == JSType.JAVA) {
|
||||||
|
return JSType.JAVA;
|
||||||
|
} else {
|
||||||
|
return JSType.MIXED;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return JSType.MIXED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JSType elementType(JSType jsType) {
|
||||||
|
return jsType instanceof JSType.ArrayType ? ((JSType.ArrayType) jsType).elementType : JSType.MIXED;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,6 @@ package org.teavm.jso.impl;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
import org.teavm.jso.JSFunctor;
|
import org.teavm.jso.JSFunctor;
|
||||||
import org.teavm.jso.JSObject;
|
import org.teavm.jso.JSObject;
|
||||||
|
@ -153,7 +152,7 @@ class JSValueMarshaller {
|
||||||
|
|
||||||
insn = new InvokeInstruction();
|
insn = new InvokeInstruction();
|
||||||
insn.setMethod(referenceCache.getCached(new MethodReference(JS.class.getName(), "map",
|
insn.setMethod(referenceCache.getCached(new MethodReference(JS.class.getName(), "map",
|
||||||
getWrappedType(type), ValueType.parse(Function.class), getWrapperType(type))));
|
getWrappedType(type), ValueType.parse(JS.WrapFunction.class), getWrapperType(type))));
|
||||||
insn.setArguments(var, function);
|
insn.setArguments(var, function);
|
||||||
insn.setReceiver(result);
|
insn.setReceiver(result);
|
||||||
insn.setType(InvocationType.SPECIAL);
|
insn.setType(InvocationType.SPECIAL);
|
||||||
|
@ -334,8 +333,8 @@ class JSValueMarshaller {
|
||||||
|
|
||||||
if (insn.getMethod().parameterCount() == 2) {
|
if (insn.getMethod().parameterCount() == 2) {
|
||||||
Variable cls = program.createVariable();
|
Variable cls = program.createVariable();
|
||||||
ClassConstantInstruction clsInsn = new ClassConstantInstruction();
|
var clsInsn = new ClassConstantInstruction();
|
||||||
clsInsn.setConstant(type);
|
clsInsn.setConstant(JSClassProcessor.processType(typeHelper, type));
|
||||||
clsInsn.setLocation(location.getSourceLocation());
|
clsInsn.setLocation(location.getSourceLocation());
|
||||||
clsInsn.setReceiver(cls);
|
clsInsn.setReceiver(cls);
|
||||||
replacement.add(clsInsn);
|
replacement.add(clsInsn);
|
||||||
|
@ -358,8 +357,8 @@ class JSValueMarshaller {
|
||||||
|
|
||||||
if (insn.getMethod().parameterCount() == 1) {
|
if (insn.getMethod().parameterCount() == 1) {
|
||||||
Variable cls = program.createVariable();
|
Variable cls = program.createVariable();
|
||||||
ClassConstantInstruction clsInsn = new ClassConstantInstruction();
|
var clsInsn = new ClassConstantInstruction();
|
||||||
clsInsn.setConstant(type);
|
clsInsn.setConstant(JSClassProcessor.processType(typeHelper, type));
|
||||||
clsInsn.setLocation(location.getSourceLocation());
|
clsInsn.setLocation(location.getSourceLocation());
|
||||||
clsInsn.setReceiver(cls);
|
clsInsn.setReceiver(cls);
|
||||||
replacement.add(clsInsn);
|
replacement.add(clsInsn);
|
||||||
|
@ -373,8 +372,8 @@ class JSValueMarshaller {
|
||||||
type = ValueType.arrayOf(type);
|
type = ValueType.arrayOf(type);
|
||||||
Variable cls = program.createVariable();
|
Variable cls = program.createVariable();
|
||||||
|
|
||||||
ClassConstantInstruction clsInsn = new ClassConstantInstruction();
|
var clsInsn = new ClassConstantInstruction();
|
||||||
clsInsn.setConstant(type);
|
clsInsn.setConstant(JSClassProcessor.processType(typeHelper, type));
|
||||||
clsInsn.setLocation(location.getSourceLocation());
|
clsInsn.setLocation(location.getSourceLocation());
|
||||||
clsInsn.setReceiver(cls);
|
clsInsn.setReceiver(cls);
|
||||||
replacement.add(clsInsn);
|
replacement.add(clsInsn);
|
||||||
|
@ -389,8 +388,8 @@ class JSValueMarshaller {
|
||||||
}
|
}
|
||||||
|
|
||||||
Variable cls = program.createVariable();
|
Variable cls = program.createVariable();
|
||||||
ClassConstantInstruction clsInsn = new ClassConstantInstruction();
|
var clsInsn = new ClassConstantInstruction();
|
||||||
clsInsn.setConstant(ValueType.arrayOf(type));
|
clsInsn.setConstant(JSClassProcessor.processType(typeHelper, ValueType.arrayOf(type)));
|
||||||
clsInsn.setLocation(location.getSourceLocation());
|
clsInsn.setLocation(location.getSourceLocation());
|
||||||
clsInsn.setReceiver(cls);
|
clsInsn.setReceiver(cls);
|
||||||
replacement.add(clsInsn);
|
replacement.add(clsInsn);
|
||||||
|
|
192
jso/impl/src/main/java/org/teavm/jso/impl/JSWrapper.java
Normal file
192
jso/impl/src/main/java/org/teavm/jso/impl/JSWrapper.java
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.impl;
|
||||||
|
|
||||||
|
import org.teavm.interop.NoSideEffects;
|
||||||
|
import org.teavm.jso.JSBody;
|
||||||
|
import org.teavm.jso.JSObject;
|
||||||
|
import org.teavm.jso.core.JSBoolean;
|
||||||
|
import org.teavm.jso.core.JSFinalizationRegistry;
|
||||||
|
import org.teavm.jso.core.JSMap;
|
||||||
|
import org.teavm.jso.core.JSNumber;
|
||||||
|
import org.teavm.jso.core.JSObjects;
|
||||||
|
import org.teavm.jso.core.JSString;
|
||||||
|
import org.teavm.jso.core.JSWeakMap;
|
||||||
|
import org.teavm.jso.core.JSWeakRef;
|
||||||
|
|
||||||
|
public final class JSWrapper {
|
||||||
|
private static final JSWeakMap<JSObject, JSNumber> hashCodes = JSWeakMap.create();
|
||||||
|
private static final JSWeakMap<JSObject, JSWeakRef<JSObject>> wrappers = JSWeakRef.isSupported()
|
||||||
|
? JSWeakMap.create() : null;
|
||||||
|
private static final JSMap<JSString, JSWeakRef<JSObject>> stringWrappers = JSWeakRef.isSupported()
|
||||||
|
? JSMap.create() : null;
|
||||||
|
private static final JSMap<JSNumber, JSWeakRef<JSObject>> numberWrappers = JSWeakRef.isSupported()
|
||||||
|
? JSMap.create() : null;
|
||||||
|
private static final JSFinalizationRegistry stringFinalizationRegistry;
|
||||||
|
private static final JSFinalizationRegistry numberFinalizationRegistry;
|
||||||
|
private static int hashCodeGen;
|
||||||
|
|
||||||
|
public final JSObject js;
|
||||||
|
|
||||||
|
static {
|
||||||
|
stringFinalizationRegistry = stringWrappers != null
|
||||||
|
? JSFinalizationRegistry.create(token -> stringWrappers.delete((JSString) token))
|
||||||
|
: null;
|
||||||
|
numberFinalizationRegistry = numberWrappers != null
|
||||||
|
? JSFinalizationRegistry.create(token -> numberWrappers.delete((JSNumber) token))
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSWrapper(JSObject js) {
|
||||||
|
this.js = js;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object wrap(Object o) {
|
||||||
|
if (o == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var js = directJavaToJs(o);
|
||||||
|
if (wrappers != null) {
|
||||||
|
var type = JSObjects.typeOf(js);
|
||||||
|
if (type.equals("object") || type.equals("function")) {
|
||||||
|
var existingRef = wrappers.get(js);
|
||||||
|
var existing = !JSObjects.isUndefined(existingRef) ? existingRef.deref() : JSObjects.undefined();
|
||||||
|
if (JSObjects.isUndefined(existing)) {
|
||||||
|
var wrapper = new JSWrapper(js);
|
||||||
|
wrappers.set(js, JSWeakRef.create(wrapperToJs(wrapper)));
|
||||||
|
return wrapper;
|
||||||
|
} else {
|
||||||
|
return jsToWrapper(existing);
|
||||||
|
}
|
||||||
|
} else if (type.equals("string")) {
|
||||||
|
var jsString = (JSString) js;
|
||||||
|
var existingRef = stringWrappers.get(jsString);
|
||||||
|
var existing = !JSObjects.isUndefined(existingRef) ? existingRef.deref() : JSObjects.undefined();
|
||||||
|
if (JSObjects.isUndefined(existing)) {
|
||||||
|
var wrapper = new JSWrapper(js);
|
||||||
|
var wrapperAsJs = wrapperToJs(wrapper);
|
||||||
|
stringWrappers.set(jsString, JSWeakRef.create(wrapperAsJs));
|
||||||
|
stringFinalizationRegistry.register(wrapperAsJs, jsString);
|
||||||
|
return wrapper;
|
||||||
|
} else {
|
||||||
|
return jsToWrapper(existing);
|
||||||
|
}
|
||||||
|
} else if (type.equals("number")) {
|
||||||
|
var jsNumber = (JSNumber) js;
|
||||||
|
var existingRef = numberWrappers.get(jsNumber);
|
||||||
|
var existing = !JSObjects.isUndefined(existingRef) ? existingRef.deref() : JSObjects.undefined();
|
||||||
|
if (JSObjects.isUndefined(existing)) {
|
||||||
|
var wrapper = new JSWrapper(js);
|
||||||
|
var wrapperAsJs = wrapperToJs(wrapper);
|
||||||
|
numberWrappers.set(jsNumber, JSWeakRef.create(wrapperAsJs));
|
||||||
|
numberFinalizationRegistry.register(wrapperAsJs, jsNumber);
|
||||||
|
return wrapper;
|
||||||
|
} else {
|
||||||
|
return jsToWrapper(existing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new JSWrapper(js);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object maybeWrap(Object o) {
|
||||||
|
return o == null || isJava(o) ? o : wrap(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NoSideEffects
|
||||||
|
public static native JSObject directJavaToJs(Object obj);
|
||||||
|
|
||||||
|
@NoSideEffects
|
||||||
|
private static native JSObject wrapperToJs(JSWrapper obj);
|
||||||
|
|
||||||
|
@NoSideEffects
|
||||||
|
private static native JSWrapper jsToWrapper(JSObject obj);
|
||||||
|
|
||||||
|
@NoSideEffects
|
||||||
|
public static native boolean isJava(Object obj);
|
||||||
|
|
||||||
|
public static JSObject unwrap(Object o) {
|
||||||
|
if (o == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ((JSWrapper) o).js;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JSObject maybeUnwrap(Object o) {
|
||||||
|
if (o == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return isJava(o) ? unwrap(o) : directJavaToJs(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isJs(Object o) {
|
||||||
|
if (o == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !isJava(o) || o instanceof JSWrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
var type = JSObjects.typeOf(js);
|
||||||
|
if (type.equals("object") || type.equals("symbol") || type.equals("function")) {
|
||||||
|
var code = hashCodes.get(js);
|
||||||
|
if (JSObjects.isUndefined(code)) {
|
||||||
|
code = JSNumber.valueOf(++hashCodeGen);
|
||||||
|
hashCodes.set(js, code);
|
||||||
|
}
|
||||||
|
return code.intValue();
|
||||||
|
} else if (type.equals("number")) {
|
||||||
|
return ((JSNumber) js).intValue();
|
||||||
|
} else if (type.equals("bigint")) {
|
||||||
|
return bigintTruncate(js);
|
||||||
|
} else if (type.equals("string")) {
|
||||||
|
var s = (JSString) js;
|
||||||
|
var hashCode = 0;
|
||||||
|
for (var i = 0; i < s.getLength(); ++i) {
|
||||||
|
hashCode = 31 * hashCode + s.charCodeAt(i);
|
||||||
|
}
|
||||||
|
return hashCode;
|
||||||
|
} else if (type.equals("boolean")) {
|
||||||
|
return js == JSBoolean.valueOf(true) ? 1 : 0;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSBody(params = "bigint", script = "return BigInt.asIntN(bigint, 32);")
|
||||||
|
@NoSideEffects
|
||||||
|
private static native int bigintTruncate(JSObject bigint);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!(obj instanceof JSWrapper)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return js == ((JSWrapper) obj).js;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return JSObjects.toString(js);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 konsoletyper.
|
||||||
|
*
|
||||||
|
* 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.impl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.teavm.backend.javascript.spi.Injector;
|
||||||
|
import org.teavm.backend.javascript.spi.InjectorContext;
|
||||||
|
import org.teavm.dependency.DependencyAgent;
|
||||||
|
import org.teavm.dependency.DependencyPlugin;
|
||||||
|
import org.teavm.dependency.MethodDependency;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
public class JSWrapperGenerator implements Injector, DependencyPlugin {
|
||||||
|
@Override
|
||||||
|
public void generate(InjectorContext context, MethodReference methodRef) throws IOException {
|
||||||
|
switch (methodRef.getName()) {
|
||||||
|
case "directJavaToJs":
|
||||||
|
case "wrapperToJs":
|
||||||
|
case "jsToWrapper":
|
||||||
|
context.writeExpr(context.getArgument(0));
|
||||||
|
break;
|
||||||
|
case "isJava":
|
||||||
|
context.writeExpr(context.getArgument(0));
|
||||||
|
context.getWriter().append(" instanceof ").appendFunction("$rt_objcls").append("()");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||||
|
if (method.getMethod().getName().equals("jsToWrapper")) {
|
||||||
|
method.getResult().propagate(agent.getType(JSWrapper.class.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
237
tests/src/test/java/org/teavm/jso/test/JSWrapperTest.java
Normal file
237
tests/src/test/java/org/teavm/jso/test/JSWrapperTest.java
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 konsoletyper.
|
||||||
|
*
|
||||||
|
* 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 static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotEquals;
|
||||||
|
import static org.junit.Assert.assertNotSame;
|
||||||
|
import static org.junit.Assert.assertSame;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.teavm.jso.JSBody;
|
||||||
|
import org.teavm.jso.JSObject;
|
||||||
|
import org.teavm.jso.core.JSNumber;
|
||||||
|
import org.teavm.jso.core.JSObjects;
|
||||||
|
import org.teavm.jso.core.JSString;
|
||||||
|
import org.teavm.junit.SkipJVM;
|
||||||
|
import org.teavm.junit.TeaVMTestRunner;
|
||||||
|
import org.teavm.junit.WholeClassCompilation;
|
||||||
|
|
||||||
|
@RunWith(TeaVMTestRunner.class)
|
||||||
|
@WholeClassCompilation
|
||||||
|
@SkipJVM
|
||||||
|
public class JSWrapperTest {
|
||||||
|
private List<Object> list = new ArrayList<>();
|
||||||
|
|
||||||
|
private Object field1;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void simple() {
|
||||||
|
list.add(JSNumber.valueOf(23));
|
||||||
|
list.add(JSString.valueOf("q"));
|
||||||
|
list.add(JSString.valueOf("q"));
|
||||||
|
list.add("q");
|
||||||
|
list.add("w");
|
||||||
|
|
||||||
|
assertEquals("23", list.get(0).toString());
|
||||||
|
assertEquals("q", list.get(1).toString());
|
||||||
|
assertEquals(list.get(1), list.get(2));
|
||||||
|
assertNotEquals(list.get(0), list.get(2));
|
||||||
|
assertNotEquals(list.get(1), list.get(3));
|
||||||
|
|
||||||
|
assertEquals(23, ((JSNumber) list.get(0)).intValue());
|
||||||
|
assertEquals("q", ((JSString) list.get(1)).stringValue());
|
||||||
|
|
||||||
|
try {
|
||||||
|
assertEquals("q", ((JSString) list.get(3)).stringValue());
|
||||||
|
fail("Expected exception not thrown");
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
// ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHashCode() {
|
||||||
|
list.add(JSNumber.valueOf(23));
|
||||||
|
|
||||||
|
var o1 = JSObjects.create();
|
||||||
|
list.add(o1);
|
||||||
|
list.add(o1);
|
||||||
|
|
||||||
|
var o2 = JSObjects.create();
|
||||||
|
list.add(o2);
|
||||||
|
|
||||||
|
assertEquals(23, list.get(0).hashCode());
|
||||||
|
assertEquals(list.get(1).hashCode(), list.get(2).hashCode());
|
||||||
|
assertNotEquals(list.get(1).hashCode(), list.get(3).hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void referentialEquality() {
|
||||||
|
var o1 = JSObjects.create();
|
||||||
|
list.add(o1);
|
||||||
|
list.add(o1);
|
||||||
|
|
||||||
|
var o2 = JSObjects.create();
|
||||||
|
list.add(o2);
|
||||||
|
|
||||||
|
assertSame(list.get(0), list.get(1));
|
||||||
|
assertNotSame(list.get(0), list.get(2));
|
||||||
|
|
||||||
|
assertSame(JSString.valueOf("q"), JSString.valueOf("q"));
|
||||||
|
assertSame(JSNumber.valueOf(23), JSNumber.valueOf(23));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void equality() {
|
||||||
|
var o1 = JSObjects.create();
|
||||||
|
list.add(o1);
|
||||||
|
list.add(o1);
|
||||||
|
|
||||||
|
var o2 = JSObjects.create();
|
||||||
|
list.add(o2);
|
||||||
|
|
||||||
|
list.add(JSString.valueOf("q"));
|
||||||
|
list.add(JSString.valueOf("q"));
|
||||||
|
list.add(JSString.valueOf("w"));
|
||||||
|
|
||||||
|
assertEquals(list.get(0), list.get(1));
|
||||||
|
assertNotEquals(list.get(0), list.get(2));
|
||||||
|
assertEquals(list.get(3), list.get(4));
|
||||||
|
assertNotEquals(list.get(3), list.get(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void wrapNull() {
|
||||||
|
list.add(jsNull());
|
||||||
|
assertEquals("null", Objects.toString(list.get(0)));
|
||||||
|
try {
|
||||||
|
list.get(0).toString();
|
||||||
|
fail("Expected exception not thrown");
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
// ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void unwrapNull() {
|
||||||
|
list.add(null);
|
||||||
|
assertTrue(isNull((JSObject) list.get(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void instanceOf() {
|
||||||
|
list.add(23);
|
||||||
|
list.add(JSNumber.valueOf(23));
|
||||||
|
list.add(null);
|
||||||
|
list.add(jsNull());
|
||||||
|
|
||||||
|
assertTrue(list.get(0) instanceof Object);
|
||||||
|
assertTrue(list.get(0) instanceof Integer);
|
||||||
|
assertFalse(list.get(0) instanceof JSNumber);
|
||||||
|
|
||||||
|
assertTrue(list.get(1) instanceof Object);
|
||||||
|
assertFalse(list.get(1) instanceof Integer);
|
||||||
|
assertTrue(list.get(1) instanceof JSNumber);
|
||||||
|
|
||||||
|
assertFalse(list.get(2) instanceof Object);
|
||||||
|
assertFalse(list.get(2) instanceof Integer);
|
||||||
|
assertFalse(list.get(2) instanceof JSNumber);
|
||||||
|
|
||||||
|
assertTrue(JSObjects.create() instanceof JSObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void mergeTypes() {
|
||||||
|
for (var i = 0; i < 2; ++i) {
|
||||||
|
var o = i == 0 ? JSNumber.valueOf(23) : 23;
|
||||||
|
list.add(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue(list.get(0) instanceof JSNumber);
|
||||||
|
assertFalse(list.get(0) instanceof Integer);
|
||||||
|
|
||||||
|
assertFalse(list.get(1) instanceof JSNumber);
|
||||||
|
assertTrue(list.get(1) instanceof Integer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClass() {
|
||||||
|
list.add(23);
|
||||||
|
list.add(JSNumber.valueOf(23));
|
||||||
|
|
||||||
|
assertEquals(Integer.class, list.get(0).getClass());
|
||||||
|
assertEquals(JSNumber.class, list.get(1).getClass());
|
||||||
|
|
||||||
|
assertEquals("java.lang.Integer", list.get(0).getClass().getName());
|
||||||
|
assertEquals("org.teavm.jso.impl.JSWrapper", list.get(1).getClass().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void array() {
|
||||||
|
var array = new JSString[3];
|
||||||
|
array[0] = JSString.valueOf("q");
|
||||||
|
array[1] = JSString.valueOf("q");
|
||||||
|
array[2] = JSString.valueOf("w");
|
||||||
|
|
||||||
|
assertEquals(JSString.valueOf("q"), array[0]);
|
||||||
|
assertEquals(JSString.valueOf("w"), array[2]);
|
||||||
|
assertEquals("q", array[0].stringValue());
|
||||||
|
assertEquals("w", array[2].stringValue());
|
||||||
|
assertEquals(array[0], array[1]);
|
||||||
|
assertEquals(JSString[].class, array.getClass());
|
||||||
|
assertEquals(JSString.class, array.getClass().getComponentType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void objectArray() {
|
||||||
|
var array = new Object[4];
|
||||||
|
array[0] = JSString.valueOf("q");
|
||||||
|
array[1] = JSString.valueOf("q");
|
||||||
|
array[2] = JSString.valueOf("w");
|
||||||
|
array[3] = "q";
|
||||||
|
|
||||||
|
assertEquals(JSString.valueOf("q"), array[0]);
|
||||||
|
assertEquals(JSString.valueOf("w"), array[2]);
|
||||||
|
assertEquals("q", array[3]);
|
||||||
|
|
||||||
|
assertEquals("q", ((JSString) array[0]).stringValue());
|
||||||
|
assertEquals(array[0], array[1]);
|
||||||
|
assertNotEquals(array[0], array[2]);
|
||||||
|
assertNotEquals(array[0], array[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void field() {
|
||||||
|
field1 = 23;
|
||||||
|
assertEquals("java.lang.Integer", field1.getClass().getName());
|
||||||
|
|
||||||
|
field1 = JSNumber.valueOf(23);
|
||||||
|
assertEquals("org.teavm.jso.impl.JSWrapper", field1.getClass().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSBody(script = "return null;")
|
||||||
|
private static native JSObject jsNull();
|
||||||
|
|
||||||
|
@JSBody(params = "o", script = "return o === null;")
|
||||||
|
private static native boolean isNull(JSObject o);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user