mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 00:04:10 -08:00
wasm gc: support wrapping JS values in Java
This commit is contained in:
parent
2340760647
commit
a291eb3026
|
@ -188,6 +188,12 @@ public class DisassemblyCodeListener extends BaseDisassemblyListener implements
|
|||
case IS_NULL:
|
||||
writer.write("ref.is_null");
|
||||
break;
|
||||
case EXTERN_TO_ANY:
|
||||
writer.write("any.convert_extern");
|
||||
break;
|
||||
case ANY_TO_EXTERN:
|
||||
writer.write("extern.convert_any");
|
||||
break;
|
||||
}
|
||||
writer.eol();
|
||||
}
|
||||
|
|
|
@ -277,6 +277,11 @@ public class WasmDefaultExpressionVisitor implements WasmExpressionVisitor {
|
|||
expression.getValue().acceptVisitor(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmExternConversion expression) {
|
||||
expression.getValue().acceptVisitor(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmStructNew expression) {
|
||||
for (var initializer : expression.getInitializers()) {
|
||||
|
|
|
@ -102,6 +102,8 @@ public interface WasmExpressionVisitor {
|
|||
|
||||
void visit(WasmCast expression);
|
||||
|
||||
void visit(WasmExternConversion expression);
|
||||
|
||||
void visit(WasmTest expression);
|
||||
|
||||
void visit(WasmStructNew expression);
|
||||
|
|
|
@ -15,5 +15,35 @@
|
|||
*/
|
||||
package org.teavm.backend.wasm.model.expression;
|
||||
|
||||
public class WasmExternConversion {
|
||||
import java.util.Objects;
|
||||
|
||||
public class WasmExternConversion extends WasmExpression {
|
||||
private WasmExternConversionType type;
|
||||
private WasmExpression value;
|
||||
|
||||
public WasmExternConversion(WasmExternConversionType type, WasmExpression value) {
|
||||
this.type = Objects.requireNonNull(type);
|
||||
this.value = Objects.requireNonNull(value);
|
||||
}
|
||||
|
||||
public WasmExternConversionType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(WasmExternConversionType type) {
|
||||
this.type = Objects.requireNonNull(type);
|
||||
}
|
||||
|
||||
public WasmExpression getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(WasmExpression value) {
|
||||
this.value = Objects.requireNonNull(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptVisitor(WasmExpressionVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,6 @@
|
|||
package org.teavm.backend.wasm.model.expression;
|
||||
|
||||
public enum WasmExternConversionType {
|
||||
EXTERN_TO_OBJECT,
|
||||
OBJECT_TO_EXTERN
|
||||
EXTERN_TO_ANY,
|
||||
ANY_TO_EXTERN
|
||||
}
|
||||
|
|
|
@ -22,8 +22,7 @@ import org.teavm.backend.wasm.model.WasmFunction;
|
|||
public class WasmReplacingExpressionVisitor implements WasmExpressionVisitor {
|
||||
private Function<WasmExpression, WasmExpression> mapper;
|
||||
|
||||
public WasmReplacingExpressionVisitor(
|
||||
Function<WasmExpression, WasmExpression> mapper) {
|
||||
public WasmReplacingExpressionVisitor(Function<WasmExpression, WasmExpression> mapper) {
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
|
@ -332,6 +331,12 @@ public class WasmReplacingExpressionVisitor implements WasmExpressionVisitor {
|
|||
expression.setValue(mapper.apply(expression.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmExternConversion expression) {
|
||||
expression.getValue().acceptVisitor(this);
|
||||
expression.setValue(mapper.apply(expression.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmStructNew expression) {
|
||||
replaceExpressions(expression.getInitializers());
|
||||
|
|
|
@ -719,6 +719,13 @@ public class CodeParser extends BaseSectionParser {
|
|||
parseCastBranch(false);
|
||||
return true;
|
||||
|
||||
case 26:
|
||||
codeListener.opcode(Opcode.EXTERN_TO_ANY);
|
||||
return true;
|
||||
case 27:
|
||||
codeListener.opcode(Opcode.ANY_TO_EXTERN);
|
||||
return true;
|
||||
|
||||
case 28:
|
||||
codeListener.int31Reference();
|
||||
return true;
|
||||
|
|
|
@ -22,5 +22,7 @@ public enum Opcode {
|
|||
DROP,
|
||||
REF_EQ,
|
||||
ARRAY_LENGTH,
|
||||
IS_NULL
|
||||
IS_NULL,
|
||||
ANY_TO_EXTERN,
|
||||
EXTERN_TO_ANY
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ import org.teavm.backend.wasm.model.expression.WasmDefaultExpressionVisitor;
|
|||
import org.teavm.backend.wasm.model.expression.WasmDrop;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpressionVisitor;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExternConversion;
|
||||
import org.teavm.backend.wasm.model.expression.WasmFill;
|
||||
import org.teavm.backend.wasm.model.expression.WasmFloat32Constant;
|
||||
import org.teavm.backend.wasm.model.expression.WasmFloat64Constant;
|
||||
|
@ -1171,6 +1172,22 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
|
|||
popLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmExternConversion expression) {
|
||||
pushLocation(expression);
|
||||
expression.getValue().acceptVisitor(this);
|
||||
writer.writeByte(0xfb);
|
||||
switch (expression.getType()) {
|
||||
case EXTERN_TO_ANY:
|
||||
writer.writeByte(26);
|
||||
break;
|
||||
case ANY_TO_EXTERN:
|
||||
writer.writeByte(27);
|
||||
break;
|
||||
}
|
||||
popLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmStructNew expression) {
|
||||
pushLocation(expression);
|
||||
|
|
|
@ -46,6 +46,7 @@ import org.teavm.backend.wasm.model.expression.WasmCopy;
|
|||
import org.teavm.backend.wasm.model.expression.WasmDrop;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpressionVisitor;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExternConversion;
|
||||
import org.teavm.backend.wasm.model.expression.WasmFill;
|
||||
import org.teavm.backend.wasm.model.expression.WasmFloat32Constant;
|
||||
import org.teavm.backend.wasm.model.expression.WasmFloat64Constant;
|
||||
|
@ -1197,6 +1198,11 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor {
|
|||
unsupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmExternConversion expression) {
|
||||
unsupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmStructNew expression) {
|
||||
unsupported();
|
||||
|
|
|
@ -43,6 +43,7 @@ import org.teavm.backend.wasm.model.expression.WasmDefaultExpressionVisitor;
|
|||
import org.teavm.backend.wasm.model.expression.WasmDrop;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpressionVisitor;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExternConversion;
|
||||
import org.teavm.backend.wasm.model.expression.WasmFill;
|
||||
import org.teavm.backend.wasm.model.expression.WasmFloat32Constant;
|
||||
import org.teavm.backend.wasm.model.expression.WasmFloat64Constant;
|
||||
|
@ -772,6 +773,21 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
|
|||
close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmExternConversion expression) {
|
||||
open();
|
||||
switch (expression.getType()) {
|
||||
case EXTERN_TO_ANY:
|
||||
append("any.convert_extern");
|
||||
break;
|
||||
case ANY_TO_EXTERN:
|
||||
append("extern.convert_any");
|
||||
break;
|
||||
}
|
||||
line(expression.getValue());
|
||||
close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmStructNew expression) {
|
||||
open().append("struct.new ");
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.teavm.backend.wasm.model.expression.WasmConversion;
|
|||
import org.teavm.backend.wasm.model.expression.WasmCopy;
|
||||
import org.teavm.backend.wasm.model.expression.WasmDrop;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpressionVisitor;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExternConversion;
|
||||
import org.teavm.backend.wasm.model.expression.WasmFill;
|
||||
import org.teavm.backend.wasm.model.expression.WasmFloat32Constant;
|
||||
import org.teavm.backend.wasm.model.expression.WasmFloat64Constant;
|
||||
|
@ -317,6 +318,18 @@ public class WasmTypeInference implements WasmExpressionVisitor {
|
|||
result = WasmType.INT32;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmExternConversion expression) {
|
||||
switch (expression.getType()) {
|
||||
case EXTERN_TO_ANY:
|
||||
result = WasmType.Reference.ANY;
|
||||
break;
|
||||
case ANY_TO_EXTERN:
|
||||
result = WasmType.Reference.EXTERN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmStructNew expression) {
|
||||
result = expression.getType().getReference();
|
||||
|
|
|
@ -278,7 +278,6 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
|||
|
||||
public void addDependencyListener(DependencyListener listener) {
|
||||
listeners.add(listener);
|
||||
listener.started(agent);
|
||||
}
|
||||
|
||||
public void addClassTransformer(ClassHolderTransformer transformer) {
|
||||
|
@ -663,6 +662,12 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
|||
}
|
||||
}
|
||||
|
||||
public void initDependencies() {
|
||||
for (var listener : listeners) {
|
||||
listener.started(agent);
|
||||
}
|
||||
}
|
||||
|
||||
public void processDependencies() {
|
||||
interrupted = false;
|
||||
processQueue();
|
||||
|
|
|
@ -41,6 +41,10 @@ public class BranchingInstruction extends Instruction {
|
|||
return condition;
|
||||
}
|
||||
|
||||
public void setCondition(BranchingCondition condition) {
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
public BasicBlock getConsequent() {
|
||||
return consequent;
|
||||
}
|
||||
|
|
|
@ -318,11 +318,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
return;
|
||||
}
|
||||
|
||||
var mainMethod = cls.getMethod(MAIN_METHOD_DESC) != null
|
||||
? dependencyAnalyzer.linkMethod(new MethodReference(entryPoint,
|
||||
"main", ValueType.parse(String[].class), ValueType.VOID))
|
||||
: null;
|
||||
dependencyAnalyzer.defer(() -> {
|
||||
var mainMethod = cls.getMethod(MAIN_METHOD_DESC) != null
|
||||
? dependencyAnalyzer.linkMethod(new MethodReference(entryPoint,
|
||||
"main", ValueType.parse(String[].class), ValueType.VOID))
|
||||
: null;
|
||||
dependencyAnalyzer.linkClass(entryPoint).initClass(null);
|
||||
if (mainMethod != null) {
|
||||
mainMethod.getVariable(1).propagate(dependencyAnalyzer.getType("[Ljava/lang/String;"));
|
||||
|
@ -388,6 +388,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
cancelled |= progressListener.progressReached(progress) != TeaVMProgressFeedback.CONTINUE;
|
||||
return !cancelled;
|
||||
});
|
||||
dependencyAnalyzer.initDependencies();
|
||||
target.contributeDependencies(dependencyAnalyzer);
|
||||
if (target.needsSystemArrayCopyOptimization()) {
|
||||
dependencyAnalyzer.addDependencyListener(new StdlibDependencyListener());
|
||||
|
|
|
@ -20,21 +20,16 @@ TeaVM.wasm = function() {
|
|||
let getGlobalName = function(name) {
|
||||
return eval(name);
|
||||
}
|
||||
let javaObjectSymbol = Symbol("javaObject");
|
||||
let functionsSymbol = Symbol("functions");
|
||||
let functionOriginSymbol = Symbol("functionOrigin");
|
||||
let javaWrappers = new WeakMap();
|
||||
|
||||
function defaults(imports) {
|
||||
let stderr = "";
|
||||
let stdout = "";
|
||||
let finalizationRegistry = new FinalizationRegistry(heldValue => {
|
||||
if (typeof exports.reportGarbageCollectedValue === "function") {
|
||||
exports.reportGarbageCollectedValue(heldValue)
|
||||
}
|
||||
});
|
||||
let stringFinalizationRegistry = new FinalizationRegistry(heldValue => {
|
||||
exports.reportGarbageCollectedString(heldValue);
|
||||
});
|
||||
dateImports(imports);
|
||||
consoleImports(imports);
|
||||
coreImports(imports);
|
||||
jsoImports(imports);
|
||||
imports.teavmMath = Math;
|
||||
}
|
||||
|
||||
function dateImports(imports) {
|
||||
imports.teavmDate = {
|
||||
currentTimeMillis: () => new Date().getTime(),
|
||||
dateToString: timestamp => stringToJava(new Date(timestamp).toString()),
|
||||
|
@ -59,6 +54,11 @@ TeaVM.wasm = function() {
|
|||
create: (year, month, date, hrs, min, sec) => new Date(year, month, date, hrs, min, sec).getTime(),
|
||||
createFromUTC: (year, month, date, hrs, min, sec) => Date.UTC(year, month, date, hrs, min, sec)
|
||||
};
|
||||
}
|
||||
|
||||
function consoleImports(imports) {
|
||||
let stderr = "";
|
||||
let stdout = "";
|
||||
imports.teavmConsole = {
|
||||
putcharStderr(c) {
|
||||
if (c === 10) {
|
||||
|
@ -77,6 +77,9 @@ TeaVM.wasm = function() {
|
|||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function coreImports(imports) {
|
||||
imports.teavm = {
|
||||
createWeakRef(value, heldValue) {
|
||||
let weakRef = new WeakRef(value);
|
||||
|
@ -93,6 +96,37 @@ TeaVM.wasm = function() {
|
|||
},
|
||||
stringDeref: weakRef => weakRef.deref()
|
||||
};
|
||||
}
|
||||
|
||||
function jsoImports(imports) {
|
||||
new FinalizationRegistry(heldValue => {
|
||||
if (typeof exports.reportGarbageCollectedValue === "function") {
|
||||
exports.reportGarbageCollectedValue(heldValue)
|
||||
}
|
||||
});
|
||||
new FinalizationRegistry(heldValue => {
|
||||
exports.reportGarbageCollectedString(heldValue);
|
||||
});
|
||||
|
||||
let javaObjectSymbol = Symbol("javaObject");
|
||||
let functionsSymbol = Symbol("functions");
|
||||
let functionOriginSymbol = Symbol("functionOrigin");
|
||||
|
||||
let jsWrappers = new WeakMap();
|
||||
let javaWrappers = new WeakMap();
|
||||
let primitiveWrappers = new Map();
|
||||
let primitiveFinalization = new FinalizationRegistry(token => primitiveFinalization.delete(token));
|
||||
let hashCodes = new WeakMap();
|
||||
let lastHashCode = 2463534242;
|
||||
let nextHashCode = () => {
|
||||
let x = lastHashCode;
|
||||
x ^= x << 13;
|
||||
x ^= x >>> 17;
|
||||
x ^= x << 5;
|
||||
lastHashCode = x;
|
||||
return x;
|
||||
}
|
||||
|
||||
function identity(value) {
|
||||
return value;
|
||||
}
|
||||
|
@ -157,7 +191,7 @@ TeaVM.wasm = function() {
|
|||
Object.defineProperty(cls.prototype, name, descriptor);
|
||||
},
|
||||
javaObjectToJS(instance, cls) {
|
||||
let existing = javaWrappers.get(instance);
|
||||
let existing = jsWrappers.get(instance);
|
||||
if (typeof existing != "undefined") {
|
||||
let result = existing.deref();
|
||||
if (typeof result !== "undefined") {
|
||||
|
@ -165,7 +199,7 @@ TeaVM.wasm = function() {
|
|||
}
|
||||
}
|
||||
let obj = new cls(instance);
|
||||
javaWrappers.set(instance, new WeakRef(obj));
|
||||
jsWrappers.set(instance, new WeakRef(obj));
|
||||
return obj;
|
||||
},
|
||||
unwrapJavaObject(instance) {
|
||||
|
@ -196,10 +230,64 @@ TeaVM.wasm = function() {
|
|||
}
|
||||
}
|
||||
return { [property]: fn };
|
||||
},
|
||||
wrapObject(obj) {
|
||||
if (obj === null) {
|
||||
return null;
|
||||
}
|
||||
if (typeof obj === "object" || typeof obj === "function" || typeof "obj" === "symbol") {
|
||||
let result = obj[javaObjectSymbol];
|
||||
if (typeof result === "object") {
|
||||
return result;
|
||||
}
|
||||
result = javaWrappers.get(obj);
|
||||
if (result !== void 0) {
|
||||
result = result.deref();
|
||||
if (result !== void 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
result = exports["teavm.jso.createWrapper"](obj);
|
||||
javaWrappers.set(obj, new WeakRef(result));
|
||||
return result;
|
||||
} else {
|
||||
let result = primitiveWrappers.get(obj);
|
||||
if (result !== void 0) {
|
||||
result = result.deref();
|
||||
if (result !== void 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
result = exports["teavm.jso.createWrapper"](obj);
|
||||
primitiveWrappers.set(obj, new WeakRef(result));
|
||||
primitiveFinalization.register(result, obj);
|
||||
return result;
|
||||
}
|
||||
},
|
||||
isPrimitive: (value, type) => typeof value === type,
|
||||
sameRef: (a, b) => a === b,
|
||||
hashCode: (obj) => {
|
||||
if (typeof obj === "object" || typeof obj === "function" || typeof obj === "symbol") {
|
||||
let code = hashCodes.get(obj);
|
||||
if (typeof code === "number") {
|
||||
return code;
|
||||
}
|
||||
code = nextHashCode();
|
||||
hashCodes.set(obj, code);
|
||||
return code;
|
||||
} else if (typeof obj === "number") {
|
||||
return obj | 0;
|
||||
} else if (typeof obj === "bigint") {
|
||||
return BigInt.asIntN(obj, 32);
|
||||
} else if (typeof obj === "boolean") {
|
||||
return obj ? 1 : 0;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
for (let name of ["wrapByte", "wrapShort", "wrapChar", "wrapInt", "wrapFloat", "wrapDouble", "unwrapByte",
|
||||
"unwrapShort", "unwrapChar", "unwrapInt", "unwrapFloat", "unwrapDouble"]) {
|
||||
"unwrapShort", "unwrapChar", "unwrapInt", "unwrapFloat", "unwrapDouble"]) {
|
||||
imports.teavmJso[name] = identity;
|
||||
}
|
||||
for (let i = 0; i < 32; ++i) {
|
||||
|
@ -208,7 +296,6 @@ TeaVM.wasm = function() {
|
|||
imports.teavmJso["callMethod" + i] = (instance, method, ...args) => instance[method](...args);
|
||||
imports.teavmJso["construct" + i] = (constructor, ...args) => new constructor(...args);
|
||||
}
|
||||
imports.teavmMath = Math;
|
||||
}
|
||||
|
||||
function load(path, options) {
|
||||
|
|
|
@ -764,6 +764,7 @@ public final class JS {
|
|||
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
@Import(name = "isPrimitive", module = "teavmJso")
|
||||
public static native boolean isPrimitive(JSObject obj, JSObject primitive);
|
||||
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
|
@ -773,4 +774,13 @@ public final class JS {
|
|||
@InjectedBy(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native JSObject argumentsBeginningAt(int index);
|
||||
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
@Import(name = "sameRef", module = "teavmJso")
|
||||
public static native boolean sameRef(JSObject a, JSObject b);
|
||||
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native boolean isNull(JSObject o);
|
||||
}
|
||||
|
|
|
@ -62,6 +62,9 @@ import org.teavm.model.ValueType;
|
|||
import org.teavm.model.Variable;
|
||||
import org.teavm.model.instructions.ArrayElementType;
|
||||
import org.teavm.model.instructions.AssignInstruction;
|
||||
import org.teavm.model.instructions.BinaryBranchingInstruction;
|
||||
import org.teavm.model.instructions.BranchingCondition;
|
||||
import org.teavm.model.instructions.BranchingInstruction;
|
||||
import org.teavm.model.instructions.CastInstruction;
|
||||
import org.teavm.model.instructions.ClassConstantInstruction;
|
||||
import org.teavm.model.instructions.ConstructArrayInstruction;
|
||||
|
@ -333,6 +336,10 @@ class JSClassProcessor {
|
|||
if (nativeConstructedObjects[index]) {
|
||||
assign.delete();
|
||||
}
|
||||
} else if (insn instanceof BinaryBranchingInstruction) {
|
||||
processReferenceEquality((BinaryBranchingInstruction) insn);
|
||||
} else if (insn instanceof BranchingInstruction) {
|
||||
processReferenceEquality((BranchingInstruction) insn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -463,6 +470,106 @@ class JSClassProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
private void processReferenceEquality(BinaryBranchingInstruction instruction) {
|
||||
if (!wasmGC) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean equal;
|
||||
switch (instruction.getCondition()) {
|
||||
case REFERENCE_EQUAL:
|
||||
equal = true;
|
||||
break;
|
||||
case REFERENCE_NOT_EQUAL:
|
||||
equal = false;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
var first = types.typeOf(instruction.getFirstOperand());
|
||||
var second = types.typeOf(instruction.getSecondOperand());
|
||||
if (first == JSType.JS || second == JSType.JS) {
|
||||
var call = new InvokeInstruction();
|
||||
call.setType(InvocationType.SPECIAL);
|
||||
call.setLocation(instruction.getLocation());
|
||||
var conditionVar = program.createVariable();
|
||||
if (first == JSType.NULL || second == JSType.NULL) {
|
||||
call.setMethod(new MethodReference(JS.class, "isNull", JSObject.class, boolean.class));
|
||||
call.setArguments(first == JSType.NULL
|
||||
? instruction.getSecondOperand()
|
||||
: instruction.getFirstOperand());
|
||||
call.setReceiver(conditionVar);
|
||||
instruction.insertPrevious(call);
|
||||
} else {
|
||||
var firstOperand = instruction.getFirstOperand();
|
||||
var secondOperand = instruction.getSecondOperand();
|
||||
if (first != JSType.JS) {
|
||||
firstOperand = convertToJs(firstOperand, instruction);
|
||||
}
|
||||
if (second != JSType.JS) {
|
||||
secondOperand = convertToJs(secondOperand, instruction);
|
||||
}
|
||||
call.setMethod(new MethodReference(JS.class, "sameRef", JSObject.class, JSObject.class,
|
||||
boolean.class));
|
||||
call.setArguments(firstOperand, secondOperand);
|
||||
call.setReceiver(conditionVar);
|
||||
instruction.insertPrevious(call);
|
||||
}
|
||||
|
||||
var newCondition = new BranchingInstruction(equal
|
||||
? BranchingCondition.NOT_EQUAL
|
||||
: BranchingCondition.EQUAL);
|
||||
newCondition.setOperand(call.getReceiver());
|
||||
newCondition.setConsequent(instruction.getConsequent());
|
||||
newCondition.setAlternative(instruction.getAlternative());
|
||||
newCondition.setLocation(instruction.getLocation());
|
||||
instruction.replace(newCondition);
|
||||
}
|
||||
}
|
||||
|
||||
private void processReferenceEquality(BranchingInstruction instruction) {
|
||||
if (!wasmGC) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean equal;
|
||||
switch (instruction.getCondition()) {
|
||||
case NULL:
|
||||
equal = true;
|
||||
break;
|
||||
case NOT_NULL:
|
||||
equal = false;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
var type = types.typeOf(instruction.getOperand());
|
||||
if (type == JSType.JS) {
|
||||
var call = new InvokeInstruction();
|
||||
call.setType(InvocationType.SPECIAL);
|
||||
call.setLocation(instruction.getLocation());
|
||||
call.setMethod(new MethodReference(JS.class, "isNull", JSObject.class, boolean.class));
|
||||
call.setArguments(instruction.getOperand());
|
||||
call.setReceiver(program.createVariable());
|
||||
instruction.insertPrevious(call);
|
||||
instruction.setOperand(call.getReceiver());
|
||||
instruction.setCondition(equal ? BranchingCondition.NOT_EQUAL : BranchingCondition.EQUAL);
|
||||
}
|
||||
}
|
||||
|
||||
private Variable convertToJs(Variable value, Instruction instruction) {
|
||||
var call = new InvokeInstruction();
|
||||
call.setType(InvocationType.SPECIAL);
|
||||
call.setMethod(new MethodReference(JS.class, "directJavaToJs", Object.class, JSObject.class));
|
||||
call.setArguments(value);
|
||||
call.setReceiver(program.createVariable());
|
||||
call.setLocation(instruction.getLocation());
|
||||
instruction.insertPrevious(call);
|
||||
return call.getReceiver();
|
||||
}
|
||||
|
||||
private ValueType processType(ValueType type) {
|
||||
return processType(typeHelper, type);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.teavm.jso.impl;
|
||||
|
||||
import org.teavm.classlib.PlatformDetector;
|
||||
import org.teavm.interop.Import;
|
||||
import org.teavm.interop.NoSideEffects;
|
||||
import org.teavm.jso.JSBody;
|
||||
|
@ -226,15 +227,40 @@ public final class JSWrapper {
|
|||
}
|
||||
|
||||
public static boolean isPrimitive(Object o, JSObject primitive) {
|
||||
if (PlatformDetector.isWebAssemblyGC()) {
|
||||
JSObject js;
|
||||
if (o instanceof JSWrapper) {
|
||||
js = ((JSWrapper) o).js;
|
||||
} else if (o instanceof JSMarshallable) {
|
||||
js = ((JSMarshallable) o).marshallToJs();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return JS.isPrimitive(js, primitive);
|
||||
}
|
||||
return isJs(o) && JS.isPrimitive(maybeUnwrap(o), primitive);
|
||||
}
|
||||
|
||||
public static boolean instanceOf(Object o, JSObject type) {
|
||||
if (PlatformDetector.isWebAssemblyGC()) {
|
||||
JSObject js;
|
||||
if (o instanceof JSWrapper) {
|
||||
js = ((JSWrapper) o).js;
|
||||
} else if (o instanceof JSMarshallable) {
|
||||
js = ((JSMarshallable) o).marshallToJs();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return JS.instanceOf(js, type);
|
||||
}
|
||||
return isJs(o) && JS.instanceOf(maybeUnwrap(o), type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (PlatformDetector.isWebAssemblyGC()) {
|
||||
return wasmGcHashCode(js);
|
||||
}
|
||||
var type = JSObjects.typeOf(js);
|
||||
if (type.equals("object") || type.equals("symbol") || type.equals("function")) {
|
||||
var code = Helper.hashCodes.get(js);
|
||||
|
@ -261,6 +287,9 @@ public final class JSWrapper {
|
|||
}
|
||||
}
|
||||
|
||||
@Import(name = "hashCode", module = "teavmJso")
|
||||
private static native int wasmGcHashCode(JSObject o);
|
||||
|
||||
@JSBody(params = "bigint", script = "return BigInt.asIntN(bigint, 32);")
|
||||
@NoSideEffects
|
||||
private static native int bigintTruncate(JSObject bigint);
|
||||
|
|
|
@ -51,6 +51,10 @@ public class JSWrapperDependency extends AbstractDependencyListener {
|
|||
case "unmarshallJavaFromJs":
|
||||
externalClassesNode.connect(method.getResult());
|
||||
break;
|
||||
case "wrap":
|
||||
method.getResult().propagate(agent.getType(JSWrapper.class.getName()));
|
||||
externalClassesNode.connect(method.getResult());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@ import static org.teavm.jso.impl.wasmgc.WasmGCJSConstants.JS_TO_STRING;
|
|||
import static org.teavm.jso.impl.wasmgc.WasmGCJSConstants.STRING_TO_JS;
|
||||
import org.teavm.dependency.AbstractDependencyListener;
|
||||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.impl.JSWrapper;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
class WasmGCJSDependencies extends AbstractDependencyListener {
|
||||
@Override
|
||||
|
@ -30,5 +33,8 @@ class WasmGCJSDependencies extends AbstractDependencyListener {
|
|||
var jsToString = agent.linkMethod(JS_TO_STRING);
|
||||
jsToString.getResult().propagate(agent.getType("java.lang.String"));
|
||||
jsToString.use();
|
||||
|
||||
agent.linkMethod(new MethodReference(JSWrapper.class, "createWrapper", JSObject.class, Object.class))
|
||||
.use();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,8 +22,13 @@ import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsic;
|
|||
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicContext;
|
||||
import org.teavm.backend.wasm.model.WasmFunction;
|
||||
import org.teavm.backend.wasm.model.WasmType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmBlock;
|
||||
import org.teavm.backend.wasm.model.expression.WasmBranch;
|
||||
import org.teavm.backend.wasm.model.expression.WasmCall;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIsNull;
|
||||
import org.teavm.backend.wasm.model.expression.WasmThrow;
|
||||
import org.teavm.backend.wasm.runtime.gc.WasmGCSupport;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.impl.JS;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
@ -47,6 +52,10 @@ class WasmGCJSIntrinsic implements WasmGCIntrinsic {
|
|||
var name = new WasmCall(stringToJs, context.generate(invocation.getArguments().get(0)));
|
||||
return new WasmCall(getGlobalFunction(context), name);
|
||||
}
|
||||
case "throwCCEIfFalse":
|
||||
return throwCCEIfFalse(context, invocation);
|
||||
case "isNull":
|
||||
return new WasmIsNull(context.generate(invocation.getArguments().get(0)));
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
@ -64,4 +73,24 @@ class WasmGCJSIntrinsic implements WasmGCIntrinsic {
|
|||
}
|
||||
return globalFunction;
|
||||
}
|
||||
|
||||
private WasmExpression throwCCEIfFalse(WasmGCIntrinsicContext context, InvocationExpr invocation) {
|
||||
var block = new WasmBlock(false);
|
||||
block.setType(WasmType.Reference.EXTERN);
|
||||
|
||||
var innerBlock = new WasmBlock(false);
|
||||
block.getBody().add(innerBlock);
|
||||
var br = new WasmBranch(context.generate(invocation.getArguments().get(0)), innerBlock);
|
||||
innerBlock.getBody().add(br);
|
||||
|
||||
var cceFunction = context.functions().forStaticMethod(new MethodReference(
|
||||
WasmGCSupport.class, "cce", ClassCastException.class));
|
||||
var cce = new WasmCall(cceFunction);
|
||||
var throwExpr = new WasmThrow(context.exceptionTag());
|
||||
throwExpr.getArguments().add(cce);
|
||||
innerBlock.getBody().add(throwExpr);
|
||||
|
||||
block.getBody().add(context.generate(invocation.getArguments().get(1)));
|
||||
return block;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright 2024 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.jso.impl.wasmgc;
|
||||
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsic;
|
||||
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicContext;
|
||||
import org.teavm.backend.wasm.model.WasmFunction;
|
||||
import org.teavm.backend.wasm.model.WasmType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmCall;
|
||||
import org.teavm.backend.wasm.model.expression.WasmCast;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExternConversion;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExternConversionType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmTest;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.impl.JSWrapper;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
class WasmGCJSWrapperIntrinsic implements WasmGCIntrinsic {
|
||||
private WasmFunction wrapFunction;
|
||||
|
||||
@Override
|
||||
public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) {
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "wrap": {
|
||||
var function = getWrapFunction(context);
|
||||
return new WasmCall(function, context.generate(invocation.getArguments().get(0)));
|
||||
}
|
||||
case "dependencyJavaToJs":
|
||||
case "directJavaToJs":
|
||||
return new WasmExternConversion(WasmExternConversionType.ANY_TO_EXTERN,
|
||||
context.generate(invocation.getArguments().get(0)));
|
||||
case "dependencyJsToJava":
|
||||
case "directJsToJava": {
|
||||
var any = new WasmExternConversion(WasmExternConversionType.EXTERN_TO_ANY,
|
||||
context.generate(invocation.getArguments().get(0)));
|
||||
var objectType = context.typeMapper().mapType(ValueType.parse(Object.class));
|
||||
return new WasmCast(any, (WasmType.Reference) objectType);
|
||||
}
|
||||
case "isJava": {
|
||||
var convert = new WasmExternConversion(WasmExternConversionType.EXTERN_TO_ANY,
|
||||
context.generate(invocation.getArguments().get(0)));
|
||||
var objectType = context.typeMapper().mapType(ValueType.parse(Object.class));
|
||||
return new WasmTest(convert, (WasmType.Reference) objectType);
|
||||
}
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
private WasmFunction getWrapFunction(WasmGCIntrinsicContext context) {
|
||||
if (wrapFunction == null) {
|
||||
var objectType = context.typeMapper().mapType(ValueType.parse(Object.class));
|
||||
wrapFunction = new WasmFunction(context.functionTypes().of(objectType, WasmType.Reference.EXTERN));
|
||||
wrapFunction.setImportName("wrapObject");
|
||||
wrapFunction.setImportModule("teavmJso");
|
||||
context.module().functions.add(wrapFunction);
|
||||
|
||||
var createWrapperFunction = context.functions().forStaticMethod(new MethodReference(
|
||||
JSWrapper.class, "createWrapper", JSObject.class, Object.class));
|
||||
createWrapperFunction.setExportName("teavm.jso.createWrapper");
|
||||
}
|
||||
return wrapFunction;
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ package org.teavm.jso.impl.wasmgc;
|
|||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.impl.JSMarshallable;
|
||||
import org.teavm.jso.impl.JSWrapper;
|
||||
import org.teavm.model.AccessLevel;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassHolderTransformerContext;
|
||||
|
@ -33,8 +34,11 @@ class WasmGCJSWrapperTransformer implements ClassHolderTransformer {
|
|||
if (cls.getName().equals(JSWrapper.class.getName())) {
|
||||
transformMarshallMethod(cls.getMethod(new MethodDescriptor("marshallJavaToJs", Object.class,
|
||||
JSObject.class)), context);
|
||||
transformWrapMethod(cls.getMethod(new MethodDescriptor("wrap", JSObject.class, Object.class)));
|
||||
transformIsJsImplementation(cls.getMethod(new MethodDescriptor("isJSImplementation",
|
||||
Object.class, boolean.class)), context);
|
||||
transformIsJava(cls.getMethod(new MethodDescriptor("isJava", Object.class, boolean.class)), context);
|
||||
addCreateWrapperMethod(cls, context);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,10 +49,30 @@ class WasmGCJSWrapperTransformer implements ClassHolderTransformer {
|
|||
obj.cast(JSMarshallable.class).invokeVirtual("marshallToJs", JSObject.class).returnValue();
|
||||
}
|
||||
|
||||
private void transformWrapMethod(MethodHolder method) {
|
||||
method.getModifiers().add(ElementModifier.NATIVE);
|
||||
method.setProgram(null);
|
||||
}
|
||||
|
||||
private void transformIsJsImplementation(MethodHolder method, ClassHolderTransformerContext context) {
|
||||
method.getModifiers().remove(ElementModifier.NATIVE);
|
||||
var pe = ProgramEmitter.create(method, context.getHierarchy());
|
||||
var obj = pe.var(1, JSObject.class);
|
||||
obj.instanceOf(ValueType.parse(JSMarshallable.class)).returnValue();
|
||||
}
|
||||
|
||||
private void addCreateWrapperMethod(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||
var method = new MethodHolder(new MethodDescriptor("createWrapper", JSObject.class, Object.class));
|
||||
method.getModifiers().add(ElementModifier.STATIC);
|
||||
method.setLevel(AccessLevel.PUBLIC);
|
||||
var pe = ProgramEmitter.create(method, context.getHierarchy());
|
||||
pe.construct(JSWrapper.class, pe.var(1, JSObject.class)).returnValue();
|
||||
cls.addMethod(method);
|
||||
}
|
||||
|
||||
private void transformIsJava(MethodHolder method, ClassHolderTransformerContext context) {
|
||||
method.getModifiers().remove(ElementModifier.NATIVE);
|
||||
var pe = ProgramEmitter.create(method, context.getHierarchy());
|
||||
pe.constant(1).returnValue();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.teavm.backend.wasm.gc.TeaVMWasmGCHost;
|
|||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.impl.JS;
|
||||
import org.teavm.jso.impl.JSBodyRepository;
|
||||
import org.teavm.jso.impl.JSWrapper;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.vm.spi.TeaVMHost;
|
||||
|
||||
|
@ -40,5 +41,22 @@ public final class WasmGCJso {
|
|||
wasmGCHost.addIntrinsic(new MethodReference(JS.class, "unwrapString", JSObject.class, String.class),
|
||||
jsIntrinsic);
|
||||
wasmGCHost.addIntrinsic(new MethodReference(JS.class, "global", String.class, JSObject.class), jsIntrinsic);
|
||||
wasmGCHost.addIntrinsic(new MethodReference(JS.class, "throwCCEIfFalse", boolean.class, JSObject.class,
|
||||
JSObject.class), jsIntrinsic);
|
||||
wasmGCHost.addIntrinsic(new MethodReference(JS.class, "isNull", JSObject.class, boolean.class), jsIntrinsic);
|
||||
|
||||
var wrapperIntrinsic = new WasmGCJSWrapperIntrinsic();
|
||||
wasmGCHost.addIntrinsic(new MethodReference(JSWrapper.class, "wrap", JSObject.class, Object.class),
|
||||
wrapperIntrinsic);
|
||||
wasmGCHost.addIntrinsic(new MethodReference(JSWrapper.class, "dependencyJavaToJs", Object.class,
|
||||
JSObject.class), wrapperIntrinsic);
|
||||
wasmGCHost.addIntrinsic(new MethodReference(JSWrapper.class, "directJavaToJs", Object.class, JSObject.class),
|
||||
wrapperIntrinsic);
|
||||
wasmGCHost.addIntrinsic(new MethodReference(JSWrapper.class, "dependencyJsToJava", JSObject.class,
|
||||
Object.class), wrapperIntrinsic);
|
||||
wasmGCHost.addIntrinsic(new MethodReference(JSWrapper.class, "directJsToJava", JSObject.class, Object.class),
|
||||
wrapperIntrinsic);
|
||||
wasmGCHost.addIntrinsic(new MethodReference(JSWrapper.class, "isJava", JSObject.class, boolean.class),
|
||||
wrapperIntrinsic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,11 +42,19 @@ val generatedCSources = File(buildDir, "generated/teavm-c")
|
|||
val executableFile = File(buildDir, "dist/teavm_benchmark")
|
||||
|
||||
teavm {
|
||||
all {
|
||||
outOfProcess = true
|
||||
processMemory = 1024
|
||||
}
|
||||
js {
|
||||
addedToWebApp = true
|
||||
mainClass = "org.teavm.samples.benchmark.teavm.BenchmarkStarter"
|
||||
debugInformation = true
|
||||
}
|
||||
wasmGC {
|
||||
addedToWebApp = true
|
||||
mainClass = "org.teavm.samples.benchmark.teavm.BenchmarkStarter"
|
||||
}
|
||||
wasm {
|
||||
addedToWebApp = true
|
||||
mainClass = "org.teavm.samples.benchmark.teavm.WasmBenchmarkStarter"
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
<li><a href="teavm.html">TeaVM</a></li>
|
||||
<li><a href="gwt.html">GWT</a></li>
|
||||
<li><a href="teavm-wasm.html">TeaVM (experimental WebAssembly backend)</a></li>
|
||||
<li><a href="teavm-wasm-gc.html">TeaVM (experimental WebAssembly GC backend)</a></li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
54
samples/benchmark/src/main/webapp/teavm-wasm-gc.html
Normal file
54
samples/benchmark/src/main/webapp/teavm-wasm-gc.html
Normal file
|
@ -0,0 +1,54 @@
|
|||
<!--
|
||||
Copyright 2014 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.
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||
<title>TeaVM jbox2d benchmark</title>
|
||||
<script type="text/javascript" charset="utf-8" src="wasm-gc/benchmark.wasm-runtime.js"></script>
|
||||
</head>
|
||||
<script type="text/javascript">
|
||||
function launch() {
|
||||
TeaVM.wasm.load("wasm-gc/benchmark.wasm").then(teavm => teavm.main());
|
||||
}
|
||||
</script>
|
||||
<body onload="launch()">
|
||||
<h1>TeaVM performance</h1>
|
||||
<div>
|
||||
<canvas id="benchmark-canvas" width="600" height="600"></canvas>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="display-animation-checkbox" checked>
|
||||
<label for="display-animation-checkbox">display animation</label>
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Second</th>
|
||||
<th>Time spent computing, ms</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="result-table-body">
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th>Average</th>
|
||||
<td id="average-time"></td>
|
||||
<tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
|
@ -46,7 +46,6 @@ public class FunctorTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.WEBASSEMBLY_GC)
|
||||
public void functorIdentityPreserved() {
|
||||
JSBiFunction javaFunction = (a, b) -> a + b;
|
||||
JSObject firstRef = getFunction(javaFunction);
|
||||
|
|
|
@ -36,12 +36,13 @@ import org.teavm.jso.core.JSUndefined;
|
|||
import org.teavm.junit.EachTestCompiledSeparately;
|
||||
import org.teavm.junit.OnlyPlatform;
|
||||
import org.teavm.junit.SkipJVM;
|
||||
import org.teavm.junit.SkipPlatform;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
import org.teavm.junit.TestPlatform;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@SkipJVM
|
||||
@OnlyPlatform(TestPlatform.JAVASCRIPT)
|
||||
@OnlyPlatform({TestPlatform.JAVASCRIPT, TestPlatform.WEBASSEMBLY_GC})
|
||||
@EachTestCompiledSeparately
|
||||
public class JSWrapperTest {
|
||||
private List<Object> list = new ArrayList<>();
|
||||
|
@ -294,6 +295,7 @@ public class JSWrapperTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.WEBASSEMBLY_GC)
|
||||
public void createArray() {
|
||||
var array = new J[] {
|
||||
new JImpl(23),
|
||||
|
@ -305,6 +307,7 @@ public class JSWrapperTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.WEBASSEMBLY_GC)
|
||||
public void createArrayAndReturnToJS() {
|
||||
assertEquals("23,42", concatFoo(() -> new J[] {
|
||||
new JImpl(23),
|
||||
|
|
Loading…
Reference in New Issue
Block a user