From 2129f5d15062a9d105454a609c4a3c21852a40b0 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sun, 6 Oct 2024 21:00:17 +0200 Subject: [PATCH] wasm gc: support exporting declarations as JS entities from module --- .../org/teavm/classlib/java/util/TDate.java | 6 +- .../backend/wasm/WasmGCModuleGenerator.java | 36 --- .../org/teavm/backend/wasm/WasmGCTarget.java | 35 +-- .../gc/WasmGCDeclarationsGenerator.java | 4 +- .../gc/methods/WasmGCGenerationContext.java | 9 +- .../gc/methods/WasmGCGenerationVisitor.java | 5 + .../gc/methods/WasmGCMethodGenerator.java | 9 + .../gc/WasmGCCustomGeneratorContext.java | 2 + .../intrinsics/gc/WasmGCIntrinsicContext.java | 2 + .../gc/EntryPointTransformation.java | 50 ++++ .../org/teavm/backend/wasm/wasm-gc-runtime.js | 195 +++++++++++----- .../java/org/teavm/jso/impl/JSMethods.java | 1 - .../jso/impl/JSObjectClassTransformer.java | 2 +- .../impl/wasmgc/WasmGCJsoCommonGenerator.java | 216 ++++++++++++++++++ .../jso/impl/wasmgc/WasmGCJsoContext.java | 25 ++ .../wasmgc/WasmGCMarshallMethodGenerator.java | 101 +------- .../src/main/resources/test-server/frame.js | 12 +- 17 files changed, 479 insertions(+), 231 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/wasm/transformation/gc/EntryPointTransformation.java diff --git a/classlib/src/main/java/org/teavm/classlib/java/util/TDate.java b/classlib/src/main/java/org/teavm/classlib/java/util/TDate.java index f7459572c..16e591725 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/util/TDate.java +++ b/classlib/src/main/java/org/teavm/classlib/java/util/TDate.java @@ -25,7 +25,9 @@ import org.teavm.interop.Platforms; import org.teavm.interop.Unmanaged; import org.teavm.interop.UnsupportedOn; +import org.teavm.jso.JSObject; import org.teavm.jso.core.JSDate; +import org.teavm.jso.impl.JS; public class TDate implements TComparable { private long value; @@ -420,7 +422,7 @@ public String toString() { } else if (PlatformDetector.isWebAssembly()) { return toStringWebAssembly(value); } else if (PlatformDetector.isWebAssemblyGC()) { - return toStringWebAssemblyGC(value); + return JS.unwrapString(toStringWebAssemblyGC(value)); } else { return JSDate.create(value).stringValue(); } @@ -435,7 +437,7 @@ public String toString() { private static native String toStringWebAssembly(double date); @Import(module = "teavmDate", name = "dateToString") - private static native String toStringWebAssemblyGC(double date); + private static native JSObject toStringWebAssemblyGC(double date); @Deprecated public String toLocaleString() { diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmGCModuleGenerator.java b/core/src/main/java/org/teavm/backend/wasm/WasmGCModuleGenerator.java index a500fefec..7e739727e 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmGCModuleGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmGCModuleGenerator.java @@ -18,7 +18,6 @@ import org.teavm.backend.wasm.generate.gc.WasmGCDeclarationsGenerator; import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.runtime.StringInternPool; -import org.teavm.backend.wasm.runtime.gc.WasmGCSupport; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; @@ -40,41 +39,6 @@ public WasmFunction generateMainFunction(String entryPoint) { "main", ValueType.parse(String[].class), ValueType.VOID)); } - public WasmFunction generateCreateStringBuilderFunction() { - return declarationsGenerator.functions().forStaticMethod(new MethodReference( - WasmGCSupport.class, "createStringBuilder", StringBuilder.class)); - } - - public WasmFunction generateCreateStringArrayFunction() { - return declarationsGenerator.functions().forStaticMethod(new MethodReference( - WasmGCSupport.class, "createStringArray", int.class, String[].class)); - } - - public WasmFunction generateAppendCharFunction() { - return declarationsGenerator.functions().forInstanceMethod(new MethodReference( - StringBuilder.class, "append", char.class, StringBuilder.class)); - } - - public WasmFunction generateBuildStringFunction() { - return declarationsGenerator.functions().forInstanceMethod(new MethodReference( - StringBuilder.class, "toString", String.class)); - } - - public WasmFunction generateSetToStringArrayFunction() { - return declarationsGenerator.functions().forStaticMethod(new MethodReference( - WasmGCSupport.class, "setToStringArray", String[].class, int.class, String.class, void.class)); - } - - public WasmFunction generateStringLengthFunction() { - return declarationsGenerator.functions().forInstanceMethod(new MethodReference( - String.class, "length", int.class)); - } - - public WasmFunction generateCharAtFunction() { - return declarationsGenerator.functions().forInstanceMethod(new MethodReference( - String.class, "charAt", int.class, char.class)); - } - public WasmFunction generateReportGarbageCollectedStringFunction() { var entryType = ValueType.object(StringInternPool.class.getName() + "$Entry"); return declarationsGenerator.functions().forStaticMethod(new MethodReference( diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java index 47ed4c9ba..87d7c3610 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java @@ -43,6 +43,7 @@ import org.teavm.backend.wasm.runtime.StringInternPool; import org.teavm.backend.wasm.transformation.gc.BaseClassesTransformation; import org.teavm.backend.wasm.transformation.gc.ClassLoaderResourceTransformation; +import org.teavm.backend.wasm.transformation.gc.EntryPointTransformation; import org.teavm.dependency.DependencyAnalyzer; import org.teavm.dependency.DependencyListener; import org.teavm.interop.Platforms; @@ -73,6 +74,7 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost { private List customTypeMapperFactories = new ArrayList<>(); private Map customCustomGenerators = new HashMap<>(); private List customGeneratorFactories = new ArrayList<>(); + private EntryPointTransformation entryPointTransformation = new EntryPointTransformation(); public void setObfuscated(boolean obfuscated) { this.obfuscated = obfuscated; @@ -118,6 +120,8 @@ public void addCustomTypeMapperFactory(WasmGCCustomTypeMapperFactory customTypeM @Override public void setController(TeaVMTargetController controller) { this.controller = controller; + entryPointTransformation.setEntryPoint(controller.getEntryPoint()); + entryPointTransformation.setEntryPointName(controller.getEntryPointName()); } @Override @@ -134,7 +138,8 @@ public List getDependencyListeners() { public List getTransformers() { return List.of( new BaseClassesTransformation(), - new ClassLoaderResourceTransformation() + new ClassLoaderResourceTransformation(), + entryPointTransformation ); } @@ -198,7 +203,8 @@ public void emit(ListableClassHolderSource classes, BuildTarget buildTarget, Str intrinsics, customTypeMapperFactories, controller::isVirtual, - strict + strict, + controller.getEntryPoint() ); declarationsGenerator.setFriendlyToDebugger(controller.isFriendlyToDebugger()); var moduleGenerator = new WasmGCModuleGenerator(declarationsGenerator); @@ -207,38 +213,17 @@ public void emit(ListableClassHolderSource classes, BuildTarget buildTarget, Str mainFunction.setExportName(controller.getEntryPointName()); mainFunction.setName(controller.getEntryPointName()); - var stringBuilderFunction = moduleGenerator.generateCreateStringBuilderFunction(); - stringBuilderFunction.setExportName("createStringBuilder"); - - var createStringArrayFunction = moduleGenerator.generateCreateStringArrayFunction(); - createStringArrayFunction.setExportName("createStringArray"); - - var appendCharFunction = moduleGenerator.generateAppendCharFunction(); - appendCharFunction.setExportName("appendChar"); - - var buildStringFunction = moduleGenerator.generateBuildStringFunction(); - buildStringFunction.setExportName("buildString"); - - var setArrayFunction = moduleGenerator.generateSetToStringArrayFunction(); - setArrayFunction.setExportName("setToStringArray"); - - var stringLengthFunction = moduleGenerator.generateStringLengthFunction(); - stringLengthFunction.setExportName("stringLength"); - - var charAtFunction = moduleGenerator.generateCharAtFunction(); - charAtFunction.setExportName("charAt"); - var internMethod = controller.getDependencyInfo().getMethod(new MethodReference(String.class, "intern", String.class)); if (internMethod != null && internMethod.isUsed()) { var removeStringEntryFunction = moduleGenerator.generateReportGarbageCollectedStringFunction(); - removeStringEntryFunction.setExportName("reportGarbageCollectedString"); + removeStringEntryFunction.setExportName("teavm.reportGarbageCollectedString"); } var exceptionMessageRef = new MethodReference(Throwable.class, "getMessage", Throwable.class); if (controller.getDependencyInfo().getMethod(exceptionMessageRef) != null) { var exceptionMessageFunction = declarationsGenerator.functions().forInstanceMethod(exceptionMessageRef); - exceptionMessageFunction.setExportName("exceptionMessage"); + exceptionMessageFunction.setExportName("teavm.exceptionMessage"); } moduleGenerator.generate(); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCDeclarationsGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCDeclarationsGenerator.java index 1bf83fd49..9453bedb7 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCDeclarationsGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCDeclarationsGenerator.java @@ -62,7 +62,8 @@ public WasmGCDeclarationsGenerator( WasmGCIntrinsicProvider intrinsics, List customTypeMapperFactories, Predicate isVirtual, - boolean strict + boolean strict, + String entryPoint ) { this.module = module; hierarchy = new ClassHierarchy(classes); @@ -82,6 +83,7 @@ public WasmGCDeclarationsGenerator( customGenerators, intrinsics, strict, + entryPoint, initializerContributors::add ); var tags = new TagRegistry(classes, hierarchy); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationContext.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationContext.java index 94cc5e203..c5be50da3 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationContext.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationContext.java @@ -65,6 +65,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext { private Map> interfaceImplementors; private WasmGCNameProvider names; private boolean strict; + private String entryPoint; private Consumer initializerContributors; public WasmGCGenerationContext(WasmModule module, WasmGCVirtualTableProvider virtualTables, @@ -73,7 +74,8 @@ public WasmGCGenerationContext(WasmModule module, WasmGCVirtualTableProvider vir WasmGCSupertypeFunctionProvider supertypeFunctions, WasmGCClassInfoProvider classInfoProvider, WasmGCStandardClasses standardClasses, WasmGCStringProvider strings, WasmGCCustomGeneratorProvider customGenerators, WasmGCIntrinsicProvider intrinsics, - WasmGCNameProvider names, boolean strict, Consumer initializerContributors) { + WasmGCNameProvider names, boolean strict, String entryPoint, + Consumer initializerContributors) { this.module = module; this.virtualTables = virtualTables; this.typeMapper = typeMapper; @@ -90,6 +92,7 @@ public WasmGCGenerationContext(WasmModule module, WasmGCVirtualTableProvider vir this.intrinsics = intrinsics; this.names = names; this.strict = strict; + this.entryPoint = entryPoint; this.initializerContributors = initializerContributors; } @@ -109,6 +112,10 @@ public WasmGCStringProvider strings() { return strings; } + public String entryPoint() { + return entryPoint; + } + public WasmGCVirtualTableProvider virtualTables() { return virtualTables; } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java index d77006d35..3da315056 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java @@ -844,6 +844,11 @@ public WasmTag exceptionTag() { return context.getExceptionTag(); } + @Override + public String entryPoint() { + return context.entryPoint(); + } + @Override public void addToInitializer(Consumer initializerContributor) { context.addToInitializer(initializerContributor); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java index 4d9b4a399..b07df051b 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java @@ -88,6 +88,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository { private WasmGCStandardClasses standardClasses; private WasmGCStringProvider strings; private boolean strict; + private String entryPoint; private Consumer initializerContributors; public WasmGCMethodGenerator( @@ -103,6 +104,7 @@ public WasmGCMethodGenerator( WasmGCCustomGeneratorProvider customGenerators, WasmGCIntrinsicProvider intrinsics, boolean strict, + String entryPoint, Consumer initializerContributors ) { this.module = module; @@ -117,6 +119,7 @@ public WasmGCMethodGenerator( this.customGenerators = customGenerators; this.intrinsics = intrinsics; this.strict = strict; + this.entryPoint = entryPoint; this.initializerContributors = initializerContributors; } @@ -362,6 +365,7 @@ private WasmGCGenerationContext getGenerationContext() { intrinsics, names, strict, + entryPoint, initializerContributors ); } @@ -434,6 +438,11 @@ public WasmGCStringProvider strings() { return context.strings(); } + @Override + public String entryPoint() { + return context.entryPoint(); + } + @Override public void addToInitializer(Consumer initializerContributor) { context.addToInitializer(initializerContributor); diff --git a/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGeneratorContext.java b/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGeneratorContext.java index f6adb6cd9..f90353048 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGeneratorContext.java +++ b/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGeneratorContext.java @@ -51,5 +51,7 @@ public interface WasmGCCustomGeneratorContext { WasmGCStringProvider strings(); + String entryPoint(); + void addToInitializer(Consumer initializerContributor); } diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicContext.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicContext.java index c88ae2532..32e4f8b68 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicContext.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicContext.java @@ -61,5 +61,7 @@ public interface WasmGCIntrinsicContext { WasmTag exceptionTag(); + String entryPoint(); + void addToInitializer(Consumer initializerContributor); } diff --git a/core/src/main/java/org/teavm/backend/wasm/transformation/gc/EntryPointTransformation.java b/core/src/main/java/org/teavm/backend/wasm/transformation/gc/EntryPointTransformation.java new file mode 100644 index 000000000..14b676925 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/transformation/gc/EntryPointTransformation.java @@ -0,0 +1,50 @@ +/* + * Copyright 2024 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.backend.wasm.transformation.gc; + +import org.teavm.model.AnnotationHolder; +import org.teavm.model.AnnotationValue; +import org.teavm.model.ClassHolder; +import org.teavm.model.ClassHolderTransformer; +import org.teavm.model.ClassHolderTransformerContext; +import org.teavm.model.MethodDescriptor; + +public class EntryPointTransformation implements ClassHolderTransformer { + private static final MethodDescriptor MAIN_METHOD = new MethodDescriptor("main", String[].class, void.class); + private String entryPoint; + private String entryPointName; + + public void setEntryPoint(String entryPoint) { + this.entryPoint = entryPoint; + } + + public void setEntryPointName(String entryPointName) { + this.entryPointName = entryPointName; + } + + @Override + public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) { + if (cls.getName().equals(entryPoint)) { + var mainMethod = cls.getMethod(MAIN_METHOD); + if (mainMethod != null) { + mainMethod.getAnnotations().add(new AnnotationHolder("org.teavm.jso.JSExport")); + + var methodAnnot = new AnnotationHolder("org.teavm.jso.JSMethod"); + methodAnnot.getValues().put("value", new AnnotationValue(entryPointName)); + } + } + } +} diff --git a/core/src/main/resources/org/teavm/backend/wasm/wasm-gc-runtime.js b/core/src/main/resources/org/teavm/backend/wasm/wasm-gc-runtime.js index 86bf6de6d..24e916f58 100644 --- a/core/src/main/resources/org/teavm/backend/wasm/wasm-gc-runtime.js +++ b/core/src/main/resources/org/teavm/backend/wasm/wasm-gc-runtime.js @@ -18,6 +18,8 @@ var TeaVM = TeaVM || {}; TeaVM.wasm = function() { let exports; let globalsCache = new Map(); + let stackDeobfuscator = null; + let chromeExceptionRegex = / *at .+\.wasm:wasm-function\[[0-9]+]:0x([0-9a-f]+).*/; let getGlobalName = function(name) { let result = globalsCache.get(name); if (typeof result === "undefined") { @@ -45,11 +47,11 @@ TeaVM.wasm = function() { this[javaExceptionSymbol] = javaException; } get message() { - let exceptionMessage = exports.exceptionMessage; + let exceptionMessage = exports["teavm.exceptionMessage"]; if (typeof exceptionMessage === "function") { let message = exceptionMessage(this[javaExceptionSymbol]); if (message != null) { - return stringToJava(message); + return message; } } return "(could not fetch message)"; @@ -59,8 +61,8 @@ TeaVM.wasm = function() { function dateImports(imports) { imports.teavmDate = { currentTimeMillis: () => new Date().getTime(), - dateToString: timestamp => stringToJava(new Date(timestamp).toString()), - getYear: timestamp =>new Date(timestamp).getFullYear(), + dateToString: timestamp => new Date(timestamp).toString(), + getYear: timestamp => new Date(timestamp).getFullYear(), setYear(timestamp, year) { let date = new Date(timestamp); date.setFullYear(year); @@ -108,12 +110,16 @@ TeaVM.wasm = function() { function coreImports(imports) { let finalizationRegistry = new FinalizationRegistry(heldValue => { - if (typeof exports.reportGarbageCollectedValue === "function") { - exports.reportGarbageCollectedValue(heldValue) + let report = exports["teavm.reportGarbageCollectedValue"]; + if (typeof report === "function") { + report(heldValue) } }); let stringFinalizationRegistry = new FinalizationRegistry(heldValue => { - exports.reportGarbageCollectedString(heldValue); + let report = exports["teavm.reportGarbageCollectedString"]; + if (typeof report === "function") { + report(heldValue); + } }); imports.teavm = { createWeakRef(value, heldValue) { @@ -129,7 +135,28 @@ TeaVM.wasm = function() { stringFinalizationRegistry.register(value, heldValue) return weakRef; }, - stringDeref: weakRef => weakRef.deref() + stringDeref: weakRef => weakRef.deref(), + currentStackTrace() { + if (stackDeobfuscator) { + return; + } + let reportCallFrame = exports["teavm.reportCallFrame"]; + if (typeof reportCallFrame !== "function") { + return; + } + let stack = new Error().stack; + for (let line in stack.split("\n")) { + let match = chromeExceptionRegex.exec(line); + if (match !== null) { + let address = parseInt(match.groups[1], 16); + let frames = stackDeobfuscator(address); + for (let frame of frames) { + let line = frame.line; + reportCallFrame(file, method, cls, line); + } + } + } + } }; } @@ -137,6 +164,7 @@ TeaVM.wasm = function() { let javaObjectSymbol = Symbol("javaObject"); let functionsSymbol = Symbol("functions"); let functionOriginSymbol = Symbol("functionOrigin"); + let wrapperCallMarkerSymbol = new Symbol("wrapperCallMarker"); let jsWrappers = new WeakMap(); let javaWrappers = new WeakMap(); @@ -182,7 +210,7 @@ TeaVM.wasm = function() { } function javaExceptionToJs(e) { if (e instanceof WebAssembly.Exception) { - let tag = exports["javaException"]; + let tag = exports["teavm.javaException"]; if (e.is(tag)) { let javaException = e.getArg(tag, 0); let extracted = extractException(javaException); @@ -226,6 +254,22 @@ TeaVM.wasm = function() { rethrowJsAsJava(e); } } + function defineFunction(fn) { + let params = []; + for (let i = 1; i < fn.length; ++i) { + params.push("p" + i); + } + let paramsAsString = params.length === 0 ? "" : params.join(", "); + return new Function("rethrowJavaAsJs", "fn", ` + return function(${paramsAsString}) { + try { + return fn(${paramsAsString}); + } catch (e) { + rethrowJavaAsJs(e); + } + }; + `)(rethrowJavaAsJs, fn); + } imports.teavmJso = { emptyString: () => "", stringFromCharCode: code => String.fromCharCode(code), @@ -247,16 +291,67 @@ TeaVM.wasm = function() { rethrowJsAsJava(e); } }, - createClass(name) { - let fn = new Function( - "javaObjectSymbol", - "functionsSymbol", - `return function JavaClass_${sanitizeName(name)}(javaObject) { - this[javaObjectSymbol] = javaObject; - this[functionsSymbol] = null; - };` - ); - return fn(javaObjectSymbol, functionsSymbol, functionOriginSymbol); + createClass(name, parent, constructor) { + name = sanitizeName(name); + if (parent === null) { + let fn = new Function( + "javaObjectSymbol", + "functionsSymbol", + "wrapperCallMarker", + "constructor", + "rethrowJavaAsJs", + `let fn; + fn = function ${name}(marker, javaObject) { + if (marker === wrapperCallMarker) { + this[javaObjectSymbol] = javaObject; + this[functionsSymbol] = null; + } else if (constructor === null) { + throw new Error("This class can't be instantiated directly"); + } else { + try { + return fn(wrapperCallMarker, constructor(arguments)); + } catch (e) { + rethrowJavaAsJs(e); + } + } + }; + let boundFn = fn.bind(wrapperCallMarker); + boundFn[wrapperCallMarker] = fn; + return boundFn;` + ); + return fn(javaObjectSymbol, functionsSymbol, functionOriginSymbol, wrapperCallMarkerSymbol, + constructor, rethrowJavaAsJs); + } else { + let fn = new Function( + "parent", + "wrapperCallMarker", + "constructor", + "rethrowJavaAsJs", + `let fn + fn = function ${name}(marker, javaObject) { + if (marker === wrapperCallMarker) { + parent.call(this, javaObject); + } else if (constructor === null) { + throw new Error("This class can't be instantiated directly"); + } else { + try { + return fn(wrapperCallMarker, constructor(arguments)); + } catch (e) { + rethrowJavaAsJs(e); + } + } + }; + fn.prototype = Object.create(parent); + fn.prototype.constructor = parent; + let boundFn = fn.bind(wrapperCallMarker); + boundFn[wrapperCallMarker] = fn; + return fn;` + ); + return fn(parent, wrapperCallMarkerSymbol, constructor, rethrowJavaAsJs); + } + }, + exportClass(cls) { + return cls[wrapperCallMarkerSymbol]; }, defineMethod(cls, name, fn) { let params = []; @@ -274,6 +369,10 @@ TeaVM.wasm = function() { }; `)(rethrowJavaAsJs, fn); }, + defineStaticMethod(cls, name, fn) { + cls[name] = defineFunction(fn); + }, + defineFunction: defineFunction, defineProperty(cls, name, getFn, setFn) { let descriptor = { get() { @@ -295,6 +394,27 @@ TeaVM.wasm = function() { } Object.defineProperty(cls.prototype, name, descriptor); }, + defineStaticProperty(cls, name, getFn, setFn) { + let descriptor = { + get() { + try { + return getFn(); + } catch (e) { + rethrowJavaAsJs(e); + } + } + }; + if (setFn !== null) { + descriptor.set = function(value) { + try { + setFn(value); + } catch (e) { + rethrowJavaAsJs(e); + } + } + } + Object.defineProperty(cls, name, descriptor); + }, javaObjectToJS(instance, cls) { let existing = jsWrappers.get(instance); if (typeof existing != "undefined") { @@ -474,41 +594,8 @@ TeaVM.wasm = function() { options.installImports(importObj); } - return WebAssembly.instantiateStreaming(fetch(path), importObj).then((obj => { - let teavm = {}; - teavm.main = createMain(obj.instance); - teavm.instance = obj.instance; - return teavm; - })); - } - - function stringToJava(str) { - let sb = exports.createStringBuilder(); - for (let i = 0; i < str.length; ++i) { - exports.appendChar(sb, str.charCodeAt(i)); - } - return exports.buildString(sb); - } - - function createMain(instance) { - return args => { - if (typeof args === "undefined") { - args = []; - } - return new Promise((resolve, reject) => { - exports = instance.exports; - let javaArgs = exports.createStringArray(args.length); - for (let i = 0; i < args.length; ++i) { - exports.setToStringArray(javaArgs, i, stringToJava(args[i])); - } - try { - exports.main(javaArgs); - } catch (e) { - reject(e); - } - }); - } + return WebAssembly.instantiateStreaming(fetch(path), importObj); } - return { load }; + return { load, defaults }; }(); diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSMethods.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSMethods.java index 88da51cd1..89bf1b4e5 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSMethods.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSMethods.java @@ -161,7 +161,6 @@ public final class JSMethods { public static final String JS_MARSHALLABLE = JSMarshallable.class.getName(); public static final MethodDescriptor MARSHALL_TO_JS = new MethodDescriptor("marshallToJs", JS_OBJECT); - public static final MethodReference MARSHALL_TO_JS_REF = new MethodReference(JS_MARSHALLABLE, MARSHALL_TO_JS); static { for (int i = 0; i < INVOKE_METHODS.length; ++i) { diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSObjectClassTransformer.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSObjectClassTransformer.java index 162adf1b5..e23c0aa80 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSObjectClassTransformer.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSObjectClassTransformer.java @@ -132,7 +132,7 @@ public void transformClass(ClassHolder cls, ClassHolderTransformerContext contex createWrapperMethod.getModifiers().add(ElementModifier.NATIVE); cls.addMethod(createWrapperMethod); - if (isJavaScriptImplementation(cls)) { + if (!isJavaScriptClass(cls)) { cls.getInterfaces().add(JSMethods.JS_MARSHALLABLE); } } 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 421ecf8c9..fe0381581 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 @@ -32,15 +32,20 @@ import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmExternConversion; import org.teavm.backend.wasm.model.expression.WasmExternConversionType; +import org.teavm.backend.wasm.model.expression.WasmFunctionReference; import org.teavm.backend.wasm.model.expression.WasmGetGlobal; import org.teavm.backend.wasm.model.expression.WasmGetLocal; import org.teavm.backend.wasm.model.expression.WasmNullConstant; import org.teavm.backend.wasm.model.expression.WasmSetGlobal; import org.teavm.backend.wasm.model.expression.WasmThrow; +import org.teavm.jso.JSClass; import org.teavm.jso.JSObject; +import org.teavm.jso.impl.AliasCollector; import org.teavm.jso.impl.JSBodyAstEmitter; import org.teavm.jso.impl.JSBodyBloatedEmitter; import org.teavm.jso.impl.JSBodyEmitter; +import org.teavm.jso.impl.JSMarshallable; +import org.teavm.model.ClassReader; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; @@ -51,6 +56,13 @@ class WasmGCJsoCommonGenerator { private boolean rethrowExported; private Map stringsConstants = new HashMap<>(); + private WasmFunction createClassFunction; + private WasmFunction defineMethodFunction; + private WasmFunction defineStaticMethodFunction; + private WasmFunction definePropertyFunction; + private WasmFunction defineStaticPropertyFunction; + private Map definedClasses = new HashMap<>(); + WasmGCJsoCommonGenerator(WasmGCJSFunctions jsFunctions) { this.jsFunctions = jsFunctions; } @@ -183,4 +195,208 @@ WasmExpression jsStringConstant(WasmGCJsoContext context, String str) { }); return new WasmGetGlobal(global); } + + WasmGlobal getDefinedClass(WasmGCJsoContext context, String className) { + return definedClasses.computeIfAbsent(className, n -> defineClass(context, n)); + } + + private WasmGlobal defineClass(WasmGCJsoContext context, String className) { + var name = context.names().topLevel(context.names().suggestForClass(className)); + var global = new WasmGlobal(name, WasmType.Reference.EXTERN, new WasmNullConstant(WasmType.Reference.EXTERN)); + context.module().globals.add(global); + + var cls = context.classes().get(className); + var expressions = new ArrayList(); + var isModule = context.entryPoint().equals(className); + + var members = AliasCollector.collectMembers(cls, AliasCollector::isInstanceMember); + for (var aliasEntry : members.methods.entrySet()) { + if (!aliasEntry.getValue().getClassName().equals(cls.getName())) { + continue; + } + var fn = context.functions().forStaticMethod(aliasEntry.getValue()); + fn.setReferenced(true); + var methodName = context.strings().getStringConstant(aliasEntry.getKey()); + var jsMethodName = stringToJs(context, new WasmGetGlobal(methodName.global)); + var defineMethod = new WasmCall(defineMethodFunction(context), new WasmGetGlobal(global), + jsMethodName, new WasmFunctionReference(fn)); + expressions.add(defineMethod); + } + + for (var aliasEntry : members.properties.entrySet()) { + var property = aliasEntry.getValue(); + if (!property.getter.getClassName().equals(cls.getName())) { + continue; + } + var getter = context.functions().forStaticMethod(property.getter); + getter.setReferenced(true); + WasmFunction setter = null; + if (property.setter != null) { + setter = context.functions().forStaticMethod(property.setter); + setter.setReferenced(true); + } + var setterRef = setter != null + ? new WasmFunctionReference(setter) + : new WasmNullConstant(WasmType.Reference.FUNC); + var methodName = context.strings().getStringConstant(aliasEntry.getKey()); + var jsMethodName = stringToJs(context, new WasmGetGlobal(methodName.global)); + var defineProperty = new WasmCall(definePropertyFunction(context), new WasmGetGlobal(global), + jsMethodName, new WasmFunctionReference(getter), setterRef); + expressions.add(defineProperty); + } + + var staticMembers = AliasCollector.collectMembers(cls, AliasCollector::isStaticMember); + for (var aliasEntry : staticMembers.methods.entrySet()) { + if (!aliasEntry.getValue().getClassName().equals(cls.getName())) { + continue; + } + var fn = context.functions().forStaticMethod(aliasEntry.getValue()); + fn.setReferenced(true); + if (isModule) { + fn.setExportName(aliasEntry.getKey()); + } + var methodName = context.strings().getStringConstant(aliasEntry.getKey()); + var jsMethodName = stringToJs(context, new WasmGetGlobal(methodName.global)); + var defineMethod = new WasmCall(defineStaticMethodFunction(context), new WasmGetGlobal(global), + jsMethodName, new WasmFunctionReference(fn)); + expressions.add(defineMethod); + } + + for (var aliasEntry : staticMembers.properties.entrySet()) { + var property = aliasEntry.getValue(); + if (!property.getter.getClassName().equals(cls.getName())) { + continue; + } + var getter = context.functions().forStaticMethod(property.getter); + getter.setReferenced(true); + WasmFunction setter = null; + if (property.setter != null) { + setter = context.functions().forStaticMethod(property.setter); + setter.setReferenced(true); + } + var setterRef = setter != null + ? new WasmFunctionReference(setter) + : new WasmNullConstant(WasmType.Reference.FUNC); + var methodName = context.strings().getStringConstant(aliasEntry.getKey()); + var jsMethodName = stringToJs(context, new WasmGetGlobal(methodName.global)); + var defineProperty = new WasmCall(defineStaticPropertyFunction(context), new WasmGetGlobal(global), + jsMethodName, new WasmFunctionReference(getter), setterRef); + expressions.add(defineProperty); + } + + var javaClassName = context.strings().getStringConstant(className); + var jsClassName = stringToJs(context, new WasmGetGlobal(javaClassName.global)); + + var exportedParent = parentExportedClass(context, cls.getParent()); + var jsExportedParent = exportedParent != null + ? new WasmGetGlobal(getDefinedClass(context, exportedParent)) + : new WasmNullConstant(WasmType.Reference.EXTERN); + + WasmExpression constructor; + if (members.constructor != null) { + var function = context.functions().forStaticMethod(members.constructor); + constructor = new WasmFunctionReference(function); + } else { + constructor = new WasmNullConstant(WasmType.Reference.FUNC); + } + var createClass = new WasmCall(createClassFunction(context), jsClassName, jsExportedParent, constructor); + expressions.add(0, new WasmSetGlobal(global, createClass)); + + context.addToInitializer(f -> f.getBody().addAll(expressions)); + return global; + } + + private String parentExportedClass(WasmGCJsoContext context, String className) { + while (className != null) { + var cls = context.classes().get(className); + if (cls == null) { + return null; + } + if (cls.getInterfaces().contains(JSMarshallable.class.getName())) { + return className; + } + className = cls.getParent(); + } + return null; + } + + private WasmFunction createClassFunction(WasmGCJsoContext context) { + if (createClassFunction == null) { + createClassFunction = new WasmFunction(context.functionTypes().of(WasmType.Reference.EXTERN, + WasmType.Reference.EXTERN, WasmType.Reference.EXTERN, WasmType.Reference.FUNC)); + createClassFunction.setName(context.names().suggestForClass("teavm.jso@createClass")); + createClassFunction.setImportName("createClass"); + createClassFunction.setImportModule("teavmJso"); + context.module().functions.add(createClassFunction); + } + return createClassFunction; + } + + private WasmFunction defineMethodFunction(WasmGCJsoContext context) { + if (defineMethodFunction == null) { + defineMethodFunction = new WasmFunction(context.functionTypes().of(null, + WasmType.Reference.EXTERN, WasmType.Reference.EXTERN, WasmType.Reference.FUNC)); + defineMethodFunction.setName(context.names().suggestForClass("teavm.jso@defineMethod")); + defineMethodFunction.setImportName("defineMethod"); + defineMethodFunction.setImportModule("teavmJso"); + context.module().functions.add(defineMethodFunction); + } + return defineMethodFunction; + } + + private WasmFunction defineStaticMethodFunction(WasmGCJsoContext context) { + if (defineStaticMethodFunction == null) { + defineStaticMethodFunction = new WasmFunction(context.functionTypes().of(null, + WasmType.Reference.EXTERN, WasmType.Reference.EXTERN, WasmType.Reference.FUNC)); + defineStaticMethodFunction.setName(context.names().suggestForClass("teavm.jso@defineStaticMethod")); + defineStaticMethodFunction.setImportName("defineStaticMethod"); + defineStaticMethodFunction.setImportModule("teavmJso"); + context.module().functions.add(defineStaticMethodFunction); + } + return defineStaticMethodFunction; + } + + private WasmFunction definePropertyFunction(WasmGCJsoContext context) { + if (definePropertyFunction == null) { + definePropertyFunction = new WasmFunction(context.functionTypes().of(null, + WasmType.Reference.EXTERN, WasmType.Reference.EXTERN, WasmType.Reference.FUNC, + WasmType.Reference.FUNC)); + definePropertyFunction.setName(context.names().suggestForClass("teavm.jso@defineProperty")); + definePropertyFunction.setImportName("defineProperty"); + definePropertyFunction.setImportModule("teavmJso"); + context.module().functions.add(definePropertyFunction); + } + return definePropertyFunction; + } + + private WasmFunction defineStaticPropertyFunction(WasmGCJsoContext context) { + if (defineStaticPropertyFunction == null) { + defineStaticPropertyFunction = new WasmFunction(context.functionTypes().of(null, + WasmType.Reference.EXTERN, WasmType.Reference.EXTERN, WasmType.Reference.FUNC, + WasmType.Reference.FUNC)); + defineStaticPropertyFunction.setName(context.names().suggestForClass("teavm.jso@defineProperty")); + defineStaticPropertyFunction.setImportName("defineProperty"); + defineStaticPropertyFunction.setImportModule("teavmJso"); + context.module().functions.add(defineStaticPropertyFunction); + } + return defineStaticPropertyFunction; + } + + private String getClassAliasName(ClassReader cls) { + var name = cls.getSimpleName(); + if (name == null) { + name = cls.getName().substring(cls.getName().lastIndexOf('.') + 1); + } + var jsExport = cls.getAnnotations().get(JSClass.class.getName()); + if (jsExport != null) { + var nameValue = jsExport.getValue("name"); + if (nameValue != null) { + var nameValueString = nameValue.getString(); + if (!nameValueString.isEmpty()) { + name = nameValueString; + } + } + } + return name; + } } diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJsoContext.java b/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJsoContext.java index a29e014bf..a1e856a68 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJsoContext.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJsoContext.java @@ -26,8 +26,11 @@ import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmTag; +import org.teavm.model.ClassReaderSource; interface WasmGCJsoContext { + ClassReaderSource classes(); + WasmModule module(); WasmFunctionTypes functionTypes(); @@ -42,10 +45,17 @@ interface WasmGCJsoContext { WasmTag exceptionTag(); + String entryPoint(); + void addToInitializer(Consumer initializerContributor); static WasmGCJsoContext wrap(WasmGCIntrinsicContext context) { return new WasmGCJsoContext() { + @Override + public ClassReaderSource classes() { + return context.hierarchy().getClassSource(); + } + @Override public WasmModule module() { return context.module(); @@ -81,6 +91,11 @@ public WasmTag exceptionTag() { return context.exceptionTag(); } + @Override + public String entryPoint() { + return context.entryPoint(); + } + @Override public void addToInitializer(Consumer initializerContributor) { context.addToInitializer(initializerContributor); @@ -90,6 +105,11 @@ public void addToInitializer(Consumer initializerContributor) { static WasmGCJsoContext wrap(WasmGCCustomGeneratorContext context) { return new WasmGCJsoContext() { + @Override + public ClassReaderSource classes() { + return context.classes(); + } + @Override public WasmModule module() { return context.module(); @@ -125,6 +145,11 @@ public WasmTag exceptionTag() { return context.exceptionTag(); } + @Override + public String entryPoint() { + return context.entryPoint(); + } + @Override public void addToInitializer(Consumer initializerContributor) { context.addToInitializer(initializerContributor); diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCMarshallMethodGenerator.java b/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCMarshallMethodGenerator.java index bc9dc8d2c..9f475b63e 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCMarshallMethodGenerator.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCMarshallMethodGenerator.java @@ -15,31 +15,20 @@ */ package org.teavm.jso.impl.wasmgc; -import java.util.ArrayList; import org.teavm.backend.wasm.generators.gc.WasmGCCustomGenerator; import org.teavm.backend.wasm.generators.gc.WasmGCCustomGeneratorContext; import org.teavm.backend.wasm.model.WasmFunction; -import org.teavm.backend.wasm.model.WasmGlobal; import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.expression.WasmCall; -import org.teavm.backend.wasm.model.expression.WasmExpression; -import org.teavm.backend.wasm.model.expression.WasmFunctionReference; import org.teavm.backend.wasm.model.expression.WasmGetGlobal; import org.teavm.backend.wasm.model.expression.WasmGetLocal; -import org.teavm.backend.wasm.model.expression.WasmNullConstant; -import org.teavm.backend.wasm.model.expression.WasmSetGlobal; -import org.teavm.jso.impl.AliasCollector; -import org.teavm.model.ClassReader; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; class WasmGCMarshallMethodGenerator implements WasmGCCustomGenerator { private WasmGCJsoCommonGenerator commonGen; private WasmFunction javaObjectToJSFunction; - private WasmFunction createClassFunction; - private WasmFunction defineMethodFunction; - private WasmFunction definePropertyFunction; WasmGCMarshallMethodGenerator(WasmGCJsoCommonGenerator commonGen) { this.commonGen = commonGen; @@ -52,8 +41,7 @@ public void apply(MethodReference method, WasmFunction function, WasmGCCustomGen var thisLocal = new WasmLocal(context.typeMapper().mapType(ValueType.object(method.getClassName())), "this"); function.add(thisLocal); - var cls = context.classes().get(method.getClassName()); - var jsClassGlobal = defineClass(jsoContext, cls); + var jsClassGlobal = commonGen.getDefinedClass(jsoContext, method.getClassName()); var wrapperFunction = javaObjectToJSFunction(context); function.getBody().add(new WasmCall(wrapperFunction, new WasmGetLocal(thisLocal), new WasmGetGlobal(jsClassGlobal))); @@ -71,91 +59,4 @@ private WasmFunction javaObjectToJSFunction(WasmGCCustomGeneratorContext context return javaObjectToJSFunction; } - WasmGlobal defineClass(WasmGCJsoContext context, ClassReader cls) { - var name = context.names().topLevel(context.names().suggestForClass(cls.getName())); - var global = new WasmGlobal(name, WasmType.Reference.EXTERN, new WasmNullConstant(WasmType.Reference.EXTERN)); - context.module().globals.add(global); - - var expressions = new ArrayList(); - var className = context.strings().getStringConstant(cls.getName()); - var jsClassName = commonGen.stringToJs(context, new WasmGetGlobal(className.global)); - var createClass = new WasmCall(createClassFunction(context), jsClassName); - expressions.add(new WasmSetGlobal(global, createClass)); - - var members = AliasCollector.collectMembers(cls, AliasCollector::isInstanceMember); - for (var aliasEntry : members.methods.entrySet()) { - if (!aliasEntry.getValue().getClassName().equals(cls.getName())) { - continue; - } - var fn = context.functions().forStaticMethod(aliasEntry.getValue()); - fn.setReferenced(true); - var methodName = context.strings().getStringConstant(aliasEntry.getKey()); - var jsMethodName = commonGen.stringToJs(context, new WasmGetGlobal(methodName.global)); - var defineMethod = new WasmCall(defineMethodFunction(context), new WasmGetGlobal(global), - jsMethodName, new WasmFunctionReference(fn)); - expressions.add(defineMethod); - } - - for (var aliasEntry : members.properties.entrySet()) { - var property = aliasEntry.getValue(); - if (!property.getter.getClassName().equals(cls.getName())) { - continue; - } - var getter = context.functions().forStaticMethod(property.getter); - getter.setReferenced(true); - WasmFunction setter = null; - if (property.setter != null) { - setter = context.functions().forStaticMethod(property.setter); - setter.setReferenced(true); - } - var setterRef = setter != null - ? new WasmFunctionReference(setter) - : new WasmNullConstant(WasmType.Reference.FUNC); - var methodName = context.strings().getStringConstant(aliasEntry.getKey()); - var jsMethodName = commonGen.stringToJs(context, new WasmGetGlobal(methodName.global)); - var defineProperty = new WasmCall(definePropertyFunction(context), new WasmGetGlobal(global), - jsMethodName, new WasmFunctionReference(getter), setterRef); - expressions.add(defineProperty); - } - - context.addToInitializer(f -> f.getBody().addAll(expressions)); - return global; - } - - private WasmFunction createClassFunction(WasmGCJsoContext context) { - if (createClassFunction == null) { - createClassFunction = new WasmFunction(context.functionTypes().of(WasmType.Reference.EXTERN, - WasmType.Reference.EXTERN)); - createClassFunction.setName(context.names().suggestForClass("teavm.jso@createClass")); - createClassFunction.setImportName("createClass"); - createClassFunction.setImportModule("teavmJso"); - context.module().functions.add(createClassFunction); - } - return createClassFunction; - } - - private WasmFunction defineMethodFunction(WasmGCJsoContext context) { - if (defineMethodFunction == null) { - defineMethodFunction = new WasmFunction(context.functionTypes().of(null, - WasmType.Reference.EXTERN, WasmType.Reference.EXTERN, WasmType.Reference.FUNC)); - defineMethodFunction.setName(context.names().suggestForClass("teavm.jso@defineMethod")); - defineMethodFunction.setImportName("defineMethod"); - defineMethodFunction.setImportModule("teavmJso"); - context.module().functions.add(defineMethodFunction); - } - return defineMethodFunction; - } - - private WasmFunction definePropertyFunction(WasmGCJsoContext context) { - if (definePropertyFunction == null) { - definePropertyFunction = new WasmFunction(context.functionTypes().of(null, - WasmType.Reference.EXTERN, WasmType.Reference.EXTERN, WasmType.Reference.FUNC, - WasmType.Reference.FUNC)); - definePropertyFunction.setName(context.names().suggestForClass("teavm.jso@defineProperty")); - definePropertyFunction.setImportName("defineProperty"); - definePropertyFunction.setImportModule("teavmJso"); - context.module().functions.add(definePropertyFunction); - } - return definePropertyFunction; - } } diff --git a/tools/browser-runner/src/main/resources/test-server/frame.js b/tools/browser-runner/src/main/resources/test-server/frame.js index 8f934171d..dae8aaf84 100644 --- a/tools/browser-runner/src/main/resources/test-server/frame.js +++ b/tools/browser-runner/src/main/resources/test-server/frame.js @@ -210,8 +210,6 @@ function launchWasmGCTest(file, argument, callback) { } } - let instance = null; - TeaVM.wasm.load(file.path, { installImports: function(o) { o.teavmConsole.putcharStdout = putchar; @@ -220,21 +218,15 @@ function launchWasmGCTest(file, argument, callback) { success() { callback(wrapResponse({ status: "OK" })); }, - failure(javaString) { - let jsString = ""; - let length = instance.exports.stringLength(javaString); - for (let i = 0; i < length; ++i) { - jsString += String.fromCharCode(instance.exports.charAt(javaString, i)); - } + failure(message) { callback(wrapResponse({ status: "failed", - errorMessage: jsString + errorMessage: message })); } }; } }).then(teavm => { - instance = teavm.instance; return teavm.main(argument ? [argument] : []); }).catch(err => { callback(wrapResponse({