mirror of
https://github.com/konsoletyper/teavm.git
synced 2024-11-27 01:30:35 +08:00
wasm gc: add support for imports from JS
This commit is contained in:
parent
1fadc71536
commit
e4a2550cc6
@ -51,6 +51,7 @@ public final class Disassembler {
|
||||
private WasmHollowFunctionType[] functionTypes;
|
||||
private int[] functionTypeRefs;
|
||||
private int importFunctionCount;
|
||||
private int importGlobalCount;
|
||||
private Map<String, DebugSectionParser> debugSectionParsers = new HashMap<>();
|
||||
private DebugLinesParser debugLines;
|
||||
private LineInfo lineInfo;
|
||||
@ -143,6 +144,7 @@ public final class Disassembler {
|
||||
var parser = new ImportSectionParser(importListener);
|
||||
parser.parse(AddressListener.EMPTY, bytes);
|
||||
importFunctionCount = importListener.functionCount();
|
||||
importGlobalCount = importListener.globalCount();
|
||||
};
|
||||
} else if (code == 3) {
|
||||
return bytes -> {
|
||||
@ -165,6 +167,7 @@ public final class Disassembler {
|
||||
var globalWriter = new DisassemblyGlobalSectionListener(writer, nameProvider);
|
||||
writer.setAddressOffset(pos);
|
||||
var sectionParser = new GlobalSectionParser(globalWriter);
|
||||
sectionParser.setGlobalIndexOffset(importGlobalCount);
|
||||
sectionParser.parse(writer.addressListener, bytes);
|
||||
writer.flush();
|
||||
};
|
||||
|
@ -17,12 +17,14 @@ package org.teavm.backend.wasm.disasm;
|
||||
|
||||
import org.teavm.backend.wasm.parser.ImportSectionListener;
|
||||
import org.teavm.backend.wasm.parser.WasmHollowFunctionType;
|
||||
import org.teavm.backend.wasm.parser.WasmHollowType;
|
||||
|
||||
public class DisassemblyImportSectionListener extends BaseDisassemblyListener implements ImportSectionListener {
|
||||
private WasmHollowFunctionType[] functionTypes;
|
||||
private String currentModule;
|
||||
private String currentName;
|
||||
private int functionIndex;
|
||||
private int globalIndex;
|
||||
|
||||
public DisassemblyImportSectionListener(DisassemblyWriter writer, NameProvider nameProvider,
|
||||
WasmHollowFunctionType[] functionTypes) {
|
||||
@ -34,6 +36,10 @@ public class DisassemblyImportSectionListener extends BaseDisassemblyListener im
|
||||
return functionIndex;
|
||||
}
|
||||
|
||||
public int globalCount() {
|
||||
return globalIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startEntry(String module, String name) {
|
||||
currentModule = module;
|
||||
@ -81,4 +87,21 @@ public class DisassemblyImportSectionListener extends BaseDisassemblyListener im
|
||||
|
||||
functionIndex++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void global(WasmHollowType type) {
|
||||
writer.address().write("(import \"").write(currentModule).write("\" \"")
|
||||
.write(currentName).write("\" ");
|
||||
writer.write("(global ");
|
||||
writer.startLinkTarget("g" + globalIndex).write("(; " + globalIndex + " ;)");
|
||||
var name = nameProvider.global(globalIndex);
|
||||
if (name != null) {
|
||||
writer.write(" $").write(name);
|
||||
}
|
||||
writer.endLinkTarget();
|
||||
writer.write(" (type ");
|
||||
writeType(type);
|
||||
writer.write("))").eol();
|
||||
++globalIndex;
|
||||
}
|
||||
}
|
||||
|
@ -24,11 +24,13 @@ public class WasmGlobal extends WasmEntity {
|
||||
private WasmExpression initialValue;
|
||||
private boolean immutable;
|
||||
private String exportName;
|
||||
private String importName;
|
||||
private String importModule;
|
||||
|
||||
public WasmGlobal(String name, WasmType type, WasmExpression initialValue) {
|
||||
this.name = name;
|
||||
this.type = Objects.requireNonNull(type);
|
||||
this.initialValue = Objects.requireNonNull(initialValue);
|
||||
this.initialValue = initialValue;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@ -48,7 +50,7 @@ public class WasmGlobal extends WasmEntity {
|
||||
}
|
||||
|
||||
public void setInitialValue(WasmExpression initialValue) {
|
||||
this.initialValue = Objects.requireNonNull(initialValue);
|
||||
this.initialValue = initialValue;
|
||||
}
|
||||
|
||||
public boolean isImmutable() {
|
||||
@ -66,4 +68,28 @@ public class WasmGlobal extends WasmEntity {
|
||||
public void setExportName(String exportName) {
|
||||
this.exportName = exportName;
|
||||
}
|
||||
|
||||
public String getImportName() {
|
||||
return importName;
|
||||
}
|
||||
|
||||
public void setImportName(String importName) {
|
||||
this.importName = importName;
|
||||
if (collection != null) {
|
||||
collection.invalidateIndexes();
|
||||
}
|
||||
}
|
||||
|
||||
public String getImportModule() {
|
||||
return importModule;
|
||||
}
|
||||
|
||||
public void setImportModule(String importModule) {
|
||||
this.importModule = importModule;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isImported() {
|
||||
return importName != null;
|
||||
}
|
||||
}
|
||||
|
@ -18,12 +18,17 @@ package org.teavm.backend.wasm.parser;
|
||||
public class GlobalSectionParser extends BaseSectionParser {
|
||||
private final GlobalSectionListener listener;
|
||||
private CodeParser codeParser;
|
||||
private int globalIndexOffset;
|
||||
|
||||
public GlobalSectionParser(GlobalSectionListener listener) {
|
||||
this.listener = listener;
|
||||
codeParser = new CodeParser();
|
||||
}
|
||||
|
||||
public void setGlobalIndexOffset(int globalIndexOffset) {
|
||||
this.globalIndexOffset = globalIndexOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseContent() {
|
||||
var count = readLEB();
|
||||
@ -31,7 +36,7 @@ public class GlobalSectionParser extends BaseSectionParser {
|
||||
reportAddress();
|
||||
var type = reader.readType();
|
||||
var mutable = reader.data[reader.ptr++] != 0;
|
||||
var codeListener = listener.startGlobal(i, type, mutable);
|
||||
var codeListener = listener.startGlobal(i + globalIndexOffset, type, mutable);
|
||||
if (codeListener == null) {
|
||||
codeListener = CodeListener.EMPTY;
|
||||
}
|
||||
|
@ -22,6 +22,9 @@ public interface ImportSectionListener {
|
||||
default void function(int typeIndex) {
|
||||
}
|
||||
|
||||
default void global(WasmHollowType type) {
|
||||
}
|
||||
|
||||
default void endEntry() {
|
||||
}
|
||||
}
|
||||
|
@ -37,11 +37,19 @@ public class ImportSectionParser extends BaseSectionParser {
|
||||
listener.startEntry(module, name);
|
||||
reportAddress();
|
||||
var type = reader.data[reader.ptr++];
|
||||
if (type == 0) {
|
||||
var typeIndex = readLEB();
|
||||
listener.function(typeIndex);
|
||||
} else {
|
||||
throw new ParseException("Unsupported import type", reader.ptr);
|
||||
switch (type) {
|
||||
case 0: {
|
||||
var typeIndex = readLEB();
|
||||
listener.function(typeIndex);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
var valueType = reader.readType();
|
||||
listener.global(valueType);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ParseException("Unsupported import type", reader.ptr);
|
||||
}
|
||||
listener.endEntry();
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import org.teavm.backend.wasm.generate.DwarfClassGenerator;
|
||||
import org.teavm.backend.wasm.generate.DwarfGenerator;
|
||||
import org.teavm.backend.wasm.model.WasmCustomSection;
|
||||
import org.teavm.backend.wasm.model.WasmFunction;
|
||||
import org.teavm.backend.wasm.model.WasmGlobal;
|
||||
import org.teavm.backend.wasm.model.WasmMemorySegment;
|
||||
import org.teavm.backend.wasm.model.WasmModule;
|
||||
import org.teavm.backend.wasm.model.WasmStructure;
|
||||
@ -138,20 +139,29 @@ public class WasmBinaryRenderer {
|
||||
}
|
||||
|
||||
private void renderImports(WasmModule module) {
|
||||
List<WasmFunction> functions = new ArrayList<>();
|
||||
var functions = new ArrayList<WasmFunction>();
|
||||
for (var function : module.functions) {
|
||||
if (function.getImportName() == null) {
|
||||
continue;
|
||||
}
|
||||
functions.add(function);
|
||||
}
|
||||
if (functions.isEmpty()) {
|
||||
|
||||
var globals = new ArrayList<WasmGlobal>();
|
||||
for (var global : module.globals) {
|
||||
if (global.getImportName() == null) {
|
||||
continue;
|
||||
}
|
||||
globals.add(global);
|
||||
}
|
||||
|
||||
if (functions.isEmpty() && globals.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
WasmBinaryWriter section = new WasmBinaryWriter();
|
||||
|
||||
section.writeLEB(functions.size());
|
||||
section.writeLEB(functions.size() + globals.size());
|
||||
for (WasmFunction function : functions) {
|
||||
int signatureIndex = module.types.indexOf(function.getType());
|
||||
String moduleName = function.getImportModule();
|
||||
@ -159,12 +169,22 @@ public class WasmBinaryRenderer {
|
||||
moduleName = "";
|
||||
}
|
||||
section.writeAsciiString(moduleName);
|
||||
|
||||
section.writeAsciiString(function.getImportName());
|
||||
|
||||
section.writeByte(EXTERNAL_KIND_FUNCTION);
|
||||
section.writeLEB(signatureIndex);
|
||||
}
|
||||
for (var global : globals) {
|
||||
var moduleName = global.getImportModule();
|
||||
if (moduleName == null) {
|
||||
moduleName = "";
|
||||
}
|
||||
section.writeAsciiString(moduleName);
|
||||
section.writeAsciiString(global.getImportName());
|
||||
section.writeByte(EXTERNAL_KIND_GLOBAL);
|
||||
section.writeType(global.getType(), module);
|
||||
section.writeByte(global.isImmutable() ? 0 : 1);
|
||||
}
|
||||
|
||||
writeSection(SECTION_IMPORT, "import", section.getData());
|
||||
}
|
||||
@ -211,16 +231,19 @@ public class WasmBinaryRenderer {
|
||||
}
|
||||
|
||||
private void renderGlobals(WasmModule module) {
|
||||
if (module.globals.isEmpty()) {
|
||||
var globals = module.globals.stream()
|
||||
.filter(global -> global.getImportName() == null)
|
||||
.collect(Collectors.toList());
|
||||
if (globals.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var section = new WasmBinaryWriter();
|
||||
var visitor = new WasmBinaryRenderingVisitor(section, module, null, null, 0);
|
||||
section.writeLEB(module.globals.size());
|
||||
for (var global : module.globals) {
|
||||
section.writeLEB(globals.size());
|
||||
for (var global : globals) {
|
||||
section.writeType(global.getType(), module);
|
||||
section.writeByte(global.isImmutable() ? 0 : 1); // mutable
|
||||
section.writeByte(global.isImmutable() ? 0 : 1);
|
||||
global.getInitialValue().acceptVisitor(visitor);
|
||||
section.writeByte(0x0b);
|
||||
}
|
||||
|
@ -15,4 +15,4 @@
|
||||
*/
|
||||
|
||||
include();
|
||||
export { load, defaults };
|
||||
export { load, defaults, wrapImport };
|
@ -52,12 +52,15 @@ function defaults(imports) {
|
||||
|
||||
let javaExceptionSymbol = Symbol("javaException");
|
||||
class JavaError extends Error {
|
||||
constructor(javaException) {
|
||||
#context
|
||||
|
||||
constructor(context, javaException) {
|
||||
super();
|
||||
this.#context = context;
|
||||
this[javaExceptionSymbol] = javaException;
|
||||
}
|
||||
get message() {
|
||||
let exceptionMessage = exports["teavm.exceptionMessage"];
|
||||
let exceptionMessage = this.#context.exports["teavm.exceptionMessage"];
|
||||
if (typeof exceptionMessage === "function") {
|
||||
let message = exceptionMessage(this[javaExceptionSymbol]);
|
||||
if (message != null) {
|
||||
@ -247,7 +250,7 @@ function jsoImports(imports, context) {
|
||||
return wrapper;
|
||||
}
|
||||
}
|
||||
let wrapper = new JavaError(javaException);
|
||||
let wrapper = new JavaError(context, javaException);
|
||||
javaExceptionWrappers.set(javaException, new WeakRef(wrapper));
|
||||
return wrapper;
|
||||
}
|
||||
@ -312,7 +315,6 @@ function jsoImports(imports, context) {
|
||||
unwrapBoolean: value => value ? 1 : 0,
|
||||
wrapBoolean: value => !!value,
|
||||
getProperty: getProperty,
|
||||
getPropertyPure: getProperty,
|
||||
setProperty: setProperty,
|
||||
setPropertyPure: setProperty,
|
||||
global(name) {
|
||||
@ -565,6 +567,7 @@ function jsoImports(imports, context) {
|
||||
imports.teavmJso["createFunction" + i] = new Function("wrapCallFromJavaToJs", ...argumentList, "body",
|
||||
`return new Function('wrapCallFromJavaToJs', ${argsAndBody}).bind(this, wrapCallFromJavaToJs);`
|
||||
).bind(null, wrapCallFromJavaToJs);
|
||||
imports.teavmJso["bindFunction" + i] = (f, ...args) => f.bind(null, ...args);
|
||||
imports.teavmJso["callFunction" + i] = new Function("rethrowJsAsJava", "fn", ...argumentList,
|
||||
`try {\n` +
|
||||
` return fn(${args});\n` +
|
||||
@ -596,24 +599,70 @@ function jsoImports(imports, context) {
|
||||
}
|
||||
}
|
||||
|
||||
function wrapImport(importObj) {
|
||||
return new Proxy(importObj, {
|
||||
get(target, prop) {
|
||||
let result = target[prop];
|
||||
return new WebAssembly.Global({ value: "externref", mutable: false }, result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function wrapImports(wasmModule, imports) {
|
||||
let promises = [];
|
||||
let propertiesToAdd = {};
|
||||
for (let { module, name, kind } of WebAssembly.Module.imports(wasmModule)) {
|
||||
if (kind !== "global" || module in imports) {
|
||||
continue;
|
||||
}
|
||||
let names = propertiesToAdd[module];
|
||||
if (names === void 0) {
|
||||
let namesByModule = [];
|
||||
names = namesByModule;
|
||||
propertiesToAdd[name] = names;
|
||||
promises.push((async () => {
|
||||
let moduleInstance = await import(module);
|
||||
let importsByModule = {};
|
||||
for (let name of namesByModule) {
|
||||
let importedName = name === "__self__" ? moduleInstance : moduleInstance[name];
|
||||
importsByModule[name] = new WebAssembly.Global(
|
||||
{ value: "externref", mutable: false },
|
||||
importedName
|
||||
);
|
||||
}
|
||||
imports[module] = importsByModule;
|
||||
})());
|
||||
}
|
||||
names.push(name);
|
||||
}
|
||||
if (promises.length === 0) {
|
||||
return;
|
||||
}
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
async function load(path, options) {
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
let deobfuscatorOptions = options.stackDeobfuscator || {};
|
||||
let debugInfoLocation = deobfuscatorOptions.infoLocation || "auto";
|
||||
let [deobfuscatorFactory, module, debugInfo] = await Promise.all([
|
||||
deobfuscatorOptions.enabled ? getDeobfuscator(path, deobfuscatorOptions) : Promise.resolve(null),
|
||||
WebAssembly.compileStreaming(fetch(path)),
|
||||
fetchExternalDebugInfo(path, debugInfoLocation, deobfuscatorOptions)
|
||||
]);
|
||||
|
||||
const importObj = {};
|
||||
const defaultsResult = defaults(importObj);
|
||||
if (typeof options.installImports !== "undefined") {
|
||||
options.installImports(importObj);
|
||||
}
|
||||
|
||||
let deobfuscatorOptions = options.stackDeobfuscator || {};
|
||||
let debugInfoLocation = deobfuscatorOptions.infoLocation || "auto";
|
||||
let [deobfuscatorFactory, { module, instance }, debugInfo] = await Promise.all([
|
||||
deobfuscatorOptions.enabled ? getDeobfuscator(path, deobfuscatorOptions) : Promise.resolve(null),
|
||||
WebAssembly.instantiateStreaming(fetch(path), importObj),
|
||||
fetchExternalDebugInfo(path, debugInfoLocation, deobfuscatorOptions)
|
||||
]);
|
||||
if (!options.noAutoImports) {
|
||||
await wrapImports(module, importObj);
|
||||
}
|
||||
let instance = new WebAssembly.Instance(module, importObj);
|
||||
|
||||
defaultsResult.supplyExports(instance.exports);
|
||||
if (deobfuscatorFactory) {
|
||||
|
@ -17,6 +17,6 @@
|
||||
var TeaVM = TeaVM || {};
|
||||
TeaVM.wasmGC = TeaVM.wasmGC || (() => {
|
||||
include();
|
||||
return { load, defaults };
|
||||
return { load, defaults, wrapImport };
|
||||
})();
|
||||
|
||||
|
@ -731,13 +731,11 @@ public final class JS {
|
||||
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@JSBody(params = { "instance", "index" }, script = "return instance[index];")
|
||||
@Import(name = "getProperty", module = "teavmJso")
|
||||
public static native JSObject get(JSObject instance, JSObject index);
|
||||
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@JSBody(params = { "instance", "index" }, script = "return instance[index];")
|
||||
@NoSideEffects
|
||||
@Import(name = "getPropertyPure", module = "teavmJso")
|
||||
public static native JSObject getPure(JSObject instance, JSObject index);
|
||||
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
|
@ -59,6 +59,11 @@ public class JSBodyAstEmitter implements JSBodyEmitter {
|
||||
return parameterNames.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsBodyImportInfo[] imports() {
|
||||
return imports.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emit(InjectorContext context) {
|
||||
var astWriter = new AstWriter(context.getWriter(), new DefaultGlobalNameWriter());
|
||||
|
@ -51,6 +51,11 @@ public class JSBodyBloatedEmitter implements JSBodyEmitter {
|
||||
return parameterNames.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsBodyImportInfo[] imports() {
|
||||
return imports.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emit(InjectorContext context) {
|
||||
emit(context.getWriter(), new EmissionStrategy() {
|
||||
|
@ -29,5 +29,7 @@ public interface JSBodyEmitter {
|
||||
|
||||
String[] parameterNames();
|
||||
|
||||
JsBodyImportInfo[] imports();
|
||||
|
||||
boolean isStatic();
|
||||
}
|
||||
|
@ -21,7 +21,9 @@ import org.teavm.backend.wasm.model.WasmType;
|
||||
|
||||
class WasmGCJSFunctions {
|
||||
private WasmFunction[] constructors = new WasmFunction[32];
|
||||
private WasmFunction[] binds = new WasmFunction[32];
|
||||
private WasmFunction[] callers = new WasmFunction[32];
|
||||
private WasmFunction getFunction;
|
||||
|
||||
WasmFunction getFunctionConstructor(WasmGCJsoContext context, int index) {
|
||||
var function = constructors[index];
|
||||
@ -40,6 +42,38 @@ class WasmGCJSFunctions {
|
||||
return function;
|
||||
}
|
||||
|
||||
WasmFunction getBind(WasmGCJsoContext context, int index) {
|
||||
var function = binds[index];
|
||||
if (function == null) {
|
||||
var extern = WasmType.SpecialReferenceKind.EXTERN.asNonNullType();
|
||||
var constructorParamTypes = new WasmType[index + 1];
|
||||
Arrays.fill(constructorParamTypes, WasmType.Reference.EXTERN);
|
||||
var functionType = context.functionTypes().of(extern, constructorParamTypes);
|
||||
function = new WasmFunction(functionType);
|
||||
function.setName(context.names().topLevel("teavm.js:bindFunction" + index));
|
||||
function.setImportModule("teavmJso");
|
||||
function.setImportName("bindFunction" + index);
|
||||
context.module().functions.add(function);
|
||||
binds[index] = function;
|
||||
}
|
||||
return function;
|
||||
}
|
||||
|
||||
WasmFunction getGet(WasmGCJsoContext context) {
|
||||
var function = getFunction;
|
||||
if (function == null) {
|
||||
var functionType = context.functionTypes().of(WasmType.Reference.EXTERN, WasmType.Reference.EXTERN,
|
||||
WasmType.Reference.EXTERN);
|
||||
function = new WasmFunction(functionType);
|
||||
function.setName(context.names().topLevel("teavm.js:getProperty"));
|
||||
function.setImportModule("teavmJso");
|
||||
function.setImportName("getProperty");
|
||||
context.module().functions.add(function);
|
||||
getFunction = function;
|
||||
}
|
||||
return function;
|
||||
}
|
||||
|
||||
WasmFunction getFunctionCaller(WasmGCJsoContext context, int index) {
|
||||
var function = callers[index];
|
||||
if (function == null) {
|
||||
|
@ -40,13 +40,16 @@ import org.teavm.model.ValueType;
|
||||
class WasmGCJSIntrinsic implements WasmGCIntrinsic {
|
||||
private WasmFunction globalFunction;
|
||||
private WasmGCJsoCommonGenerator commonGen;
|
||||
private WasmGCJSFunctions functions;
|
||||
|
||||
WasmGCJSIntrinsic(WasmGCJsoCommonGenerator commonGen) {
|
||||
WasmGCJSIntrinsic(WasmGCJsoCommonGenerator commonGen, WasmGCJSFunctions functions) {
|
||||
this.commonGen = commonGen;
|
||||
this.functions = functions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) {
|
||||
var jsoContext = WasmGCJsoContext.wrap(context);
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "wrap":
|
||||
return wrapString(invocation.getArguments().get(0), context);
|
||||
@ -64,6 +67,10 @@ class WasmGCJSIntrinsic implements WasmGCIntrinsic {
|
||||
return new WasmIsNull(context.generate(invocation.getArguments().get(0)));
|
||||
case "jsArrayItem":
|
||||
return arrayItem(invocation, context);
|
||||
case "get":
|
||||
case "getPure":
|
||||
return new WasmCall(functions.getGet(jsoContext), context.generate(invocation.getArguments().get(0)),
|
||||
context.generate(invocation.getArguments().get(1)));
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ public final class WasmGCJso {
|
||||
}
|
||||
});
|
||||
|
||||
var jsIntrinsic = new WasmGCJSIntrinsic(commonGen);
|
||||
var jsIntrinsic = new WasmGCJSIntrinsic(commonGen, jsFunctions);
|
||||
wasmGCHost.addIntrinsic(new MethodReference(JS.class, "wrap", String.class, JSObject.class), jsIntrinsic);
|
||||
wasmGCHost.addIntrinsic(new MethodReference(JS.class, "unwrapString", JSObject.class, String.class),
|
||||
jsIntrinsic);
|
||||
@ -53,6 +53,10 @@ public final class WasmGCJso {
|
||||
wasmGCHost.addIntrinsic(new MethodReference(JS.class, "isNull", JSObject.class, boolean.class), jsIntrinsic);
|
||||
wasmGCHost.addIntrinsic(new MethodReference(JS.class, "jsArrayItem", Object.class, int.class, Object.class),
|
||||
jsIntrinsic);
|
||||
wasmGCHost.addIntrinsic(new MethodReference(JS.class, "get", JSObject.class, JSObject.class, JSObject.class),
|
||||
jsIntrinsic);
|
||||
wasmGCHost.addIntrinsic(new MethodReference(JS.class, "getPure", JSObject.class, JSObject.class,
|
||||
JSObject.class), jsIntrinsic);
|
||||
|
||||
var wrapperIntrinsic = new WasmGCJSWrapperIntrinsic();
|
||||
wasmGCHost.addIntrinsic(new MethodReference(JSWrapper.class, "wrap", JSObject.class, Object.class),
|
||||
|
@ -20,6 +20,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import org.teavm.backend.javascript.rendering.AstWriter;
|
||||
import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider;
|
||||
@ -66,6 +67,7 @@ class WasmGCJsoCommonGenerator {
|
||||
private WasmFunction javaObjectToJSFunction;
|
||||
private WasmGlobal defaultWrapperClass;
|
||||
private Map<String, WasmGlobal> definedClasses = new HashMap<>();
|
||||
private Map<ImportDecl, WasmGlobal> importGlobals = new HashMap<>();
|
||||
|
||||
WasmGCJsoCommonGenerator(WasmGCJSFunctions jsFunctions) {
|
||||
this.jsFunctions = jsFunctions;
|
||||
@ -97,6 +99,9 @@ class WasmGCJsoCommonGenerator {
|
||||
if (!emitter.isStatic()) {
|
||||
paramCount++;
|
||||
}
|
||||
var imports = emitter.imports();
|
||||
paramCount += imports.length;
|
||||
|
||||
var global = new WasmGlobal(context.names().suggestForMethod(emitter.method()),
|
||||
WasmType.Reference.EXTERN, new WasmNullConstant(WasmType.Reference.EXTERN));
|
||||
context.module().globals.add(global);
|
||||
@ -124,6 +129,9 @@ class WasmGCJsoCommonGenerator {
|
||||
|
||||
var constructor = new WasmCall(jsFunctions.getFunctionConstructor(context, paramCount));
|
||||
var paramNames = new ArrayList<String>();
|
||||
for (var importDecl : imports) {
|
||||
paramNames.add(importDecl.alias);
|
||||
}
|
||||
if (!emitter.isStatic()) {
|
||||
paramNames.add("__this__");
|
||||
}
|
||||
@ -134,11 +142,35 @@ class WasmGCJsoCommonGenerator {
|
||||
}
|
||||
var functionBody = new WasmGetGlobal(context.strings().getStringConstant(body).global);
|
||||
constructor.getArguments().add(stringToJs(context, functionBody));
|
||||
initializerParts.add(initializer -> initializer.getBody().add(new WasmSetGlobal(global, constructor)));
|
||||
WasmExpression value = constructor;
|
||||
if (imports.length > 0) {
|
||||
var bind = new WasmCall(jsFunctions.getBind(context, imports.length));
|
||||
bind.getArguments().add(value);
|
||||
for (var importDecl : imports) {
|
||||
var importGlobal = getImportGlobal(context, importDecl.fromModule, "__self__");
|
||||
bind.getArguments().add(new WasmGetGlobal(importGlobal));
|
||||
}
|
||||
value = bind;
|
||||
}
|
||||
var result = value;
|
||||
initializerParts.add(initializer -> initializer.getBody().add(new WasmSetGlobal(global, result)));
|
||||
|
||||
return global;
|
||||
}
|
||||
|
||||
WasmGlobal getImportGlobal(WasmGCJsoContext context, String module, String id) {
|
||||
return importGlobals.computeIfAbsent(new ImportDecl(module, id), m -> {
|
||||
var name = context.names().topLevel(WasmGCNameProvider.sanitize("teavm.js@imports:" + module + "#" + id));
|
||||
var global = new WasmGlobal(name, WasmType.Reference.EXTERN,
|
||||
new WasmNullConstant(WasmType.Reference.EXTERN));
|
||||
global.setImmutable(true);
|
||||
context.module().globals.add(global);
|
||||
global.setImportModule(module);
|
||||
global.setImportName(id);
|
||||
return global;
|
||||
});
|
||||
}
|
||||
|
||||
private WasmFunction stringToJsFunction(WasmGCJsoContext context) {
|
||||
return context.functions().forStaticMethod(STRING_TO_JS);
|
||||
}
|
||||
@ -506,4 +538,31 @@ class WasmGCJsoCommonGenerator {
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
private static class ImportDecl {
|
||||
final String module;
|
||||
final String name;
|
||||
|
||||
ImportDecl(String module, String name) {
|
||||
this.module = module;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof ImportDecl)) {
|
||||
return false;
|
||||
}
|
||||
var that = (ImportDecl) o;
|
||||
return Objects.equals(module, that.module) && Objects.equals(name, that.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(module, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ import org.teavm.junit.TestPlatform;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@SkipJVM
|
||||
@OnlyPlatform(TestPlatform.JAVASCRIPT)
|
||||
@OnlyPlatform({TestPlatform.JAVASCRIPT, TestPlatform.WEBASSEMBLY_GC})
|
||||
@EachTestCompiledSeparately
|
||||
public class ImportModuleTest {
|
||||
@Test
|
||||
@ -39,12 +39,14 @@ public class ImportModuleTest {
|
||||
"org/teavm/jso/test/amd.js",
|
||||
"org/teavm/jso/test/amdModule.js"
|
||||
})
|
||||
@OnlyPlatform(TestPlatform.JAVASCRIPT)
|
||||
public void amd() {
|
||||
assertEquals(23, runTestFunction());
|
||||
}
|
||||
|
||||
@Test
|
||||
@AttachJavaScript("org/teavm/jso/test/commonjs.js")
|
||||
@OnlyPlatform(TestPlatform.JAVASCRIPT)
|
||||
public void commonjs() {
|
||||
assertEquals(23, runTestFunction());
|
||||
}
|
||||
@ -52,6 +54,7 @@ public class ImportModuleTest {
|
||||
@Test
|
||||
@JsModuleTest
|
||||
@ServeJS(from = "org/teavm/jso/test/es2015.js", as = "testModule.js")
|
||||
@OnlyPlatform({TestPlatform.JAVASCRIPT, TestPlatform.WEBASSEMBLY_GC})
|
||||
public void es2015() {
|
||||
assertEquals(23, runTestFunction());
|
||||
}
|
||||
@ -59,6 +62,7 @@ public class ImportModuleTest {
|
||||
@Test
|
||||
@JsModuleTest
|
||||
@ServeJS(from = "org/teavm/jso/test/classWithConstructorInModule.js", as = "testModule.js")
|
||||
@OnlyPlatform(TestPlatform.JAVASCRIPT)
|
||||
public void classConstructor() {
|
||||
var o = new ClassWithConstructorInModule();
|
||||
assertEquals(99, o.getFoo());
|
||||
@ -71,6 +75,7 @@ public class ImportModuleTest {
|
||||
@Test
|
||||
@JsModuleTest
|
||||
@ServeJS(from = "org/teavm/jso/test/classWithConstructorInModule.js", as = "testModule.js")
|
||||
@OnlyPlatform(TestPlatform.JAVASCRIPT)
|
||||
public void topLevel() {
|
||||
assertEquals("top level", ClassWithConstructorInModule.topLevelFunction());
|
||||
assertEquals("top level prop", ClassWithConstructorInModule.getTopLevelProperty());
|
||||
|
@ -55,7 +55,7 @@ final class TestUtil {
|
||||
if (properties.isEmpty()) {
|
||||
try (InputStream input = TeaVMTestRunner.class.getClassLoader().getResourceAsStream(resource);
|
||||
OutputStream output = new BufferedOutputStream(new FileOutputStream(file))) {
|
||||
IOUtils.copy(input, output);
|
||||
input.transferTo(output);
|
||||
}
|
||||
} else {
|
||||
String content;
|
||||
|
@ -188,7 +188,7 @@ class WebAssemblyGCPlatformSupport extends TestPlatformSupport<WasmGCTarget> {
|
||||
getExtension() + "-deobfuscator.wasm");
|
||||
try {
|
||||
TestUtil.resourceToFile("org/teavm/backend/wasm/wasm-gc-runtime.js", testPath, Map.of());
|
||||
TestUtil.resourceToFile("deobfuscator.wasm", testDeobfuscatorPath, Map.of());
|
||||
TestUtil.resourceToFile("org/teavm/backend/wasm/deobfuscator.wasm", testDeobfuscatorPath, Map.of());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user