From a81eeeee036aa32d08b37cf3ca8b8bc4442d1a17 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 10 May 2018 16:40:48 +0300 Subject: [PATCH] Wasm backend: fix hanging tests, make more tests pass, change name generation scheme --- .../src/main/java/org/teavm/ast/Mangling.java | 149 ------------ .../backend/c/generate/NameProvider.java | 197 +--------------- .../generate/LowLevelNameProvider.java | 219 ++++++++++++++++++ .../org/teavm/backend/wasm/WasmTarget.java | 40 ++-- .../backend/wasm/generate/NameProvider.java | 25 ++ .../wasm/generate/WasmClassGenerator.java | 15 +- .../wasm/generate/WasmGenerationContext.java | 5 +- .../wasm/generate/WasmGenerationVisitor.java | 30 ++- .../backend/wasm/generate/WasmGenerator.java | 9 +- .../wasm/intrinsics/AddressIntrinsic.java | 3 +- .../wasm/intrinsics/AllocatorIntrinsic.java | 3 +- .../PlatformClassMetadataIntrinsic.java | 70 ++++++ .../wasm/intrinsics/WasmIntrinsicManager.java | 3 + .../backend/wasm/render/WasmCRenderer.java | 28 ++- .../org/teavm/backend/wasm/wasm-runtime.c | 12 + tests/src/test/js/frame.js | 4 +- .../teavm/junit/TeaVMTestConfiguration.java | 2 + 17 files changed, 423 insertions(+), 391 deletions(-) delete mode 100644 core/src/main/java/org/teavm/ast/Mangling.java create mode 100644 core/src/main/java/org/teavm/backend/lowlevel/generate/LowLevelNameProvider.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/generate/NameProvider.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/intrinsics/PlatformClassMetadataIntrinsic.java create mode 100644 core/src/main/resources/org/teavm/backend/wasm/wasm-runtime.c diff --git a/core/src/main/java/org/teavm/ast/Mangling.java b/core/src/main/java/org/teavm/ast/Mangling.java deleted file mode 100644 index 24db1d6d3..000000000 --- a/core/src/main/java/org/teavm/ast/Mangling.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2018 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.ast; - -import java.util.Arrays; -import java.util.stream.Collectors; -import org.teavm.model.FieldReference; -import org.teavm.model.MethodDescriptor; -import org.teavm.model.MethodReference; -import org.teavm.model.ValueType; - -public final class Mangling { - private Mangling() { - } - - public static String mangleIsSupertype(ValueType type) { - return "isSupertype$" + mangleType(type); - } - - public static String mangleMethod(MethodReference method) { - String className = mangleClassBase(method.getClassName()); - StringBuilder sb = new StringBuilder("method$" + className + "_"); - mangleSignature(method.getDescriptor(), sb); - return sb.toString(); - } - - public static String mangleVTableEntry(MethodDescriptor method) { - StringBuilder sb = new StringBuilder("m_"); - mangleSignature(method, sb); - return sb.toString(); - } - - private static void mangleSignature(MethodDescriptor method, StringBuilder sb) { - sb.append(mangleType(method.getResultType())); - sb.append(mangleString(method.getName())); - sb.append(Arrays.stream(method.getParameterTypes()) - .map(Mangling::mangleType) - .collect(Collectors.joining())); - } - - public static String mangleField(FieldReference field) { - String className = mangleClassBase(field.getClassName()); - return "field$" + className + "_" + field.getFieldName().length() + mangleString(field.getFieldName()); - } - - public static String mangleClass(String className) { - return "class$" + mangleClassBase(className); - } - - public static String mangleClassObject(ValueType type) { - return "tobject$" + mangleType(type); - } - - public static String mangleVtable(String className) { - return "vt$" + mangleClassBase(className); - } - - private static String mangleClassBase(String className) { - return className.length() + mangleString(className); - } - - public static String mangleInitializer(String className) { - return "clinit$" + mangleString(className); - } - - public static String mangleInstanceOf(ValueType type) { - return "instanceof$" + mangleType(type); - } - - private static String mangleString(String string) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < string.length(); ++i) { - char c = string.charAt(i); - switch (c) { - case '$': - sb.append(c); - break; - case '.': - sb.append("_g"); - break; - case '<': - sb.append("_h"); - break; - case '>': - sb.append("_i"); - break; - case '_': - sb.append("__"); - break; - default: - if (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c >= '0' && c <= '9') { - sb.append(c); - } else { - sb.append('_') - .append(Character.forDigit(c >>> 12, 16)) - .append(Character.forDigit((c >>> 8) & 0xF, 16)) - .append(Character.forDigit((c >>> 4) & 0xF, 16)) - .append(Character.forDigit(c & 0xF, 16)); - } - break; - } - } - return sb.toString(); - } - - public static String mangleType(ValueType type) { - if (type instanceof ValueType.Primitive) { - switch (((ValueType.Primitive) type).getKind()) { - case BOOLEAN: - return "Z"; - case BYTE: - return "B"; - case SHORT: - return "S"; - case CHARACTER: - return "C"; - case INTEGER: - return "I"; - case LONG: - return "L"; - case FLOAT: - return "F"; - case DOUBLE: - return "D"; - } - } else if (type instanceof ValueType.Void) { - return "V"; - } else if (type instanceof ValueType.Array) { - return "A" + mangleType(((ValueType.Array) type).getItemType()); - } else if (type instanceof ValueType.Object) { - String className = ((ValueType.Object) type).getClassName(); - return className.length() + "_" + mangleString(className); - } - throw new IllegalArgumentException("Don't know how to mangle " + type); - } -} diff --git a/core/src/main/java/org/teavm/backend/c/generate/NameProvider.java b/core/src/main/java/org/teavm/backend/c/generate/NameProvider.java index 0893982ac..7757b11d8 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/NameProvider.java +++ b/core/src/main/java/org/teavm/backend/c/generate/NameProvider.java @@ -16,46 +16,17 @@ package org.teavm.backend.c.generate; import java.util.Arrays; -import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; -import org.teavm.interop.Export; -import org.teavm.interop.Import; -import org.teavm.model.AnnotationReader; +import org.teavm.backend.lowlevel.generate.LowLevelNameProvider; import org.teavm.model.ClassReaderSource; import org.teavm.model.FieldReference; -import org.teavm.model.MethodReader; -import org.teavm.model.MethodReference; -import org.teavm.model.ValueType; import org.teavm.runtime.RuntimeArray; import org.teavm.runtime.RuntimeClass; import org.teavm.runtime.RuntimeObject; -public class NameProvider { - private ClassReaderSource classSource; - - private Set occupiedTopLevelNames = new HashSet<>(); - private Map> occupiedVtableNames = new HashMap<>(); - private Map> occupiedClassNames = new HashMap<>(); - - private Map methodNames = new HashMap<>(); - private Map virtualMethodNames = new HashMap<>(); - - private Map staticFieldNames = new HashMap<>(); - private Map memberFieldNames = new HashMap<>(); - - private Map classNames = new HashMap<>(); - private Map classInitializerNames = new HashMap<>(); - private Map classClassNames = new HashMap<>(); - private Map classInstanceNames = new HashMap<>(); - private Map supertypeNames = new HashMap<>(); - - private Set types = new LinkedHashSet<>(); - +public class NameProvider extends LowLevelNameProvider { public NameProvider(ClassReaderSource classSource) { - this.classSource = classSource; + super(classSource); occupiedTopLevelNames.add("JavaObject"); occupiedTopLevelNames.add("JavaArray"); @@ -75,166 +46,4 @@ public class NameProvider { occupiedClassNames.put(RuntimeObject.class.getName(), new HashSet<>(Arrays.asList("header"))); occupiedClassNames.put(RuntimeArray.class.getName(), new HashSet<>(Arrays.asList("length"))); } - - public String forMethod(MethodReference method) { - return methodNames.computeIfAbsent(method, k -> { - String specialName = getSpecialName(k); - return specialName == null ? pickUnoccupied(suggestForMethod(k)) : specialName; - }); - } - - public String forVirtualMethod(MethodReference method) { - return virtualMethodNames.computeIfAbsent(method, k -> { - Set occupied = occupiedVtableNames.computeIfAbsent(k.getClassName(), - c -> new HashSet<>(Arrays.asList("parent"))); - return pickUnoccupied(k.getName(), occupied); - }); - } - - private String getSpecialName(MethodReference methodReference) { - MethodReader method = classSource.resolve(methodReference); - if (method == null) { - return null; - } - - AnnotationReader exportAnnot = method.getAnnotations().get(Export.class.getName()); - if (exportAnnot != null) { - return exportAnnot.getValue("name").getString(); - } - - AnnotationReader importAnnot = method.getAnnotations().get(Import.class.getName()); - if (importAnnot != null) { - return importAnnot.getValue("name").getString(); - } - - return null; - } - - public String forStaticField(FieldReference field) { - return staticFieldNames.computeIfAbsent(field, k -> pickUnoccupied(suggestForStaticField(k))); - } - - public String forMemberField(FieldReference field) { - return memberFieldNames.computeIfAbsent(field, k -> { - Set occupied = occupiedClassNames.computeIfAbsent(k.getClassName(), - c -> new HashSet<>(Arrays.asList("parent"))); - return pickUnoccupied(sanitize(field.getFieldName()), occupied); - }); - } - - public String forClass(String className) { - return classNames.computeIfAbsent(className, k -> pickUnoccupied(suggestForClass(k))); - } - - public String forClassInitializer(String className) { - return classInitializerNames.computeIfAbsent(className, k -> pickUnoccupied("initclass_" + suggestForClass(k))); - } - - public String forClassClass(String className) { - types.add(ValueType.object(className)); - return classClassNames.computeIfAbsent(className, k -> pickUnoccupied(suggestForClass(k) + "_VT")); - } - - public String forClassInstance(ValueType type) { - types.add(type); - return classInstanceNames.computeIfAbsent(type, k -> pickUnoccupied(suggestForType(k) + "_Cls")); - } - - public String forSupertypeFunction(ValueType type) { - return supertypeNames.computeIfAbsent(type, k -> pickUnoccupied("supertypeof_" + suggestForType(k))); - } - - private String suggestForMethod(MethodReference method) { - StringBuilder sb = new StringBuilder(); - suggestForClass(method.getClassName(), sb); - sb.append('_'); - sb.append(sanitize(method.getName())); - return sb.toString(); - } - - private String suggestForStaticField(FieldReference field) { - StringBuilder sb = new StringBuilder(); - suggestForClass(field.getClassName(), sb); - sb.append('_'); - sb.append(sanitize(field.getFieldName())); - return sb.toString(); - } - - private String suggestForClass(String className) { - StringBuilder sb = new StringBuilder(); - suggestForClass(className, sb); - return sb.toString(); - } - - private void suggestForClass(String className, StringBuilder sb) { - int index = 0; - while (true) { - int next = className.indexOf('.', index); - if (next < 0) { - if (index > 0) { - sb.append('_'); - sb.append(sanitize(className.substring(index))); - } else { - sb.append(sanitize(className)); - } - return; - } - - sb.append(sanitize(String.valueOf(className.charAt(index)))); - index = next + 1; - } - } - - private String suggestForType(ValueType type) { - StringBuilder sb = new StringBuilder(); - suggestForType(type, sb); - return sb.toString(); - } - - private void suggestForType(ValueType type, StringBuilder sb) { - if (type instanceof ValueType.Object) { - suggestForClass(((ValueType.Object) type).getClassName(), sb); - } else if (type instanceof ValueType.Array) { - sb.append("Arr_"); - suggestForType(((ValueType.Array) type).getItemType(), sb); - } else { - sb.append(type.toString()); - } - } - - private String sanitize(String name) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < name.length(); ++i) { - char c = name.charAt(i); - switch (c) { - case '>': - case '<': - case '$': - sb.append('_'); - break; - default: - sb.append(c); - break; - } - } - return sb.toString(); - } - - private String pickUnoccupied(String name) { - return pickUnoccupied(name, occupiedTopLevelNames); - } - - private String pickUnoccupied(String name, Set occupied) { - String result = name; - int index = 0; - while (!occupied.add(result)) { - result = name + "_" + index++; - } - - return result; - } - - public Set getTypes() { - return types; - } } diff --git a/core/src/main/java/org/teavm/backend/lowlevel/generate/LowLevelNameProvider.java b/core/src/main/java/org/teavm/backend/lowlevel/generate/LowLevelNameProvider.java new file mode 100644 index 000000000..410191799 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/lowlevel/generate/LowLevelNameProvider.java @@ -0,0 +1,219 @@ +/* + * Copyright 2018 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.lowlevel.generate; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import org.teavm.interop.Export; +import org.teavm.interop.Import; +import org.teavm.model.AnnotationReader; +import org.teavm.model.ClassReaderSource; +import org.teavm.model.FieldReference; +import org.teavm.model.MethodReader; +import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; + +public abstract class LowLevelNameProvider { + private ClassReaderSource classSource; + + protected Set occupiedTopLevelNames = new HashSet<>(); + protected Map> occupiedVtableNames = new HashMap<>(); + protected Map> occupiedClassNames = new HashMap<>(); + + protected Map methodNames = new HashMap<>(); + protected Map virtualMethodNames = new HashMap<>(); + + protected Map staticFieldNames = new HashMap<>(); + protected Map memberFieldNames = new HashMap<>(); + + protected Map classNames = new HashMap<>(); + protected Map classInitializerNames = new HashMap<>(); + protected Map classClassNames = new HashMap<>(); + protected Map classInstanceNames = new HashMap<>(); + protected Map supertypeNames = new HashMap<>(); + + private Set types = new LinkedHashSet<>(); + + public LowLevelNameProvider(ClassReaderSource classSource) { + this.classSource = classSource; + } + + public String forMethod(MethodReference method) { + return methodNames.computeIfAbsent(method, k -> { + String specialName = getSpecialName(k); + return specialName == null ? pickUnoccupied(suggestForMethod(k)) : specialName; + }); + } + + public String forVirtualMethod(MethodReference method) { + return virtualMethodNames.computeIfAbsent(method, k -> { + Set occupied = occupiedVtableNames.computeIfAbsent(k.getClassName(), + c -> new HashSet<>(Arrays.asList("parent"))); + return pickUnoccupied(k.getName(), occupied); + }); + } + + private String getSpecialName(MethodReference methodReference) { + MethodReader method = classSource.resolve(methodReference); + if (method == null) { + return null; + } + + AnnotationReader exportAnnot = method.getAnnotations().get(Export.class.getName()); + if (exportAnnot != null) { + return exportAnnot.getValue("name").getString(); + } + + AnnotationReader importAnnot = method.getAnnotations().get(Import.class.getName()); + if (importAnnot != null) { + return importAnnot.getValue("name").getString(); + } + + return null; + } + + public String forStaticField(FieldReference field) { + return staticFieldNames.computeIfAbsent(field, k -> pickUnoccupied(suggestForStaticField(k))); + } + + public String forMemberField(FieldReference field) { + return memberFieldNames.computeIfAbsent(field, k -> { + Set occupied = occupiedClassNames.computeIfAbsent(k.getClassName(), + c -> new HashSet<>(Arrays.asList("parent"))); + return pickUnoccupied(sanitize(field.getFieldName()), occupied); + }); + } + + public String forClass(String className) { + return classNames.computeIfAbsent(className, k -> pickUnoccupied(suggestForClass(k))); + } + + public String forClassInitializer(String className) { + return classInitializerNames.computeIfAbsent(className, k -> pickUnoccupied("initclass_" + suggestForClass(k))); + } + + public String forClassClass(String className) { + types.add(ValueType.object(className)); + return classClassNames.computeIfAbsent(className, k -> pickUnoccupied(suggestForClass(k) + "_VT")); + } + + public String forClassInstance(ValueType type) { + types.add(type); + return classInstanceNames.computeIfAbsent(type, k -> pickUnoccupied(suggestForType(k) + "_Cls")); + } + + public String forSupertypeFunction(ValueType type) { + return supertypeNames.computeIfAbsent(type, k -> pickUnoccupied("supertypeof_" + suggestForType(k))); + } + + private String suggestForMethod(MethodReference method) { + StringBuilder sb = new StringBuilder(); + suggestForClass(method.getClassName(), sb); + sb.append('_'); + sb.append(sanitize(method.getName())); + return sb.toString(); + } + + private String suggestForStaticField(FieldReference field) { + StringBuilder sb = new StringBuilder(); + suggestForClass(field.getClassName(), sb); + sb.append('_'); + sb.append(sanitize(field.getFieldName())); + return sb.toString(); + } + + private String suggestForClass(String className) { + StringBuilder sb = new StringBuilder(); + suggestForClass(className, sb); + return sb.toString(); + } + + private void suggestForClass(String className, StringBuilder sb) { + int index = 0; + while (true) { + int next = className.indexOf('.', index); + if (next < 0) { + if (index > 0) { + sb.append('_'); + sb.append(sanitize(className.substring(index))); + } else { + sb.append(sanitize(className)); + } + return; + } + + sb.append(sanitize(String.valueOf(className.charAt(index)))); + index = next + 1; + } + } + + private String suggestForType(ValueType type) { + StringBuilder sb = new StringBuilder(); + suggestForType(type, sb); + return sb.toString(); + } + + private void suggestForType(ValueType type, StringBuilder sb) { + if (type instanceof ValueType.Object) { + suggestForClass(((ValueType.Object) type).getClassName(), sb); + } else if (type instanceof ValueType.Array) { + sb.append("Arr_"); + suggestForType(((ValueType.Array) type).getItemType(), sb); + } else { + sb.append(type.toString()); + } + } + + private String sanitize(String name) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < name.length(); ++i) { + char c = name.charAt(i); + switch (c) { + case '>': + case '<': + case '$': + sb.append('_'); + break; + default: + sb.append(c); + break; + } + } + return sb.toString(); + } + + private String pickUnoccupied(String name) { + return pickUnoccupied(name, occupiedTopLevelNames); + } + + private String pickUnoccupied(String name, Set occupied) { + String result = name; + int index = 0; + while (!occupied.add(result)) { + result = name + "_" + index++; + } + + return result; + } + + public Set getTypes() { + return types; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index fc2374ffb..0090179aa 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -27,9 +27,9 @@ import java.util.HashSet; import java.util.List; import java.util.Properties; import java.util.Set; -import org.teavm.ast.Mangling; import org.teavm.ast.decompilation.Decompiler; import org.teavm.backend.wasm.binary.BinaryWriter; +import org.teavm.backend.wasm.generate.NameProvider; import org.teavm.backend.wasm.generate.WasmClassGenerator; import org.teavm.backend.wasm.generate.WasmDependencyListener; import org.teavm.backend.wasm.generate.WasmGenerationContext; @@ -43,6 +43,7 @@ import org.teavm.backend.wasm.intrinsics.FunctionIntrinsic; import org.teavm.backend.wasm.intrinsics.GCIntrinsic; import org.teavm.backend.wasm.intrinsics.MutatorIntrinsic; import org.teavm.backend.wasm.intrinsics.PlatformClassIntrinsic; +import org.teavm.backend.wasm.intrinsics.PlatformClassMetadataIntrinsic; import org.teavm.backend.wasm.intrinsics.PlatformIntrinsic; import org.teavm.backend.wasm.intrinsics.PlatformObjectIntrinsic; import org.teavm.backend.wasm.intrinsics.ShadowStackIntrinsic; @@ -132,6 +133,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { private boolean debugging; private boolean wastEmitted; private boolean cEmitted; + private boolean cLineNumbersEmitted = Boolean.parseBoolean(System.getProperty("wasm.c.lineNumbers", "false")); private ClassInitializerInsertionTransformer clinitInsertionTransformer; private ClassInitializerEliminator classInitializerEliminator; private ClassInitializerTransformer classInitializerTransformer; @@ -205,6 +207,10 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { this.cEmitted = cEmitted; } + public void setCLineNumbersEmitted(boolean cLineNumbersEmitted) { + this.cLineNumbersEmitted = cLineNumbersEmitted; + } + public WasmBinaryVersion getVersion() { return version; } @@ -297,14 +303,15 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { VirtualTableProvider vtableProvider = createVirtualTableProvider(classes); TagRegistry tagRegistry = new TagRegistry(classes); BinaryWriter binaryWriter = new BinaryWriter(256); + NameProvider names = new NameProvider(controller.getUnprocessedClassSource()); WasmClassGenerator classGenerator = new WasmClassGenerator( - controller.getUnprocessedClassSource(), vtableProvider, tagRegistry, binaryWriter); + controller.getUnprocessedClassSource(), vtableProvider, tagRegistry, binaryWriter, names); Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), new HashSet<>(), new HashSet<>(), false, true); WasmStringPool stringPool = classGenerator.getStringPool(); WasmGenerationContext context = new WasmGenerationContext(classes, module, controller.getDiagnostics(), - vtableProvider, tagRegistry, stringPool); + vtableProvider, tagRegistry, stringPool, names); context.addIntrinsic(new AddressIntrinsic(classGenerator)); context.addIntrinsic(new StructureIntrinsic(classes, classGenerator)); @@ -315,6 +322,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { context.addIntrinsic(new PlatformIntrinsic()); context.addIntrinsic(new PlatformClassIntrinsic()); context.addIntrinsic(new PlatformObjectIntrinsic(classGenerator)); + context.addIntrinsic(new PlatformClassMetadataIntrinsic()); context.addIntrinsic(new ClassIntrinsic()); IntrinsicFactoryContext intrinsicFactoryContext = new IntrinsicFactoryContext(classes); @@ -362,13 +370,13 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { if (clinit == null) { continue; } - initFunction.getBody().add(new WasmCall(Mangling.mangleInitializer(className))); + initFunction.getBody().add(new WasmCall(names.forClassInitializer(className))); } module.add(initFunction); module.setStartFunction(initFunction); for (TeaVMEntryPoint entryPoint : controller.getEntryPoints().values()) { - String mangledName = Mangling.mangleMethod(entryPoint.getReference()); + String mangledName = names.forMethod(entryPoint.getReference()); WasmFunction function = module.getFunctions().get(mangledName); if (function != null) { function.setExportName(entryPoint.getPublicName()); @@ -403,7 +411,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { emitWast(module, buildTarget, getBaseName(outputName) + ".wast"); } if (cEmitted) { - emitC(module, buildTarget, getBaseName(outputName) + ".c"); + emitC(module, buildTarget, getBaseName(outputName) + ".wasm.c"); } } @@ -452,7 +460,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { private void emitC(WasmModule module, BuildTarget buildTarget, String outputName) throws IOException { WasmCRenderer renderer = new WasmCRenderer(); - renderer.setLineNumbersEmitted(Boolean.parseBoolean(System.getProperty("wasm.c.lineNumbers", "false"))); + renderer.setLineNumbersEmitted(cLineNumbersEmitted); renderer.setMemoryAccessChecked(Boolean.parseBoolean(System.getProperty("wasm.c.assertMemory", "false"))); renderer.render(module); try (OutputStream output = buildTarget.createResource(outputName); @@ -512,7 +520,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { if (method == implementor) { generator.generate(method.getReference(), implementor); } else { - generateStub(module, method, implementor); + generateStub(context.names, module, method, implementor); } if (controller.wasCancelled()) { return; @@ -523,7 +531,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { private void generateIsSupertypeFunctions(TagRegistry tagRegistry, WasmModule module, WasmClassGenerator classGenerator) { for (ValueType type : classGenerator.getRegisteredClasses()) { - WasmFunction function = new WasmFunction(Mangling.mangleIsSupertype(type)); + WasmFunction function = new WasmFunction(classGenerator.names.forSupertypeFunction(type)); function.getParameters().add(WasmType.INT32); function.setResult(WasmType.INT32); module.add(function); @@ -573,7 +581,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { testLower.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0))); body.add(testLower); - WasmExpression upperCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GT_SIGNED, + WasmExpression upperCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GE_SIGNED, new WasmGetLocal(subtypeVar), new WasmInt32Constant(upper)); WasmConditional testUpper = new WasmConditional(upperCondition); testUpper.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0))); @@ -615,17 +623,17 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { itemTest.setType(WasmType.INT32); itemTest.getThenBlock().getBody().add(new WasmInt32Constant(0)); - WasmCall delegateToItem = new WasmCall(Mangling.mangleIsSupertype(itemType)); + WasmCall delegateToItem = new WasmCall(classGenerator.names.forSupertypeFunction(itemType)); delegateToItem.getArguments().add(new WasmGetLocal(subtypeVar)); itemTest.getElseBlock().getBody().add(delegateToItem); body.add(new WasmReturn(itemTest)); } - private void generateStub(WasmModule module, MethodHolder method, MethodHolder implementor) { - WasmFunction function = module.getFunctions().get(Mangling.mangleMethod(method.getReference())); + private void generateStub(NameProvider names, WasmModule module, MethodHolder method, MethodHolder implementor) { + WasmFunction function = module.getFunctions().get(names.forMethod(method.getReference())); - WasmCall call = new WasmCall(Mangling.mangleMethod(implementor.getReference())); + WasmCall call = new WasmCall(names.forMethod(implementor.getReference())); for (WasmType param : function.getParameters()) { WasmLocal local = new WasmLocal(param); function.add(local); @@ -659,7 +667,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { continue; } - WasmFunction initFunction = new WasmFunction(Mangling.mangleInitializer(className)); + WasmFunction initFunction = new WasmFunction(classGenerator.names.forClassInitializer(className)); module.add(initFunction); WasmBlock block = new WasmBlock(false); @@ -679,7 +687,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { WasmInt32Subtype.INT32)); if (method != null) { - block.getBody().add(new WasmCall(Mangling.mangleMethod(method.getReference()))); + block.getBody().add(new WasmCall(classGenerator.names.forMethod(method.getReference()))); } if (controller.wasCancelled()) { diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/NameProvider.java b/core/src/main/java/org/teavm/backend/wasm/generate/NameProvider.java new file mode 100644 index 000000000..540fc5bdd --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/NameProvider.java @@ -0,0 +1,25 @@ +/* + * Copyright 2018 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.generate; + +import org.teavm.backend.lowlevel.generate.LowLevelNameProvider; +import org.teavm.model.ClassReaderSource; + +public class NameProvider extends LowLevelNameProvider { + public NameProvider(ClassReaderSource classSource) { + super(classSource); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java index 97b3d0387..9934a5471 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java @@ -24,7 +24,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import org.teavm.ast.Mangling; import org.teavm.backend.wasm.binary.BinaryWriter; import org.teavm.backend.wasm.binary.DataArray; import org.teavm.backend.wasm.binary.DataPrimitives; @@ -52,6 +51,7 @@ import org.teavm.runtime.RuntimeObject; public class WasmClassGenerator { private ClassReaderSource classSource; + public final NameProvider names; private Map binaryDataMap = new LinkedHashMap<>(); private BinaryWriter binaryWriter; private Map functions = new HashMap<>(); @@ -96,12 +96,13 @@ public class WasmClassGenerator { private static final int CLASS_SIMPLE_NAME = 13; public WasmClassGenerator(ClassReaderSource classSource, VirtualTableProvider vtableProvider, - TagRegistry tagRegistry, BinaryWriter binaryWriter) { + TagRegistry tagRegistry, BinaryWriter binaryWriter, NameProvider names) { this.classSource = classSource; this.vtableProvider = vtableProvider; this.tagRegistry = tagRegistry; this.binaryWriter = binaryWriter; this.stringPool = new WasmStringPool(this, binaryWriter); + this.names = names; } public WasmStringPool getStringPool() { @@ -173,7 +174,7 @@ public class WasmClassGenerator { binaryData.data.setAddress(CLASS_ITEM_TYPE, itemBinaryData.start); binaryData.data.setInt(CLASS_IS_INSTANCE, functionTable.size()); binaryData.data.setInt(CLASS_CANARY, RuntimeClass.computeCanary(4, 0)); - functionTable.add(Mangling.mangleIsSupertype(type)); + functionTable.add(names.forSupertypeFunction(type)); binaryData.data.setAddress(CLASS_SIMPLE_NAME, 0); binaryData.data.setInt(CLASS_INIT, -1); binaryData.start = binaryWriter.append(vtableSize > 0 ? wrapper : binaryData.data); @@ -189,7 +190,7 @@ public class WasmClassGenerator { value.setInt(CLASS_IS_INSTANCE, functionTable.size()); value.setAddress(CLASS_SIMPLE_NAME, 0); value.setInt(CLASS_INIT, -1); - functionTable.add(Mangling.mangleIsSupertype(type)); + functionTable.add(names.forSupertypeFunction(type)); return value; } @@ -226,7 +227,7 @@ public class WasmClassGenerator { header.setInt(CLASS_CANARY, RuntimeClass.computeCanary(occupiedSize, tag)); header.setAddress(CLASS_NAME, stringPool.getStringPointer(name)); header.setInt(CLASS_IS_INSTANCE, functionTable.size()); - functionTable.add(Mangling.mangleIsSupertype(ValueType.object(name))); + functionTable.add(names.forSupertypeFunction(ValueType.object(name))); header.setAddress(CLASS_PARENT, parentPtr); if (vtable != null) { @@ -259,7 +260,7 @@ public class WasmClassGenerator { if (cls != null && binaryData.start >= 0 && cls.getMethod(new MethodDescriptor("", ValueType.VOID)) != null) { header.setInt(CLASS_INIT, functionTable.size()); - functionTable.add(Mangling.mangleInitializer(name)); + functionTable.add(names.forClassInitializer(name)); } else { header.setInt(CLASS_INIT, -1); } @@ -340,7 +341,7 @@ public class WasmClassGenerator { } else { methodIndex = functions.computeIfAbsent(vtableEntry.getImplementor(), implementor -> { int result = functionTable.size(); - functionTable.add(Mangling.mangleMethod(implementor)); + functionTable.add(names.forMethod(implementor)); return result; }); } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java index 3fef6b600..e423764ea 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java @@ -43,18 +43,21 @@ public class WasmGenerationContext { private VirtualTableProvider vtableProvider; private TagRegistry tagRegistry; private WasmStringPool stringPool; + public final NameProvider names; private Map importedMethods = new HashMap<>(); private List intrinsics = new ArrayList<>(); private Map intrinsicCache = new HashMap<>(); public WasmGenerationContext(ClassReaderSource classSource, WasmModule module, Diagnostics diagnostics, - VirtualTableProvider vtableProvider, TagRegistry tagRegistry, WasmStringPool stringPool) { + VirtualTableProvider vtableProvider, TagRegistry tagRegistry, WasmStringPool stringPool, + NameProvider names) { this.classSource = classSource; this.module = module; this.diagnostics = diagnostics; this.vtableProvider = vtableProvider; this.tagRegistry = tagRegistry; this.stringPool = stringPool; + this.names = names; } public void addIntrinsic(WasmIntrinsic intrinsic) { diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java index ba3d1b9d8..cdbe7533e 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java @@ -42,7 +42,6 @@ import org.teavm.ast.InitClassStatement; import org.teavm.ast.InstanceOfExpr; import org.teavm.ast.InvocationExpr; import org.teavm.ast.InvocationType; -import org.teavm.ast.Mangling; import org.teavm.ast.MonitorEnterStatement; import org.teavm.ast.MonitorExitStatement; import org.teavm.ast.NewArrayExpr; @@ -185,7 +184,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { default: Class type = convertType(expr.getType()); MethodReference method = new MethodReference(WasmRuntime.class, "remainder", type, type, type); - WasmCall call = new WasmCall(Mangling.mangleMethod(method), false); + WasmCall call = new WasmCall(context.names.forMethod(method), false); accept(expr.getFirstOperand()); call.getArguments().add(result); @@ -239,7 +238,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { case COMPARE: { Class type = convertType(expr.getType()); MethodReference method = new MethodReference(WasmRuntime.class, "compare", type, type, int.class); - WasmCall call = new WasmCall(Mangling.mangleMethod(method), false); + WasmCall call = new WasmCall(context.names.forMethod(method), false); accept(expr.getFirstOperand()); call.getArguments().add(result); @@ -912,7 +911,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } if (expr.getType() == InvocationType.STATIC || expr.getType() == InvocationType.SPECIAL) { - String methodName = Mangling.mangleMethod(expr.getMethod()); + String methodName = context.names.forMethod(expr.getMethod()); WasmCall call = new WasmCall(methodName); if (context.getImportedMethod(expr.getMethod()) != null) { @@ -931,7 +930,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { block.getBody().add(new WasmSetLocal(tmp, allocateObject(expr.getMethod().getClassName(), expr.getLocation()))); - String methodName = Mangling.mangleMethod(expr.getMethod()); + String methodName = context.names.forMethod(expr.getMethod()); WasmCall call = new WasmCall(methodName); call.getArguments().add(new WasmGetLocal(tmp)); for (Expr argument : expr.getArguments()) { @@ -1201,7 +1200,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { private WasmExpression allocateObject(String className, TextLocation location) { int tag = classGenerator.getClassPointer(ValueType.object(className)); - String allocName = Mangling.mangleMethod(new MethodReference(Allocator.class, "allocate", + String allocName = context.names.forMethod(new MethodReference(Allocator.class, "allocate", RuntimeClass.class, Address.class)); WasmCall call = new WasmCall(allocName); call.getArguments().add(new WasmInt32Constant(tag)); @@ -1214,7 +1213,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { ValueType type = expr.getType(); int classPointer = classGenerator.getClassPointer(ValueType.arrayOf(type)); - String allocName = Mangling.mangleMethod(new MethodReference(Allocator.class, "allocateArray", + String allocName = context.names.forMethod(new MethodReference(Allocator.class, "allocateArray", RuntimeClass.class, int.class, Address.class)); WasmCall call = new WasmCall(allocName); call.getArguments().add(new WasmInt32Constant(classPointer)); @@ -1244,7 +1243,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } int classPointer = classGenerator.getClassPointer(ValueType.arrayOf(type)); - String allocName = Mangling.mangleMethod(new MethodReference(Allocator.class, "allocateMultiArray", + String allocName = context.names.forMethod(new MethodReference(Allocator.class, "allocateMultiArray", RuntimeClass.class, Address.class, int.class, RuntimeArray.class)); WasmCall call = new WasmCall(allocName); call.getArguments().add(new WasmInt32Constant(classPointer)); @@ -1345,7 +1344,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { @Override public void visit(InitClassStatement statement) { if (classGenerator.hasClinit(statement.getClassName())) { - result = new WasmCall(Mangling.mangleInitializer(statement.getClassName())); + result = new WasmCall(context.names.forClassInitializer(statement.getClassName())); result.setLocation(statement.getLocation()); } else { result = null; @@ -1378,10 +1377,12 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { @Override public void visit(MonitorEnterStatement statement) { + result = emptyStatement(statement.getLocation()); } @Override public void visit(MonitorExitStatement statement) { + result = emptyStatement(statement.getLocation()); } private WasmExpression negate(WasmExpression expr) { @@ -1546,6 +1547,11 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { public Diagnostics getDiagnostics() { return context.getDiagnostics(); } + + @Override + public NameProvider getNames() { + return context.names; + } }; private WasmLocal getTemporary(WasmType type) { @@ -1568,4 +1574,10 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, classIndex, new WasmInt32Constant(3)); } + + private WasmExpression emptyStatement(TextLocation location) { + WasmDrop drop = new WasmDrop(new WasmInt32Constant(0)); + drop.setLocation(location); + return drop; + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java index 42f0d9375..3c1958580 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java @@ -15,7 +15,6 @@ */ package org.teavm.backend.wasm.generate; -import org.teavm.ast.Mangling; import org.teavm.ast.RegularMethodNode; import org.teavm.ast.VariableNode; import org.teavm.ast.decompilation.Decompiler; @@ -39,6 +38,7 @@ public class WasmGenerator { private WasmGenerationContext context; private WasmClassGenerator classGenerator; private BinaryWriter binaryWriter; + private NameProvider names; public WasmGenerator(Decompiler decompiler, ClassHolderSource classSource, WasmGenerationContext context, WasmClassGenerator classGenerator, BinaryWriter binaryWriter) { @@ -47,12 +47,13 @@ public class WasmGenerator { this.context = context; this.classGenerator = classGenerator; this.binaryWriter = binaryWriter; + names = classGenerator.names; } public WasmFunction generateDefinition(MethodReference methodReference) { ClassHolder cls = classSource.get(methodReference.getClassName()); MethodHolder method = cls.getMethod(methodReference.getDescriptor()); - WasmFunction function = new WasmFunction(Mangling.mangleMethod(method.getReference())); + WasmFunction function = new WasmFunction(names.forMethod(method.getReference())); if (!method.hasModifier(ElementModifier.STATIC)) { function.getParameters().add(WasmType.INT32); @@ -72,7 +73,7 @@ public class WasmGenerator { MethodHolder method = cls.getMethod(methodReference.getDescriptor()); RegularMethodNode methodAst = decompiler.decompileRegular(bodyMethod); - WasmFunction function = context.getFunction(Mangling.mangleMethod(methodReference)); + WasmFunction function = context.getFunction(names.forMethod(methodReference)); int firstVariable = method.hasModifier(ElementModifier.STATIC) ? 1 : 0; for (int i = firstVariable; i < methodAst.getVariables().size(); ++i) { VariableNode variable = methodAst.getVariables().get(i); @@ -99,7 +100,7 @@ public class WasmGenerator { } public WasmFunction generateNative(MethodReference methodReference) { - WasmFunction function = context.getFunction(Mangling.mangleMethod(methodReference)); + WasmFunction function = context.getFunction(names.forMethod(methodReference)); WasmGenerationContext.ImportedMethod importedMethod = context.getImportedMethod(methodReference); if (importedMethod != null) { diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/AddressIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/AddressIntrinsic.java index e96eef320..576ff07b0 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/AddressIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/AddressIntrinsic.java @@ -18,7 +18,6 @@ package org.teavm.backend.wasm.intrinsics; import java.util.stream.Collectors; import org.teavm.ast.ConstantExpr; import org.teavm.ast.InvocationExpr; -import org.teavm.ast.Mangling; import org.teavm.backend.wasm.WasmRuntime; import org.teavm.backend.wasm.generate.WasmClassGenerator; import org.teavm.backend.wasm.model.WasmType; @@ -154,7 +153,7 @@ public class AddressIntrinsic implements WasmIntrinsic { case "align": { MethodReference delegate = new MethodReference(WasmRuntime.class.getName(), invocation.getMethod().getDescriptor()); - WasmCall call = new WasmCall(Mangling.mangleMethod(delegate)); + WasmCall call = new WasmCall(manager.getNames().forMethod(delegate)); call.getArguments().addAll(invocation.getArguments().stream() .map(arg -> manager.generate(arg)) .collect(Collectors.toList())); diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/AllocatorIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/AllocatorIntrinsic.java index e7c54c0f8..d59f7d220 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/AllocatorIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/AllocatorIntrinsic.java @@ -17,7 +17,6 @@ package org.teavm.backend.wasm.intrinsics; import java.util.stream.Collectors; import org.teavm.ast.InvocationExpr; -import org.teavm.ast.Mangling; import org.teavm.backend.wasm.WasmRuntime; import org.teavm.backend.wasm.generate.WasmClassGenerator; import org.teavm.backend.wasm.model.expression.WasmCall; @@ -63,7 +62,7 @@ public class AllocatorIntrinsic implements WasmIntrinsic { case "moveMemoryBlock": { MethodReference delegateMethod = new MethodReference(WasmRuntime.class.getName(), invocation.getMethod().getDescriptor()); - WasmCall call = new WasmCall(Mangling.mangleMethod(delegateMethod)); + WasmCall call = new WasmCall(manager.getNames().forMethod(delegateMethod)); call.getArguments().addAll(invocation.getArguments().stream() .map(manager::generate) .collect(Collectors.toList())); diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/PlatformClassMetadataIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/PlatformClassMetadataIntrinsic.java new file mode 100644 index 000000000..d8ebe0100 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/PlatformClassMetadataIntrinsic.java @@ -0,0 +1,70 @@ +/* + * Copyright 2018 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.intrinsics; + +import org.teavm.ast.InvocationExpr; +import org.teavm.ast.QualificationExpr; +import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.backend.wasm.model.expression.WasmUnreachable; +import org.teavm.model.FieldReference; +import org.teavm.model.MethodReference; +import org.teavm.runtime.RuntimeClass; + +public class PlatformClassMetadataIntrinsic implements WasmIntrinsic { + private static final String PLATFORM_CLASS_METADATA = "org.teavm.platform.PlatformClassMetadata"; + private static final FieldReference ITEM_TYPE_FIELD = new FieldReference( + RuntimeClass.class.getName(), "itemType"); + private static final FieldReference SUPERCLASS_FIELD = new FieldReference( + RuntimeClass.class.getName(), "parent"); + private static final FieldReference NAME_FIELD = new FieldReference( + RuntimeClass.class.getName(), "name"); + + @Override + public boolean isApplicable(MethodReference methodReference) { + if (!methodReference.getClassName().equals(PLATFORM_CLASS_METADATA)) { + return false; + } + switch (methodReference.getName()) { + case "getArrayItem": + case "getSuperclass": + case "getName": + return true; + } + return false; + } + + @Override + public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) { + switch (invocation.getMethod().getName()) { + case "getArrayItem": + return fieldAccess(manager, invocation, ITEM_TYPE_FIELD); + case "getSuperclass": + return fieldAccess(manager, invocation, SUPERCLASS_FIELD); + case "getName": + return fieldAccess(manager, invocation, NAME_FIELD); + default: + return new WasmUnreachable(); + } + } + + private WasmExpression fieldAccess(WasmIntrinsicManager manager, InvocationExpr expr, FieldReference field) { + QualificationExpr qualification = new QualificationExpr(); + qualification.setField(field); + qualification.setQualified(expr.getArguments().get(0)); + qualification.setLocation(expr.getLocation()); + return manager.generate(qualification); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java index 3b23f5f47..a3eb9eed9 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java @@ -17,6 +17,7 @@ package org.teavm.backend.wasm.intrinsics; import org.teavm.ast.Expr; import org.teavm.backend.wasm.binary.BinaryWriter; +import org.teavm.backend.wasm.generate.NameProvider; import org.teavm.backend.wasm.generate.WasmStringPool; import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.diagnostics.Diagnostics; @@ -29,4 +30,6 @@ public interface WasmIntrinsicManager { WasmStringPool getStringPool(); Diagnostics getDiagnostics(); + + NameProvider getNames(); } diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java index 0cb8c3ebd..975741026 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java @@ -15,6 +15,9 @@ */ package org.teavm.backend.wasm.render; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import org.teavm.backend.wasm.model.WasmFunction; @@ -65,10 +68,7 @@ public class WasmCRenderer { } public void render(WasmModule module) { - line("#include "); - line("#include "); - line("#include "); - line("#include "); + renderPrologue(); line(""); renderFunctionDeclarations(module); @@ -100,6 +100,22 @@ public class WasmCRenderer { line("}"); } + private void renderPrologue() { + ClassLoader classLoader = WasmCRenderer.class.getClassLoader(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader( + classLoader.getResourceAsStream("org/teavm/backend/wasm/wasm-runtime.c")))) { + while (true) { + String line = reader.readLine(); + if (line == null) { + break; + } + line(line); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + private void renderHeap(WasmModule module) { line("wasm_heap_size = " + 65536 * module.getMemorySize() + ";"); line("wasm_heap = malloc(" + 65536 * module.getMemorySize() + ");"); @@ -143,7 +159,9 @@ public class WasmCRenderer { private void renderFunctionDeclarations(WasmModule module) { for (WasmFunction function : module.getFunctions().values()) { - line(functionDeclaration(function) + ";"); + if (function.getImportName() == null) { + line(functionDeclaration(function) + ";"); + } } } diff --git a/core/src/main/resources/org/teavm/backend/wasm/wasm-runtime.c b/core/src/main/resources/org/teavm/backend/wasm/wasm-runtime.c new file mode 100644 index 000000000..c6963450b --- /dev/null +++ b/core/src/main/resources/org/teavm/backend/wasm/wasm-runtime.c @@ -0,0 +1,12 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +static inline float TeaVM_getNaN() { + return NAN; +} \ No newline at end of file diff --git a/tests/src/test/js/frame.js b/tests/src/test/js/frame.js index d340f03bc..6f0ef2d1d 100644 --- a/tests/src/test/js/frame.js +++ b/tests/src/test/js/frame.js @@ -104,14 +104,14 @@ function launchWasmTest(path, callback) { case "SUCCESS": callback({status: "OK"}); break; - case "FAILED": + case "FAILURE": callback({ status: "failed", errorMessage: output.join("\n") }); break; default: - output.push(TeaVM_outputBuffer); + output.push(outputBuffer); outputBuffer = ""; } } else { diff --git a/tools/junit/src/main/java/org/teavm/junit/TeaVMTestConfiguration.java b/tools/junit/src/main/java/org/teavm/junit/TeaVMTestConfiguration.java index 0be5bf2c3..bdb5ad0f4 100644 --- a/tools/junit/src/main/java/org/teavm/junit/TeaVMTestConfiguration.java +++ b/tools/junit/src/main/java/org/teavm/junit/TeaVMTestConfiguration.java @@ -95,6 +95,8 @@ interface TeaVMTestConfiguration { public void apply(WasmTarget target) { target.setMinHeapSize(32 * 1024 * 1024); target.setWastEmitted(true); + target.setCEmitted(true); + target.setDebugging(true); } };