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