From ec8bae1d40039a28380299a92f25d6a06d6d5992 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 4 Apr 2019 17:20:28 +0300 Subject: [PATCH] C: fix bugs in RTTI. Support args parameter of main method --- .../org/teavm/classlib/java/lang/TDouble.java | 6 +- .../org/teavm/classlib/java/lang/TFloat.java | 6 +- .../java/org/teavm/backend/c/CTarget.java | 52 ++++++++++-- .../backend/c/generate/ClassGenerator.java | 63 ++++++++------ .../c/generate/CodeGenerationVisitor.java | 2 +- .../ExceptionHandlingDependencyListener.java | 49 +++++++++++ .../org/teavm/backend/wasm/WasmTarget.java | 4 +- .../org/teavm/model/classes/TagRegistry.java | 29 +++---- .../teavm/model/util/RegisterAllocator.java | 6 +- .../main/java/org/teavm/runtime/Fiber.java | 6 +- .../org/teavm/backend/c/runtime-epilogue.c | 83 +++++++++++++------ .../resources/org/teavm/backend/c/runtime.c | 2 + .../benchmark/teavm/Gtk3BenchmarkStarter.java | 3 + .../src/test/java/org/teavm/vm/RttiTest.java | 75 +++++++++++++++++ 14 files changed, 304 insertions(+), 82 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/lowlevel/dependency/ExceptionHandlingDependencyListener.java create mode 100644 tests/src/test/java/org/teavm/vm/RttiTest.java diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java index ef7f44a21..0179b05c6 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java @@ -206,7 +206,11 @@ public class TDouble extends TNumber implements TComparable { @Override public int hashCode() { - long h = doubleToLongBits(value); + return hashCode(value); + } + + public static int hashCode(double d) { + long h = doubleToLongBits(d); return (int) (h >>> 32) ^ (int) h; } diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java index 16600faaf..c1d0950f0 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java @@ -87,7 +87,11 @@ public class TFloat extends TNumber implements TComparable { @Override public int hashCode() { - return floatToIntBits(value); + return hashCode(value); + } + + public static int hashCode(float f) { + return floatToIntBits(f); } @JSBody(params = "v", script = "return isNaN(v);") diff --git a/core/src/main/java/org/teavm/backend/c/CTarget.java b/core/src/main/java/org/teavm/backend/c/CTarget.java index f1b8ce232..e928301a3 100644 --- a/core/src/main/java/org/teavm/backend/c/CTarget.java +++ b/core/src/main/java/org/teavm/backend/c/CTarget.java @@ -38,6 +38,7 @@ import org.teavm.ast.decompilation.Decompiler; import org.teavm.backend.c.analyze.CDependencyListener; import org.teavm.backend.c.generate.BufferedCodeWriter; import org.teavm.backend.c.generate.ClassGenerator; +import org.teavm.backend.c.generate.CodeGenerationVisitor; import org.teavm.backend.c.generate.CodeGeneratorUtil; import org.teavm.backend.c.generate.CodeWriter; import org.teavm.backend.c.generate.GenerationContext; @@ -63,6 +64,7 @@ import org.teavm.backend.c.intrinsic.PlatformObjectIntrinsic; import org.teavm.backend.c.intrinsic.RuntimeClassIntrinsic; import org.teavm.backend.c.intrinsic.ShadowStackIntrinsic; import org.teavm.backend.c.intrinsic.StructureIntrinsic; +import org.teavm.backend.lowlevel.dependency.ExceptionHandlingDependencyListener; import org.teavm.backend.lowlevel.transform.CoroutineTransformation; import org.teavm.dependency.ClassDependency; import org.teavm.dependency.DependencyAnalyzer; @@ -71,6 +73,7 @@ import org.teavm.interop.Address; import org.teavm.interop.PlatformMarkers; import org.teavm.interop.Structure; import org.teavm.model.BasicBlock; +import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ClassReader; @@ -116,6 +119,9 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { private static final Set VIRTUAL_METHODS = new HashSet<>(Arrays.asList( new MethodReference(Object.class, "clone", Object.class) )); + private static final MethodReference STRING_CONSTRUCTOR = new MethodReference(String.class, + "", char[].class, void.class); + private TeaVMTargetController controller; private ClassInitializerEliminator classInitializerEliminator; private ClassInitializerTransformer classInitializerTransformer; @@ -196,6 +202,10 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { dependencyAnalyzer.linkClass("java.lang.String"); dependencyAnalyzer.linkClass("java.lang.Class"); dependencyAnalyzer.linkField(new FieldReference("java.lang.String", "hashCode")); + dependencyAnalyzer.linkMethod(STRING_CONSTRUCTOR) + .propagate(0, "java.lang.String") + .propagate(1, "[C") + .use(); ClassDependency runtimeClassDep = dependencyAnalyzer.linkClass(RuntimeClass.class.getName()); ClassDependency runtimeObjectDep = dependencyAnalyzer.linkClass(RuntimeObject.class.getName()); @@ -209,7 +219,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { dependencyAnalyzer.linkMethod(new MethodReference(Fiber.class, "isResuming", boolean.class)).use(); dependencyAnalyzer.linkMethod(new MethodReference(Fiber.class, "isSuspending", boolean.class)).use(); dependencyAnalyzer.linkMethod(new MethodReference(Fiber.class, "current", Fiber.class)).use(); - dependencyAnalyzer.linkMethod(new MethodReference(Fiber.class, "startMain", void.class)).use(); + dependencyAnalyzer.linkMethod(new MethodReference(Fiber.class, "startMain", String[].class, void.class)).use(); dependencyAnalyzer.linkMethod(new MethodReference(EventQueue.class, "process", void.class)).use(); dependencyAnalyzer.linkMethod(new MethodReference(Thread.class, "setCurrentThread", Thread.class, void.class)).use(); @@ -220,6 +230,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { dependencyAnalyzer.linkMethod(method.getReference()).use(); } } + + dependencyAnalyzer.addDependencyListener(new ExceptionHandlingDependencyListener()); } @Override @@ -249,7 +261,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { @Override public void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName) throws IOException { VirtualTableProvider vtableProvider = createVirtualTableProvider(classes); - TagRegistry tagRegistry = new TagRegistry(classes); + ClassHierarchy hierarchy = new ClassHierarchy(classes); + TagRegistry tagRegistry = new TagRegistry(classes, hierarchy); StringPool stringPool = new StringPool(); Decompiler decompiler = new Decompiler(classes, new HashSet<>(), false, true); @@ -404,6 +417,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { private void generateSpecialFunctions(GenerationContext context, CodeWriter writer) { generateThrowCCE(context, writer); generateAllocateStringArray(context, writer); + generateAllocateCharArray(context, writer); + generateCreateString(context, writer); } private void generateThrowCCE(GenerationContext context, CodeWriter writer) { @@ -425,6 +440,25 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { writer.outdent().println("}"); } + private void generateAllocateCharArray(GenerationContext context, CodeWriter writer) { + writer.println("static JavaArray* teavm_allocateCharArray(int32_t size) {").indent(); + String allocateArrayName = context.getNames().forMethod(new MethodReference(Allocator.class, + "allocateArray", RuntimeClass.class, int.class, Address.class)); + String charClassName = context.getNames().forClassInstance(ValueType.arrayOf(ValueType.CHARACTER)); + writer.println("return (JavaArray*) " + allocateArrayName + "(&" + charClassName + ", size);"); + writer.outdent().println("}"); + } + + private void generateCreateString(GenerationContext context, CodeWriter writer) { + NameProvider names = context.getNames(); + writer.println("static JavaString* teavm_createString(JavaArray* array) {").indent(); + writer.print("JavaString* str = (JavaString*) ").print(names.forMethod(CodeGenerationVisitor.ALLOC_METHOD)) + .print("(&").print(names.forClassInstance(ValueType.object("java.lang.String"))).println(");"); + writer.print(names.forMethod(STRING_CONSTRUCTOR)).println("(str, array);"); + writer.println("return str;"); + writer.outdent().println("}"); + } + private void generateArrayOfClassReferences(GenerationContext context, CodeWriter writer, List types) { writer.print("static JavaClass* teavm_classReferences[" + types.size() + "] = {").indent(); @@ -512,9 +546,10 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { } private void generateFiberStart(GenerationContext context, CodeWriter writer) { - String startName = context.getNames().forMethod(new MethodReference(Fiber.class, "startMain", void.class)); + String startName = context.getNames().forMethod(new MethodReference(Fiber.class, + "startMain", String[].class, void.class)); String processName = context.getNames().forMethod(new MethodReference(EventQueue.class, "process", void.class)); - writer.println(startName + "();"); + writer.println(startName + "(teavm_parseArguments(argc, argv));"); writer.println(processName + "();"); } @@ -537,7 +572,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { public void apply(IntrinsicContext context, InvocationExpr invocation) { switch (invocation.getMethod().getName()) { case "runMain": - generateCallToMainMethod(context.names(), context.writer()); + generateCallToMainMethod(context, invocation); break; case "setCurrentThread": String methodName = context.names().forMethod(new MethodReference(Thread.class, @@ -550,11 +585,14 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { } } - private void generateCallToMainMethod(NameProvider names, CodeWriter writer) { + private void generateCallToMainMethod(IntrinsicContext context, InvocationExpr invocation) { + NameProvider names = context.names(); TeaVMEntryPoint entryPoint = controller.getEntryPoints().get("main"); if (entryPoint != null) { String mainMethod = names.forMethod(entryPoint.getMethod()); - writer.println(mainMethod + "(NULL);"); + context.writer().print(mainMethod + "("); + context.emit(invocation.getArguments().get(0)); + context.writer().println(");"); } } diff --git a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java index 9ea443d80..9addac4e0 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java @@ -118,6 +118,7 @@ public class ClassGenerator { public void generateClass(ClassHolder cls) { generateClassStructure(cls); + generateClassStaticFields(cls); generateClassMethods(cls); generateInitializer(cls); } @@ -230,7 +231,6 @@ public class ClassGenerator { String name = context.getNames().forClass(cls.getName()); CodeWriter structWriter = structuresWriter.fragment(); - CodeWriter fieldsWriter = structuresWriter.fragment(); structWriter.print("typedef struct ").print(name).println(" {").indent(); @@ -244,38 +244,19 @@ public class ClassGenerator { int layoutIndex = currentLayoutIndex; - FieldReference[] staticFields = new FieldReference[cls.getFields().size()]; - int staticIndex = 0; FieldReference[] instanceFields = new FieldReference[cls.getFields().size()]; int instanceIndex = 0; for (FieldHolder field : cls.getFields()) { if (field.hasModifier(ElementModifier.STATIC)) { - String fieldName = context.getNames().forStaticField(field.getReference()); - fieldsWriter.print("static ").printStrictType(field.getType()).print(" ").print(fieldName) - .println(";"); - if (isReferenceType(field.getType())) { - staticFields[staticIndex++] = field.getReference(); - } - - Object initialValue = field.getInitialValue(); - if (initialValue == null) { - initialValue = getDefaultValue(field.getType()); - } - staticFieldInitWriter.print(fieldName + " = "); - CodeGeneratorUtil.writeValue(staticFieldInitWriter, context, initialValue); - staticFieldInitWriter.println(";"); - } else { - String fieldName = context.getNames().forMemberField(field.getReference()); - structWriter.printStrictType(field.getType()).print(" ").print(fieldName).println(";"); - if (isReferenceType(field.getType())) { - instanceFields[instanceIndex++] = field.getReference(); - } + continue; + } + String fieldName = context.getNames().forMemberField(field.getReference()); + structWriter.printStrictType(field.getType()).print(" ").print(fieldName).println(";"); + if (isReferenceType(field.getType())) { + instanceFields[instanceIndex++] = field.getReference(); } } - if (staticIndex > 0) { - staticGcRoots.add(Arrays.copyOf(staticFields, staticIndex)); - } if (instanceIndex > 0) { classLayoutOffsets.put(cls.getName(), layoutIndex); layouts.add(Arrays.copyOf(instanceFields, instanceIndex)); @@ -285,6 +266,36 @@ public class ClassGenerator { structWriter.outdent().print("} ").print(name).println(";"); } + private void generateClassStaticFields(ClassHolder cls) { + CodeWriter fieldsWriter = structuresWriter.fragment(); + + FieldReference[] staticFields = new FieldReference[cls.getFields().size()]; + int staticIndex = 0; + for (FieldHolder field : cls.getFields()) { + if (!field.hasModifier(ElementModifier.STATIC)) { + continue; + } + String fieldName = context.getNames().forStaticField(field.getReference()); + fieldsWriter.print("static ").printStrictType(field.getType()).print(" ").print(fieldName) + .println(";"); + if (isReferenceType(field.getType())) { + staticFields[staticIndex++] = field.getReference(); + } + + Object initialValue = field.getInitialValue(); + if (initialValue == null) { + initialValue = getDefaultValue(field.getType()); + } + staticFieldInitWriter.print(fieldName + " = "); + CodeGeneratorUtil.writeValue(staticFieldInitWriter, context, initialValue); + staticFieldInitWriter.println(";"); + } + + if (staticIndex > 0) { + staticGcRoots.add(Arrays.copyOf(staticFields, staticIndex)); + } + } + private static Object getDefaultValue(ValueType type) { if (type instanceof ValueType.Primitive) { ValueType.Primitive primitive = (ValueType.Primitive) type; diff --git a/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java b/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java index 5222807fd..6b2f40f3f 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java @@ -78,7 +78,7 @@ import org.teavm.runtime.RuntimeClass; import org.teavm.runtime.RuntimeObject; public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { - private static final MethodReference ALLOC_METHOD = new MethodReference(Allocator.class, + public static final MethodReference ALLOC_METHOD = new MethodReference(Allocator.class, "allocate", RuntimeClass.class, Address.class); private static final MethodReference ALLOC_ARRAY_METHOD = new MethodReference(Allocator.class, "allocateArray", RuntimeClass.class, int.class, Address.class); diff --git a/core/src/main/java/org/teavm/backend/lowlevel/dependency/ExceptionHandlingDependencyListener.java b/core/src/main/java/org/teavm/backend/lowlevel/dependency/ExceptionHandlingDependencyListener.java new file mode 100644 index 000000000..d4b5a97c4 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/lowlevel/dependency/ExceptionHandlingDependencyListener.java @@ -0,0 +1,49 @@ +/* + * Copyright 2019 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.dependency; + +import org.teavm.dependency.AbstractDependencyListener; +import org.teavm.dependency.DependencyAgent; +import org.teavm.dependency.DependencyNode; +import org.teavm.dependency.DependencyType; +import org.teavm.dependency.MethodDependency; +import org.teavm.model.FieldReference; +import org.teavm.model.MethodReference; + +public class ExceptionHandlingDependencyListener extends AbstractDependencyListener { + private static final MethodReference FILL_IN_STACK_TRACE = new MethodReference(Throwable.class, + "fillInStackTrace", Throwable.class); + private static final FieldReference STACK_TRACE = new FieldReference(Throwable.class.getName(), "stackTrace"); + private static final MethodReference STACK_TRACE_ELEMENT_INIT = new MethodReference(StackTraceElement.class, + "", String.class, String.class, String.class, int.class, void.class); + + @Override + public void methodReached(DependencyAgent agent, MethodDependency method) { + if (method.getReference().equals(FILL_IN_STACK_TRACE)) { + DependencyNode node = agent.linkField(STACK_TRACE).getValue(); + node.propagate(agent.getType("[java/lang.StackTraceElement;")); + node.getArrayItem().propagate(agent.getType("java.lang.StackTraceElement")); + + MethodDependency initElem = agent.linkMethod(STACK_TRACE_ELEMENT_INIT); + initElem.use(); + DependencyType stringType = agent.getType("java.lang.String"); + initElem.propagate(0, agent.getType(STACK_TRACE_ELEMENT_INIT.getClassName())); + initElem.propagate(1, stringType); + initElem.propagate(2, stringType); + initElem.propagate(3, stringType); + } + } +} 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 b5d3a3b85..e101dd2e7 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -104,6 +104,7 @@ import org.teavm.interop.StaticInit; import org.teavm.model.AnnotationHolder; import org.teavm.model.BasicBlock; import org.teavm.model.CallLocation; +import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ClassReader; @@ -314,7 +315,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { WasmFunction initFunction = new WasmFunction("__start__"); VirtualTableProvider vtableProvider = createVirtualTableProvider(classes); - TagRegistry tagRegistry = new TagRegistry(classes); + ClassHierarchy hierarchy = new ClassHierarchy(classes); + TagRegistry tagRegistry = new TagRegistry(classes, hierarchy); BinaryWriter binaryWriter = new BinaryWriter(256); NameProvider names = new NameProvider(controller.getUnprocessedClassSource()); WasmClassGenerator classGenerator = new WasmClassGenerator(classes, controller.getUnprocessedClassSource(), diff --git a/core/src/main/java/org/teavm/model/classes/TagRegistry.java b/core/src/main/java/org/teavm/model/classes/TagRegistry.java index 29991f77b..c1bad1b5b 100644 --- a/core/src/main/java/org/teavm/model/classes/TagRegistry.java +++ b/core/src/main/java/org/teavm/model/classes/TagRegistry.java @@ -26,15 +26,15 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; +import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassReader; -import org.teavm.model.ClassReaderSource; import org.teavm.model.ElementModifier; import org.teavm.model.ListableClassReaderSource; public class TagRegistry { private Map> ranges = new HashMap<>(); - public TagRegistry(ListableClassReaderSource classSource) { + public TagRegistry(ListableClassReaderSource classSource, ClassHierarchy classHierarchy) { List roots = new ArrayList<>(); Map> implementedBy = new HashMap<>(); @@ -45,8 +45,7 @@ public class TagRegistry { continue; } for (String iface : cls.getInterfaces()) { - String topmostImplementor = findTopmostImplementor(classSource, className, iface); - markImplementor(classSource, topmostImplementor, iface, implementedBy); + markImplementor(classHierarchy, className, iface, implementedBy); } if (cls.getParent() == null || cls.getParent().equals(cls.getName())) { roots.add(className); @@ -58,8 +57,7 @@ public class TagRegistry { Map simpleRanges = new HashMap<>(); int current = 0; for (String root : roots) { - assignRange(current, hierarchy, root, simpleRanges); - ++current; + current = 1 + assignRange(current, hierarchy, root, simpleRanges); } for (String className : classSource.getClassNames()) { @@ -71,7 +69,7 @@ public class TagRegistry { Set implementorRoots = implementedBy.get(cls.getName()); if (implementorRoots != null) { List ifaceRanges = implementorRoots.stream() - .map(implementor -> simpleRanges.get(implementor)) + .map(simpleRanges::get) .filter(Objects::nonNull) .sorted(Comparator.comparing(range -> range.lower)) .collect(Collectors.toList()); @@ -86,37 +84,40 @@ public class TagRegistry { } } } + } - private String findTopmostImplementor(ClassReaderSource classSource, String className, String ifaceName) { - ClassReader cls = classSource.get(className); + private String findTopmostImplementor(ClassHierarchy hierarchy, String className, String ifaceName) { + ClassReader cls = hierarchy.getClassSource().get(className); if (cls == null) { return null; } if (cls.getParent() != null) { - String candidate = findTopmostImplementor(classSource, cls.getParent(), ifaceName); + String candidate = findTopmostImplementor(hierarchy, cls.getParent(), ifaceName); if (candidate != null) { return candidate; } } - return cls.getInterfaces().contains(ifaceName) ? className : null; + return hierarchy.isSuperType(ifaceName, className, false) ? className : null; } - private void markImplementor(ClassReaderSource classSource, String className, String ifaceName, + private void markImplementor(ClassHierarchy hierarchy, String className, String ifaceName, Map> implementedBy) { + className = findTopmostImplementor(hierarchy, className, ifaceName); + if (!implementedBy.computeIfAbsent(ifaceName, key -> new LinkedHashSet<>()).add(className)) { return; } - ClassReader iface = classSource.get(ifaceName); + ClassReader iface = hierarchy.getClassSource().get(ifaceName); if (iface == null) { return; } for (String superIface : iface.getInterfaces()) { - markImplementor(classSource, className, superIface, implementedBy); + markImplementor(hierarchy, className, superIface, implementedBy); } } diff --git a/core/src/main/java/org/teavm/model/util/RegisterAllocator.java b/core/src/main/java/org/teavm/model/util/RegisterAllocator.java index d2e93afab..55539e4dd 100644 --- a/core/src/main/java/org/teavm/model/util/RegisterAllocator.java +++ b/core/src/main/java/org/teavm/model/util/RegisterAllocator.java @@ -115,9 +115,11 @@ public class RegisterAllocator { case FLOAT: return 2; case DOUBLE: - return 2; - default: return 3; + case OBJECT: + return 4; + default: + return 5; } } diff --git a/core/src/main/java/org/teavm/runtime/Fiber.java b/core/src/main/java/org/teavm/runtime/Fiber.java index 2ed1f3dc9..65db5b75a 100644 --- a/core/src/main/java/org/teavm/runtime/Fiber.java +++ b/core/src/main/java/org/teavm/runtime/Fiber.java @@ -213,11 +213,11 @@ public class Fiber { new Fiber(runner, daemon).start(); } - static void startMain() { - start(Fiber::runMain, false); + static void startMain(String[] args) { + start(() -> runMain(args), false); } - static native void runMain(); + static native void runMain(String[] args); private void start() { Fiber former = current; diff --git a/core/src/main/resources/org/teavm/backend/c/runtime-epilogue.c b/core/src/main/resources/org/teavm/backend/c/runtime-epilogue.c index ea68293ac..5a181b704 100644 --- a/core/src/main/resources/org/teavm/backend/c/runtime-epilogue.c +++ b/core/src/main/resources/org/teavm/backend/c/runtime-epilogue.c @@ -52,38 +52,37 @@ static JavaArray* teavm_resourceMapKeys(TeaVM_ResourceMap *map) { return array; } -static inline int teavm_isHighSurrogate(char16_t c) { - return (c & 0xFC00) == 0xD800; -} - -static inline int teavm_isLowSurrogate(char16_t c) { - return (c & 0xFC00) == 0xDC00; -} - -static inline int teavm_isSurrogatePair(char16_t* chars, int32_t index, int32_t limit) { - return index < limit - 1 && teavm_isHighSurrogate(chars[index]) && teavm_isLowSurrogate(chars[index + 1]); -} - -static inline int teavm_getCodePoint(char16_t* chars, int32_t *index, int32_t limit) { - wchar_t codePoint; - if (teavm_isSurrogatePair(chars, *index, limit)) { - codePoint = (wchar_t) (((((chars[*index] & 0x03FF) << 10) | chars[*index + 1] & 0x03FF)) + 0x010000); - (*index)++; - } else { - codePoint = (wchar_t) chars[*index]; - } - return codePoint; -} - static size_t teavm_mbSize(char16_t* javaChars, int32_t javaCharsCount) { size_t sz = 0; - char buffer[6]; + char buffer[__STDC_UTF_16__]; + mbstate_t state = {0}; for (int32_t i = 0; i < javaCharsCount; ++i) { - sz += wctomb(buffer, teavm_getCodePoint(javaChars, &i, javaCharsCount)); + size_t result = c16rtomb(buffer, javaChars[i], &state); + if (result < 0) { + break; + } + sz += result; } return sz; } +static int32_t teavm_c16Size(char* cstring, size_t count) { + mbstate_t state = {0}; + int32_t sz = 0; + while (count > 0) { + size_t result = mbrtoc16(NULL, cstring, count, &state); + if (result == -1) { + break; + } else if (result >= 0) { + sz++; + count -= result; + cstring += result; + } + } + + return sz; +} + static char* teavm_stringToC(void* obj) { if (obj == NULL) { return NULL; @@ -98,15 +97,47 @@ static char* teavm_stringToC(void* obj) { int32_t j = 0; char* dst = result; + mbstate_t state = {0}; for (int32_t i = 0; i < charArray->size; ++i) { - dst += wctomb(dst, teavm_getCodePoint(javaChars, &i, charArray->size)); + dst += c16rtomb(dst, javaChars[i], &state); } *dst = '\0'; return result; } +static JavaString* teavm_cToString(char* cstring) { + if (cstring == NULL) { + return NULL; + } + + size_t clen = strlen(cstring); + int32_t size = teavm_c16Size(cstring, clen); + JavaArray* charArray = teavm_allocateCharArray(size); + char16_t* javaChars = ARRAY_DATA(charArray, char16_t); + mbstate_t state = {0}; + for (int32_t i = 0; i < size; ++i) { + int32_t result = mbrtoc16(javaChars++, cstring, clen, &state); + if (result == -1) { + break; + } else if (result >= 0) { + clen -= result; + cstring += result; + } + } + return teavm_createString(charArray); +} + static inline void teavm_free(void* s) { if (s != NULL) { free(s); } +} + +static JavaArray* teavm_parseArguments(int argc, char** argv) { + JavaArray* array = teavm_allocateStringArray(argc - 1); + JavaString** arrayData = ARRAY_DATA(array, JavaString*); + for (int i = 1; i < argc; ++i) { + arrayData[i - 1] = teavm_cToString(argv[i]); + } + return array; } \ No newline at end of file diff --git a/core/src/main/resources/org/teavm/backend/c/runtime.c b/core/src/main/resources/org/teavm/backend/c/runtime.c index c27ac941a..c95c03aba 100644 --- a/core/src/main/resources/org/teavm/backend/c/runtime.c +++ b/core/src/main/resources/org/teavm/backend/c/runtime.c @@ -141,6 +141,8 @@ typedef struct { static int32_t teavm_hashCode(JavaString*); static int32_t teavm_equals(JavaString*, JavaString*); static JavaArray* teavm_allocateStringArray(int32_t size); +static JavaArray* teavm_allocateCharArray(int32_t size); +static JavaString* teavm_createString(JavaArray* chars); static TeaVM_ResourceMapEntry* teavm_lookupResource(TeaVM_ResourceMap *map, JavaString* string) { uint32_t hashCode = teavm_hashCode(string); diff --git a/samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/Gtk3BenchmarkStarter.java b/samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/Gtk3BenchmarkStarter.java index 579af9bff..fdaf2b2c1 100644 --- a/samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/Gtk3BenchmarkStarter.java +++ b/samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/Gtk3BenchmarkStarter.java @@ -15,6 +15,7 @@ */ package org.teavm.samples.benchmark.teavm; +import java.util.Arrays; import org.jbox2d.collision.shapes.CircleShape; import org.jbox2d.collision.shapes.PolygonShape; import org.jbox2d.collision.shapes.Shape; @@ -42,6 +43,8 @@ public final class Gtk3BenchmarkStarter { } public static void main(String[] args) { + System.out.println(Arrays.asList(args)); + Gtk.init(null, null); Gtk.Window window = Gtk.windowNew(Gtk.WINDOW_TOPLEVEL); GLib.signalConnect(window, "delete-event", diff --git a/tests/src/test/java/org/teavm/vm/RttiTest.java b/tests/src/test/java/org/teavm/vm/RttiTest.java new file mode 100644 index 000000000..d020574af --- /dev/null +++ b/tests/src/test/java/org/teavm/vm/RttiTest.java @@ -0,0 +1,75 @@ +/* + * Copyright 2019 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.vm; + +import static org.junit.Assert.assertTrue; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.teavm.junit.TeaVMTestRunner; + +@RunWith(TeaVMTestRunner.class) +public class RttiTest { + @Test + public void instanceOfInterface() { + checkImplements(new A(), true, false, false); + checkImplements(new B(), false, true, true); + checkImplements(new C(), false, true, false); + checkImplements(new D(), false, true, true); + checkImplements(new E(), true, false, false); + checkImplements(new F(), false, true, true); + checkImplements(new G(), true, true, false); + } + + private void checkImplements(Object o, boolean i, boolean j, boolean k) { + assertTrue(predicate(o, i, "I"), !i ^ o instanceof I); + assertTrue(predicate(o, j, "J"), !j ^ o instanceof J); + assertTrue(predicate(o, k, "K"), !k ^ o instanceof K); + } + + private String predicate(Object o, boolean b, String name) { + return o.getClass().getName() + " should" + (b ? " not" : "") + " implement " + name; + } + + static class A implements I { + } + + static class B implements K { + } + + static class C implements J { + } + + static class D extends C implements K { + } + + static class E extends A implements I { + } + + static class F extends B implements J { + } + + static class G extends C implements I { + } + + interface I { + } + + interface J { + } + + interface K extends J { + } +}