mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -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:
|
case IS_NULL:
|
||||||
writer.write("ref.is_null");
|
writer.write("ref.is_null");
|
||||||
break;
|
break;
|
||||||
|
case EXTERN_TO_ANY:
|
||||||
|
writer.write("any.convert_extern");
|
||||||
|
break;
|
||||||
|
case ANY_TO_EXTERN:
|
||||||
|
writer.write("extern.convert_any");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
writer.eol();
|
writer.eol();
|
||||||
}
|
}
|
||||||
|
|
|
@ -277,6 +277,11 @@ public class WasmDefaultExpressionVisitor implements WasmExpressionVisitor {
|
||||||
expression.getValue().acceptVisitor(this);
|
expression.getValue().acceptVisitor(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(WasmExternConversion expression) {
|
||||||
|
expression.getValue().acceptVisitor(this);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(WasmStructNew expression) {
|
public void visit(WasmStructNew expression) {
|
||||||
for (var initializer : expression.getInitializers()) {
|
for (var initializer : expression.getInitializers()) {
|
||||||
|
|
|
@ -102,6 +102,8 @@ public interface WasmExpressionVisitor {
|
||||||
|
|
||||||
void visit(WasmCast expression);
|
void visit(WasmCast expression);
|
||||||
|
|
||||||
|
void visit(WasmExternConversion expression);
|
||||||
|
|
||||||
void visit(WasmTest expression);
|
void visit(WasmTest expression);
|
||||||
|
|
||||||
void visit(WasmStructNew expression);
|
void visit(WasmStructNew expression);
|
||||||
|
|
|
@ -15,5 +15,35 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.wasm.model.expression;
|
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;
|
package org.teavm.backend.wasm.model.expression;
|
||||||
|
|
||||||
public enum WasmExternConversionType {
|
public enum WasmExternConversionType {
|
||||||
EXTERN_TO_OBJECT,
|
EXTERN_TO_ANY,
|
||||||
OBJECT_TO_EXTERN
|
ANY_TO_EXTERN
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,7 @@ import org.teavm.backend.wasm.model.WasmFunction;
|
||||||
public class WasmReplacingExpressionVisitor implements WasmExpressionVisitor {
|
public class WasmReplacingExpressionVisitor implements WasmExpressionVisitor {
|
||||||
private Function<WasmExpression, WasmExpression> mapper;
|
private Function<WasmExpression, WasmExpression> mapper;
|
||||||
|
|
||||||
public WasmReplacingExpressionVisitor(
|
public WasmReplacingExpressionVisitor(Function<WasmExpression, WasmExpression> mapper) {
|
||||||
Function<WasmExpression, WasmExpression> mapper) {
|
|
||||||
this.mapper = mapper;
|
this.mapper = mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,6 +331,12 @@ public class WasmReplacingExpressionVisitor implements WasmExpressionVisitor {
|
||||||
expression.setValue(mapper.apply(expression.getValue()));
|
expression.setValue(mapper.apply(expression.getValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(WasmExternConversion expression) {
|
||||||
|
expression.getValue().acceptVisitor(this);
|
||||||
|
expression.setValue(mapper.apply(expression.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(WasmStructNew expression) {
|
public void visit(WasmStructNew expression) {
|
||||||
replaceExpressions(expression.getInitializers());
|
replaceExpressions(expression.getInitializers());
|
||||||
|
|
|
@ -719,6 +719,13 @@ public class CodeParser extends BaseSectionParser {
|
||||||
parseCastBranch(false);
|
parseCastBranch(false);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case 26:
|
||||||
|
codeListener.opcode(Opcode.EXTERN_TO_ANY);
|
||||||
|
return true;
|
||||||
|
case 27:
|
||||||
|
codeListener.opcode(Opcode.ANY_TO_EXTERN);
|
||||||
|
return true;
|
||||||
|
|
||||||
case 28:
|
case 28:
|
||||||
codeListener.int31Reference();
|
codeListener.int31Reference();
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -22,5 +22,7 @@ public enum Opcode {
|
||||||
DROP,
|
DROP,
|
||||||
REF_EQ,
|
REF_EQ,
|
||||||
ARRAY_LENGTH,
|
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.WasmDrop;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmExpressionVisitor;
|
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.WasmFill;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmFloat32Constant;
|
import org.teavm.backend.wasm.model.expression.WasmFloat32Constant;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmFloat64Constant;
|
import org.teavm.backend.wasm.model.expression.WasmFloat64Constant;
|
||||||
|
@ -1171,6 +1172,22 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
|
||||||
popLocation();
|
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
|
@Override
|
||||||
public void visit(WasmStructNew expression) {
|
public void visit(WasmStructNew expression) {
|
||||||
pushLocation(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.WasmDrop;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmExpressionVisitor;
|
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.WasmFill;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmFloat32Constant;
|
import org.teavm.backend.wasm.model.expression.WasmFloat32Constant;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmFloat64Constant;
|
import org.teavm.backend.wasm.model.expression.WasmFloat64Constant;
|
||||||
|
@ -1197,6 +1198,11 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor {
|
||||||
unsupported();
|
unsupported();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(WasmExternConversion expression) {
|
||||||
|
unsupported();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(WasmStructNew expression) {
|
public void visit(WasmStructNew expression) {
|
||||||
unsupported();
|
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.WasmDrop;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmExpressionVisitor;
|
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.WasmFill;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmFloat32Constant;
|
import org.teavm.backend.wasm.model.expression.WasmFloat32Constant;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmFloat64Constant;
|
import org.teavm.backend.wasm.model.expression.WasmFloat64Constant;
|
||||||
|
@ -772,6 +773,21 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
|
||||||
close();
|
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
|
@Override
|
||||||
public void visit(WasmStructNew expression) {
|
public void visit(WasmStructNew expression) {
|
||||||
open().append("struct.new ");
|
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.WasmCopy;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmDrop;
|
import org.teavm.backend.wasm.model.expression.WasmDrop;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmExpressionVisitor;
|
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.WasmFill;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmFloat32Constant;
|
import org.teavm.backend.wasm.model.expression.WasmFloat32Constant;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmFloat64Constant;
|
import org.teavm.backend.wasm.model.expression.WasmFloat64Constant;
|
||||||
|
@ -317,6 +318,18 @@ public class WasmTypeInference implements WasmExpressionVisitor {
|
||||||
result = WasmType.INT32;
|
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
|
@Override
|
||||||
public void visit(WasmStructNew expression) {
|
public void visit(WasmStructNew expression) {
|
||||||
result = expression.getType().getReference();
|
result = expression.getType().getReference();
|
||||||
|
|
|
@ -278,7 +278,6 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
||||||
|
|
||||||
public void addDependencyListener(DependencyListener listener) {
|
public void addDependencyListener(DependencyListener listener) {
|
||||||
listeners.add(listener);
|
listeners.add(listener);
|
||||||
listener.started(agent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addClassTransformer(ClassHolderTransformer transformer) {
|
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() {
|
public void processDependencies() {
|
||||||
interrupted = false;
|
interrupted = false;
|
||||||
processQueue();
|
processQueue();
|
||||||
|
|
|
@ -41,6 +41,10 @@ public class BranchingInstruction extends Instruction {
|
||||||
return condition;
|
return condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setCondition(BranchingCondition condition) {
|
||||||
|
this.condition = condition;
|
||||||
|
}
|
||||||
|
|
||||||
public BasicBlock getConsequent() {
|
public BasicBlock getConsequent() {
|
||||||
return consequent;
|
return consequent;
|
||||||
}
|
}
|
||||||
|
|
|
@ -318,11 +318,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var mainMethod = cls.getMethod(MAIN_METHOD_DESC) != null
|
|
||||||
? dependencyAnalyzer.linkMethod(new MethodReference(entryPoint,
|
|
||||||
"main", ValueType.parse(String[].class), ValueType.VOID))
|
|
||||||
: null;
|
|
||||||
dependencyAnalyzer.defer(() -> {
|
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);
|
dependencyAnalyzer.linkClass(entryPoint).initClass(null);
|
||||||
if (mainMethod != null) {
|
if (mainMethod != null) {
|
||||||
mainMethod.getVariable(1).propagate(dependencyAnalyzer.getType("[Ljava/lang/String;"));
|
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;
|
cancelled |= progressListener.progressReached(progress) != TeaVMProgressFeedback.CONTINUE;
|
||||||
return !cancelled;
|
return !cancelled;
|
||||||
});
|
});
|
||||||
|
dependencyAnalyzer.initDependencies();
|
||||||
target.contributeDependencies(dependencyAnalyzer);
|
target.contributeDependencies(dependencyAnalyzer);
|
||||||
if (target.needsSystemArrayCopyOptimization()) {
|
if (target.needsSystemArrayCopyOptimization()) {
|
||||||
dependencyAnalyzer.addDependencyListener(new StdlibDependencyListener());
|
dependencyAnalyzer.addDependencyListener(new StdlibDependencyListener());
|
||||||
|
|
|
@ -20,21 +20,16 @@ TeaVM.wasm = function() {
|
||||||
let getGlobalName = function(name) {
|
let getGlobalName = function(name) {
|
||||||
return eval(name);
|
return eval(name);
|
||||||
}
|
}
|
||||||
let javaObjectSymbol = Symbol("javaObject");
|
|
||||||
let functionsSymbol = Symbol("functions");
|
|
||||||
let functionOriginSymbol = Symbol("functionOrigin");
|
|
||||||
let javaWrappers = new WeakMap();
|
|
||||||
function defaults(imports) {
|
function defaults(imports) {
|
||||||
let stderr = "";
|
dateImports(imports);
|
||||||
let stdout = "";
|
consoleImports(imports);
|
||||||
let finalizationRegistry = new FinalizationRegistry(heldValue => {
|
coreImports(imports);
|
||||||
if (typeof exports.reportGarbageCollectedValue === "function") {
|
jsoImports(imports);
|
||||||
exports.reportGarbageCollectedValue(heldValue)
|
imports.teavmMath = Math;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
let stringFinalizationRegistry = new FinalizationRegistry(heldValue => {
|
function dateImports(imports) {
|
||||||
exports.reportGarbageCollectedString(heldValue);
|
|
||||||
});
|
|
||||||
imports.teavmDate = {
|
imports.teavmDate = {
|
||||||
currentTimeMillis: () => new Date().getTime(),
|
currentTimeMillis: () => new Date().getTime(),
|
||||||
dateToString: timestamp => stringToJava(new Date(timestamp).toString()),
|
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(),
|
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)
|
createFromUTC: (year, month, date, hrs, min, sec) => Date.UTC(year, month, date, hrs, min, sec)
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function consoleImports(imports) {
|
||||||
|
let stderr = "";
|
||||||
|
let stdout = "";
|
||||||
imports.teavmConsole = {
|
imports.teavmConsole = {
|
||||||
putcharStderr(c) {
|
putcharStderr(c) {
|
||||||
if (c === 10) {
|
if (c === 10) {
|
||||||
|
@ -77,6 +77,9 @@ TeaVM.wasm = function() {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function coreImports(imports) {
|
||||||
imports.teavm = {
|
imports.teavm = {
|
||||||
createWeakRef(value, heldValue) {
|
createWeakRef(value, heldValue) {
|
||||||
let weakRef = new WeakRef(value);
|
let weakRef = new WeakRef(value);
|
||||||
|
@ -93,6 +96,37 @@ TeaVM.wasm = function() {
|
||||||
},
|
},
|
||||||
stringDeref: weakRef => weakRef.deref()
|
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) {
|
function identity(value) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
@ -157,7 +191,7 @@ TeaVM.wasm = function() {
|
||||||
Object.defineProperty(cls.prototype, name, descriptor);
|
Object.defineProperty(cls.prototype, name, descriptor);
|
||||||
},
|
},
|
||||||
javaObjectToJS(instance, cls) {
|
javaObjectToJS(instance, cls) {
|
||||||
let existing = javaWrappers.get(instance);
|
let existing = jsWrappers.get(instance);
|
||||||
if (typeof existing != "undefined") {
|
if (typeof existing != "undefined") {
|
||||||
let result = existing.deref();
|
let result = existing.deref();
|
||||||
if (typeof result !== "undefined") {
|
if (typeof result !== "undefined") {
|
||||||
|
@ -165,7 +199,7 @@ TeaVM.wasm = function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let obj = new cls(instance);
|
let obj = new cls(instance);
|
||||||
javaWrappers.set(instance, new WeakRef(obj));
|
jsWrappers.set(instance, new WeakRef(obj));
|
||||||
return obj;
|
return obj;
|
||||||
},
|
},
|
||||||
unwrapJavaObject(instance) {
|
unwrapJavaObject(instance) {
|
||||||
|
@ -196,10 +230,64 @@ TeaVM.wasm = function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { [property]: fn };
|
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",
|
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;
|
imports.teavmJso[name] = identity;
|
||||||
}
|
}
|
||||||
for (let i = 0; i < 32; ++i) {
|
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["callMethod" + i] = (instance, method, ...args) => instance[method](...args);
|
||||||
imports.teavmJso["construct" + i] = (constructor, ...args) => new constructor(...args);
|
imports.teavmJso["construct" + i] = (constructor, ...args) => new constructor(...args);
|
||||||
}
|
}
|
||||||
imports.teavmMath = Math;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function load(path, options) {
|
function load(path, options) {
|
||||||
|
|
|
@ -764,6 +764,7 @@ public final class JS {
|
||||||
|
|
||||||
@InjectedBy(JSNativeInjector.class)
|
@InjectedBy(JSNativeInjector.class)
|
||||||
@NoSideEffects
|
@NoSideEffects
|
||||||
|
@Import(name = "isPrimitive", module = "teavmJso")
|
||||||
public static native boolean isPrimitive(JSObject obj, JSObject primitive);
|
public static native boolean isPrimitive(JSObject obj, JSObject primitive);
|
||||||
|
|
||||||
@InjectedBy(JSNativeInjector.class)
|
@InjectedBy(JSNativeInjector.class)
|
||||||
|
@ -773,4 +774,13 @@ public final class JS {
|
||||||
@InjectedBy(JSNativeInjector.class)
|
@InjectedBy(JSNativeInjector.class)
|
||||||
@NoSideEffects
|
@NoSideEffects
|
||||||
public static native JSObject argumentsBeginningAt(int index);
|
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.Variable;
|
||||||
import org.teavm.model.instructions.ArrayElementType;
|
import org.teavm.model.instructions.ArrayElementType;
|
||||||
import org.teavm.model.instructions.AssignInstruction;
|
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.CastInstruction;
|
||||||
import org.teavm.model.instructions.ClassConstantInstruction;
|
import org.teavm.model.instructions.ClassConstantInstruction;
|
||||||
import org.teavm.model.instructions.ConstructArrayInstruction;
|
import org.teavm.model.instructions.ConstructArrayInstruction;
|
||||||
|
@ -333,6 +336,10 @@ class JSClassProcessor {
|
||||||
if (nativeConstructedObjects[index]) {
|
if (nativeConstructedObjects[index]) {
|
||||||
assign.delete();
|
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) {
|
private ValueType processType(ValueType type) {
|
||||||
return processType(typeHelper, type);
|
return processType(typeHelper, type);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.jso.impl;
|
package org.teavm.jso.impl;
|
||||||
|
|
||||||
|
import org.teavm.classlib.PlatformDetector;
|
||||||
import org.teavm.interop.Import;
|
import org.teavm.interop.Import;
|
||||||
import org.teavm.interop.NoSideEffects;
|
import org.teavm.interop.NoSideEffects;
|
||||||
import org.teavm.jso.JSBody;
|
import org.teavm.jso.JSBody;
|
||||||
|
@ -226,15 +227,40 @@ public final class JSWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isPrimitive(Object o, JSObject primitive) {
|
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);
|
return isJs(o) && JS.isPrimitive(maybeUnwrap(o), primitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean instanceOf(Object o, JSObject type) {
|
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);
|
return isJs(o) && JS.instanceOf(maybeUnwrap(o), type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
|
if (PlatformDetector.isWebAssemblyGC()) {
|
||||||
|
return wasmGcHashCode(js);
|
||||||
|
}
|
||||||
var type = JSObjects.typeOf(js);
|
var type = JSObjects.typeOf(js);
|
||||||
if (type.equals("object") || type.equals("symbol") || type.equals("function")) {
|
if (type.equals("object") || type.equals("symbol") || type.equals("function")) {
|
||||||
var code = Helper.hashCodes.get(js);
|
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);")
|
@JSBody(params = "bigint", script = "return BigInt.asIntN(bigint, 32);")
|
||||||
@NoSideEffects
|
@NoSideEffects
|
||||||
private static native int bigintTruncate(JSObject bigint);
|
private static native int bigintTruncate(JSObject bigint);
|
||||||
|
|
|
@ -51,6 +51,10 @@ public class JSWrapperDependency extends AbstractDependencyListener {
|
||||||
case "unmarshallJavaFromJs":
|
case "unmarshallJavaFromJs":
|
||||||
externalClassesNode.connect(method.getResult());
|
externalClassesNode.connect(method.getResult());
|
||||||
break;
|
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 static org.teavm.jso.impl.wasmgc.WasmGCJSConstants.STRING_TO_JS;
|
||||||
import org.teavm.dependency.AbstractDependencyListener;
|
import org.teavm.dependency.AbstractDependencyListener;
|
||||||
import org.teavm.dependency.DependencyAgent;
|
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 {
|
class WasmGCJSDependencies extends AbstractDependencyListener {
|
||||||
@Override
|
@Override
|
||||||
|
@ -30,5 +33,8 @@ class WasmGCJSDependencies extends AbstractDependencyListener {
|
||||||
var jsToString = agent.linkMethod(JS_TO_STRING);
|
var jsToString = agent.linkMethod(JS_TO_STRING);
|
||||||
jsToString.getResult().propagate(agent.getType("java.lang.String"));
|
jsToString.getResult().propagate(agent.getType("java.lang.String"));
|
||||||
jsToString.use();
|
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.intrinsics.gc.WasmGCIntrinsicContext;
|
||||||
import org.teavm.backend.wasm.model.WasmFunction;
|
import org.teavm.backend.wasm.model.WasmFunction;
|
||||||
import org.teavm.backend.wasm.model.WasmType;
|
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.WasmCall;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
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.JSObject;
|
||||||
import org.teavm.jso.impl.JS;
|
import org.teavm.jso.impl.JS;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
@ -47,6 +52,10 @@ class WasmGCJSIntrinsic implements WasmGCIntrinsic {
|
||||||
var name = new WasmCall(stringToJs, context.generate(invocation.getArguments().get(0)));
|
var name = new WasmCall(stringToJs, context.generate(invocation.getArguments().get(0)));
|
||||||
return new WasmCall(getGlobalFunction(context), name);
|
return new WasmCall(getGlobalFunction(context), name);
|
||||||
}
|
}
|
||||||
|
case "throwCCEIfFalse":
|
||||||
|
return throwCCEIfFalse(context, invocation);
|
||||||
|
case "isNull":
|
||||||
|
return new WasmIsNull(context.generate(invocation.getArguments().get(0)));
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
|
@ -64,4 +73,24 @@ class WasmGCJSIntrinsic implements WasmGCIntrinsic {
|
||||||
}
|
}
|
||||||
return globalFunction;
|
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.JSObject;
|
||||||
import org.teavm.jso.impl.JSMarshallable;
|
import org.teavm.jso.impl.JSMarshallable;
|
||||||
import org.teavm.jso.impl.JSWrapper;
|
import org.teavm.jso.impl.JSWrapper;
|
||||||
|
import org.teavm.model.AccessLevel;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
import org.teavm.model.ClassHolderTransformerContext;
|
import org.teavm.model.ClassHolderTransformerContext;
|
||||||
|
@ -33,8 +34,11 @@ class WasmGCJSWrapperTransformer implements ClassHolderTransformer {
|
||||||
if (cls.getName().equals(JSWrapper.class.getName())) {
|
if (cls.getName().equals(JSWrapper.class.getName())) {
|
||||||
transformMarshallMethod(cls.getMethod(new MethodDescriptor("marshallJavaToJs", Object.class,
|
transformMarshallMethod(cls.getMethod(new MethodDescriptor("marshallJavaToJs", Object.class,
|
||||||
JSObject.class)), context);
|
JSObject.class)), context);
|
||||||
|
transformWrapMethod(cls.getMethod(new MethodDescriptor("wrap", JSObject.class, Object.class)));
|
||||||
transformIsJsImplementation(cls.getMethod(new MethodDescriptor("isJSImplementation",
|
transformIsJsImplementation(cls.getMethod(new MethodDescriptor("isJSImplementation",
|
||||||
Object.class, boolean.class)), context);
|
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();
|
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) {
|
private void transformIsJsImplementation(MethodHolder method, ClassHolderTransformerContext context) {
|
||||||
method.getModifiers().remove(ElementModifier.NATIVE);
|
method.getModifiers().remove(ElementModifier.NATIVE);
|
||||||
var pe = ProgramEmitter.create(method, context.getHierarchy());
|
var pe = ProgramEmitter.create(method, context.getHierarchy());
|
||||||
var obj = pe.var(1, JSObject.class);
|
var obj = pe.var(1, JSObject.class);
|
||||||
obj.instanceOf(ValueType.parse(JSMarshallable.class)).returnValue();
|
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.JSObject;
|
||||||
import org.teavm.jso.impl.JS;
|
import org.teavm.jso.impl.JS;
|
||||||
import org.teavm.jso.impl.JSBodyRepository;
|
import org.teavm.jso.impl.JSBodyRepository;
|
||||||
|
import org.teavm.jso.impl.JSWrapper;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.vm.spi.TeaVMHost;
|
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),
|
wasmGCHost.addIntrinsic(new MethodReference(JS.class, "unwrapString", JSObject.class, String.class),
|
||||||
jsIntrinsic);
|
jsIntrinsic);
|
||||||
wasmGCHost.addIntrinsic(new MethodReference(JS.class, "global", String.class, JSObject.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")
|
val executableFile = File(buildDir, "dist/teavm_benchmark")
|
||||||
|
|
||||||
teavm {
|
teavm {
|
||||||
|
all {
|
||||||
|
outOfProcess = true
|
||||||
|
processMemory = 1024
|
||||||
|
}
|
||||||
js {
|
js {
|
||||||
addedToWebApp = true
|
addedToWebApp = true
|
||||||
mainClass = "org.teavm.samples.benchmark.teavm.BenchmarkStarter"
|
mainClass = "org.teavm.samples.benchmark.teavm.BenchmarkStarter"
|
||||||
debugInformation = true
|
debugInformation = true
|
||||||
}
|
}
|
||||||
|
wasmGC {
|
||||||
|
addedToWebApp = true
|
||||||
|
mainClass = "org.teavm.samples.benchmark.teavm.BenchmarkStarter"
|
||||||
|
}
|
||||||
wasm {
|
wasm {
|
||||||
addedToWebApp = true
|
addedToWebApp = true
|
||||||
mainClass = "org.teavm.samples.benchmark.teavm.WasmBenchmarkStarter"
|
mainClass = "org.teavm.samples.benchmark.teavm.WasmBenchmarkStarter"
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
<li><a href="teavm.html">TeaVM</a></li>
|
<li><a href="teavm.html">TeaVM</a></li>
|
||||||
<li><a href="gwt.html">GWT</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.html">TeaVM (experimental WebAssembly backend)</a></li>
|
||||||
|
<li><a href="teavm-wasm-gc.html">TeaVM (experimental WebAssembly GC backend)</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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
|
@Test
|
||||||
@SkipPlatform(TestPlatform.WEBASSEMBLY_GC)
|
|
||||||
public void functorIdentityPreserved() {
|
public void functorIdentityPreserved() {
|
||||||
JSBiFunction javaFunction = (a, b) -> a + b;
|
JSBiFunction javaFunction = (a, b) -> a + b;
|
||||||
JSObject firstRef = getFunction(javaFunction);
|
JSObject firstRef = getFunction(javaFunction);
|
||||||
|
|
|
@ -36,12 +36,13 @@ import org.teavm.jso.core.JSUndefined;
|
||||||
import org.teavm.junit.EachTestCompiledSeparately;
|
import org.teavm.junit.EachTestCompiledSeparately;
|
||||||
import org.teavm.junit.OnlyPlatform;
|
import org.teavm.junit.OnlyPlatform;
|
||||||
import org.teavm.junit.SkipJVM;
|
import org.teavm.junit.SkipJVM;
|
||||||
|
import org.teavm.junit.SkipPlatform;
|
||||||
import org.teavm.junit.TeaVMTestRunner;
|
import org.teavm.junit.TeaVMTestRunner;
|
||||||
import org.teavm.junit.TestPlatform;
|
import org.teavm.junit.TestPlatform;
|
||||||
|
|
||||||
@RunWith(TeaVMTestRunner.class)
|
@RunWith(TeaVMTestRunner.class)
|
||||||
@SkipJVM
|
@SkipJVM
|
||||||
@OnlyPlatform(TestPlatform.JAVASCRIPT)
|
@OnlyPlatform({TestPlatform.JAVASCRIPT, TestPlatform.WEBASSEMBLY_GC})
|
||||||
@EachTestCompiledSeparately
|
@EachTestCompiledSeparately
|
||||||
public class JSWrapperTest {
|
public class JSWrapperTest {
|
||||||
private List<Object> list = new ArrayList<>();
|
private List<Object> list = new ArrayList<>();
|
||||||
|
@ -294,6 +295,7 @@ public class JSWrapperTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@SkipPlatform(TestPlatform.WEBASSEMBLY_GC)
|
||||||
public void createArray() {
|
public void createArray() {
|
||||||
var array = new J[] {
|
var array = new J[] {
|
||||||
new JImpl(23),
|
new JImpl(23),
|
||||||
|
@ -305,6 +307,7 @@ public class JSWrapperTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@SkipPlatform(TestPlatform.WEBASSEMBLY_GC)
|
||||||
public void createArrayAndReturnToJS() {
|
public void createArrayAndReturnToJS() {
|
||||||
assertEquals("23,42", concatFoo(() -> new J[] {
|
assertEquals("23,42", concatFoo(() -> new J[] {
|
||||||
new JImpl(23),
|
new JImpl(23),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user