diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmGlobal.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmGlobal.java index 9548f15d2c..ad47ee94c7 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/WasmGlobal.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmGlobal.java @@ -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 WasmExpression getInitialValue() { } public void setInitialValue(WasmExpression initialValue) { - this.initialValue = Objects.requireNonNull(initialValue); + this.initialValue = initialValue; } public boolean isImmutable() { @@ -66,4 +68,28 @@ public String getExportName() { 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; + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java index a91e99ee8a..7947cb5c2c 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java @@ -27,6 +27,7 @@ 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 @@ private void renderTypes(WasmModule module) { } private void renderImports(WasmModule module) { - List functions = new ArrayList<>(); + var functions = new ArrayList(); for (var function : module.functions) { if (function.getImportName() == null) { continue; } functions.add(function); } - if (functions.isEmpty()) { + + var globals = new ArrayList(); + 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 @@ private void renderImports(WasmModule module) { 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()); } diff --git a/core/src/main/js/wasm-gc-runtime/module-wrapper.js b/core/src/main/js/wasm-gc-runtime/module-wrapper.js index 4abbfc5c7d..cb0ddaa697 100644 --- a/core/src/main/js/wasm-gc-runtime/module-wrapper.js +++ b/core/src/main/js/wasm-gc-runtime/module-wrapper.js @@ -15,4 +15,4 @@ */ include(); -export { load, defaults }; \ No newline at end of file +export { load, defaults, wrapImport }; \ No newline at end of file diff --git a/core/src/main/js/wasm-gc-runtime/runtime.js b/core/src/main/js/wasm-gc-runtime/runtime.js index 689df177eb..d713a27788 100644 --- a/core/src/main/js/wasm-gc-runtime/runtime.js +++ b/core/src/main/js/wasm-gc-runtime/runtime.js @@ -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,25 +599,73 @@ 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 = { + __self__: moduleInstance + }; + for (let name of namesByModule) { + let importedName = 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 = {}; } - 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([ + let [deobfuscatorFactory, module, debugInfo] = await Promise.all([ deobfuscatorOptions.enabled ? getDeobfuscator(path, deobfuscatorOptions) : Promise.resolve(null), - WebAssembly.instantiateStreaming(fetch(path), importObj), + WebAssembly.compileStreaming(fetch(path)), fetchExternalDebugInfo(path, debugInfoLocation, deobfuscatorOptions) ]); + const importObj = {}; + const defaultsResult = defaults(importObj); + if (typeof options.installImports !== "undefined") { + options.installImports(importObj); + } + if (!options.noAutoImports) { + await wrapImports(module, importObj); + } + let instance = new WebAssembly.Instance(module, importObj); + defaultsResult.supplyExports(instance.exports); if (deobfuscatorFactory) { let moduleToPass = debugInfoLocation === "auto" || debugInfoLocation === "embedded" ? module : null; diff --git a/core/src/main/js/wasm-gc-runtime/simple-wrapper.js b/core/src/main/js/wasm-gc-runtime/simple-wrapper.js index c3845bf893..09f6c9f8f2 100644 --- a/core/src/main/js/wasm-gc-runtime/simple-wrapper.js +++ b/core/src/main/js/wasm-gc-runtime/simple-wrapper.js @@ -17,6 +17,6 @@ var TeaVM = TeaVM || {}; TeaVM.wasmGC = TeaVM.wasmGC || (() => { include(); - return { load, defaults }; + return { load, defaults, wrapImport }; })(); diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JS.java b/jso/impl/src/main/java/org/teavm/jso/impl/JS.java index d4e561381b..d39782d274 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JS.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JS.java @@ -731,13 +731,11 @@ public static native JSObject construct(JSObject cls, JSObject a, JSObject b, 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) diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSBodyAstEmitter.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSBodyAstEmitter.java index 5ca0a81efc..1147b955df 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSBodyAstEmitter.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSBodyAstEmitter.java @@ -59,6 +59,11 @@ public String[] parameterNames() { return parameterNames.clone(); } + @Override + public JsBodyImportInfo[] imports() { + return imports.clone(); + } + @Override public void emit(InjectorContext context) { var astWriter = new AstWriter(context.getWriter(), new DefaultGlobalNameWriter()); diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSBodyBloatedEmitter.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSBodyBloatedEmitter.java index c7e5ce14ba..626532fb83 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSBodyBloatedEmitter.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSBodyBloatedEmitter.java @@ -51,6 +51,11 @@ public String[] parameterNames() { return parameterNames.clone(); } + @Override + public JsBodyImportInfo[] imports() { + return imports.clone(); + } + @Override public void emit(InjectorContext context) { emit(context.getWriter(), new EmissionStrategy() { diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSBodyEmitter.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSBodyEmitter.java index 2abec80055..a6c1acab0f 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSBodyEmitter.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSBodyEmitter.java @@ -29,5 +29,7 @@ public interface JSBodyEmitter { String[] parameterNames(); + JsBodyImportInfo[] imports(); + boolean isStatic(); } diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJSFunctions.java b/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJSFunctions.java index e4a18b0f19..4f4732fb2b 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJSFunctions.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJSFunctions.java @@ -21,7 +21,9 @@ 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,37 @@ WasmFunction getFunctionConstructor(WasmGCJsoContext context, int index) { 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); + 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) { diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJSIntrinsic.java b/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJSIntrinsic.java index 8c91fa4335..bc5badff33 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJSIntrinsic.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJSIntrinsic.java @@ -40,13 +40,16 @@ 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 @@ public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext co 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(); } diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJso.java b/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJso.java index 60ee3b7090..2072e64a3d 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJso.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJso.java @@ -43,7 +43,7 @@ public static void install(TeaVMHost host, TeaVMWasmGCHost wasmGCHost, JSBodyRep } }); - 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 static void install(TeaVMHost host, TeaVMWasmGCHost wasmGCHost, JSBodyRep 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), diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJsoCommonGenerator.java b/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJsoCommonGenerator.java index 5460c6bb55..984715d406 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJsoCommonGenerator.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJsoCommonGenerator.java @@ -20,6 +20,7 @@ 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 definedClasses = new HashMap<>(); + private Map importGlobals = new HashMap<>(); WasmGCJsoCommonGenerator(WasmGCJSFunctions jsFunctions) { this.jsFunctions = jsFunctions; @@ -97,6 +99,9 @@ WasmGlobal addJSBody(WasmGCJsoContext context, JSBodyEmitter emitter, boolean in 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 @@ WasmGlobal addJSBody(WasmGCJsoContext context, JSBodyEmitter emitter, boolean in var constructor = new WasmCall(jsFunctions.getFunctionConstructor(context, paramCount)); var paramNames = new ArrayList(); + for (var importDecl : imports) { + paramNames.add(importDecl.alias); + } if (!emitter.isStatic()) { paramNames.add("__this__"); } @@ -134,11 +142,34 @@ WasmGlobal addJSBody(WasmGCJsoContext context, JSBodyEmitter emitter, boolean in } 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)); + 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 +537,31 @@ private String getClassAliasName(ClassReader cls) { } 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); + } + } } diff --git a/tests/src/test/java/org/teavm/jso/test/ImportModuleTest.java b/tests/src/test/java/org/teavm/jso/test/ImportModuleTest.java index 285f049114..fa4fbf98a3 100644 --- a/tests/src/test/java/org/teavm/jso/test/ImportModuleTest.java +++ b/tests/src/test/java/org/teavm/jso/test/ImportModuleTest.java @@ -31,7 +31,7 @@ @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 void commonjs() { @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 void es2015() { @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 void classConstructor() { @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()); diff --git a/tools/junit/src/main/java/org/teavm/junit/TestUtil.java b/tools/junit/src/main/java/org/teavm/junit/TestUtil.java index bba757612b..c8d757f4e2 100644 --- a/tools/junit/src/main/java/org/teavm/junit/TestUtil.java +++ b/tools/junit/src/main/java/org/teavm/junit/TestUtil.java @@ -55,7 +55,7 @@ static void resourceToFile(String resource, File file, Map prope 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; diff --git a/tools/junit/src/main/java/org/teavm/junit/WebAssemblyGCPlatformSupport.java b/tools/junit/src/main/java/org/teavm/junit/WebAssemblyGCPlatformSupport.java index 6901b07055..568e271338 100644 --- a/tools/junit/src/main/java/org/teavm/junit/WebAssemblyGCPlatformSupport.java +++ b/tools/junit/src/main/java/org/teavm/junit/WebAssemblyGCPlatformSupport.java @@ -188,7 +188,7 @@ void additionalSingleTestOutput(File outputPathForMethod, TeaVMTestConfiguration 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); }