mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
C: fix bugs in RTTI. Support args parameter of main method
This commit is contained in:
parent
56cb14e30c
commit
ec8bae1d40
|
@ -206,7 +206,11 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
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;
|
return (int) (h >>> 32) ^ (int) h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -87,7 +87,11 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
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);")
|
@JSBody(params = "v", script = "return isNaN(v);")
|
||||||
|
|
|
@ -38,6 +38,7 @@ import org.teavm.ast.decompilation.Decompiler;
|
||||||
import org.teavm.backend.c.analyze.CDependencyListener;
|
import org.teavm.backend.c.analyze.CDependencyListener;
|
||||||
import org.teavm.backend.c.generate.BufferedCodeWriter;
|
import org.teavm.backend.c.generate.BufferedCodeWriter;
|
||||||
import org.teavm.backend.c.generate.ClassGenerator;
|
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.CodeGeneratorUtil;
|
||||||
import org.teavm.backend.c.generate.CodeWriter;
|
import org.teavm.backend.c.generate.CodeWriter;
|
||||||
import org.teavm.backend.c.generate.GenerationContext;
|
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.RuntimeClassIntrinsic;
|
||||||
import org.teavm.backend.c.intrinsic.ShadowStackIntrinsic;
|
import org.teavm.backend.c.intrinsic.ShadowStackIntrinsic;
|
||||||
import org.teavm.backend.c.intrinsic.StructureIntrinsic;
|
import org.teavm.backend.c.intrinsic.StructureIntrinsic;
|
||||||
|
import org.teavm.backend.lowlevel.dependency.ExceptionHandlingDependencyListener;
|
||||||
import org.teavm.backend.lowlevel.transform.CoroutineTransformation;
|
import org.teavm.backend.lowlevel.transform.CoroutineTransformation;
|
||||||
import org.teavm.dependency.ClassDependency;
|
import org.teavm.dependency.ClassDependency;
|
||||||
import org.teavm.dependency.DependencyAnalyzer;
|
import org.teavm.dependency.DependencyAnalyzer;
|
||||||
|
@ -71,6 +73,7 @@ import org.teavm.interop.Address;
|
||||||
import org.teavm.interop.PlatformMarkers;
|
import org.teavm.interop.PlatformMarkers;
|
||||||
import org.teavm.interop.Structure;
|
import org.teavm.interop.Structure;
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
|
@ -116,6 +119,9 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
private static final Set<MethodReference> VIRTUAL_METHODS = new HashSet<>(Arrays.asList(
|
private static final Set<MethodReference> VIRTUAL_METHODS = new HashSet<>(Arrays.asList(
|
||||||
new MethodReference(Object.class, "clone", Object.class)
|
new MethodReference(Object.class, "clone", Object.class)
|
||||||
));
|
));
|
||||||
|
private static final MethodReference STRING_CONSTRUCTOR = new MethodReference(String.class,
|
||||||
|
"<init>", char[].class, void.class);
|
||||||
|
|
||||||
private TeaVMTargetController controller;
|
private TeaVMTargetController controller;
|
||||||
private ClassInitializerEliminator classInitializerEliminator;
|
private ClassInitializerEliminator classInitializerEliminator;
|
||||||
private ClassInitializerTransformer classInitializerTransformer;
|
private ClassInitializerTransformer classInitializerTransformer;
|
||||||
|
@ -196,6 +202,10 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
dependencyAnalyzer.linkClass("java.lang.String");
|
dependencyAnalyzer.linkClass("java.lang.String");
|
||||||
dependencyAnalyzer.linkClass("java.lang.Class");
|
dependencyAnalyzer.linkClass("java.lang.Class");
|
||||||
dependencyAnalyzer.linkField(new FieldReference("java.lang.String", "hashCode"));
|
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 runtimeClassDep = dependencyAnalyzer.linkClass(RuntimeClass.class.getName());
|
||||||
ClassDependency runtimeObjectDep = dependencyAnalyzer.linkClass(RuntimeObject.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, "isResuming", boolean.class)).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(Fiber.class, "isSuspending", 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, "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(EventQueue.class, "process", void.class)).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(Thread.class, "setCurrentThread", Thread.class,
|
dependencyAnalyzer.linkMethod(new MethodReference(Thread.class, "setCurrentThread", Thread.class,
|
||||||
void.class)).use();
|
void.class)).use();
|
||||||
|
@ -220,6 +230,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
dependencyAnalyzer.linkMethod(method.getReference()).use();
|
dependencyAnalyzer.linkMethod(method.getReference()).use();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dependencyAnalyzer.addDependencyListener(new ExceptionHandlingDependencyListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -249,7 +261,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
@Override
|
@Override
|
||||||
public void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName) throws IOException {
|
public void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName) throws IOException {
|
||||||
VirtualTableProvider vtableProvider = createVirtualTableProvider(classes);
|
VirtualTableProvider vtableProvider = createVirtualTableProvider(classes);
|
||||||
TagRegistry tagRegistry = new TagRegistry(classes);
|
ClassHierarchy hierarchy = new ClassHierarchy(classes);
|
||||||
|
TagRegistry tagRegistry = new TagRegistry(classes, hierarchy);
|
||||||
StringPool stringPool = new StringPool();
|
StringPool stringPool = new StringPool();
|
||||||
|
|
||||||
Decompiler decompiler = new Decompiler(classes, new HashSet<>(), false, true);
|
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) {
|
private void generateSpecialFunctions(GenerationContext context, CodeWriter writer) {
|
||||||
generateThrowCCE(context, writer);
|
generateThrowCCE(context, writer);
|
||||||
generateAllocateStringArray(context, writer);
|
generateAllocateStringArray(context, writer);
|
||||||
|
generateAllocateCharArray(context, writer);
|
||||||
|
generateCreateString(context, writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateThrowCCE(GenerationContext context, CodeWriter writer) {
|
private void generateThrowCCE(GenerationContext context, CodeWriter writer) {
|
||||||
|
@ -425,6 +440,25 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
writer.outdent().println("}");
|
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,
|
private void generateArrayOfClassReferences(GenerationContext context, CodeWriter writer,
|
||||||
List<? extends ValueType> types) {
|
List<? extends ValueType> types) {
|
||||||
writer.print("static JavaClass* teavm_classReferences[" + types.size() + "] = {").indent();
|
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) {
|
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));
|
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 + "();");
|
writer.println(processName + "();");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -537,7 +572,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
public void apply(IntrinsicContext context, InvocationExpr invocation) {
|
public void apply(IntrinsicContext context, InvocationExpr invocation) {
|
||||||
switch (invocation.getMethod().getName()) {
|
switch (invocation.getMethod().getName()) {
|
||||||
case "runMain":
|
case "runMain":
|
||||||
generateCallToMainMethod(context.names(), context.writer());
|
generateCallToMainMethod(context, invocation);
|
||||||
break;
|
break;
|
||||||
case "setCurrentThread":
|
case "setCurrentThread":
|
||||||
String methodName = context.names().forMethod(new MethodReference(Thread.class,
|
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");
|
TeaVMEntryPoint entryPoint = controller.getEntryPoints().get("main");
|
||||||
if (entryPoint != null) {
|
if (entryPoint != null) {
|
||||||
String mainMethod = names.forMethod(entryPoint.getMethod());
|
String mainMethod = names.forMethod(entryPoint.getMethod());
|
||||||
writer.println(mainMethod + "(NULL);");
|
context.writer().print(mainMethod + "(");
|
||||||
|
context.emit(invocation.getArguments().get(0));
|
||||||
|
context.writer().println(");");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -118,6 +118,7 @@ public class ClassGenerator {
|
||||||
|
|
||||||
public void generateClass(ClassHolder cls) {
|
public void generateClass(ClassHolder cls) {
|
||||||
generateClassStructure(cls);
|
generateClassStructure(cls);
|
||||||
|
generateClassStaticFields(cls);
|
||||||
generateClassMethods(cls);
|
generateClassMethods(cls);
|
||||||
generateInitializer(cls);
|
generateInitializer(cls);
|
||||||
}
|
}
|
||||||
|
@ -230,7 +231,6 @@ public class ClassGenerator {
|
||||||
String name = context.getNames().forClass(cls.getName());
|
String name = context.getNames().forClass(cls.getName());
|
||||||
|
|
||||||
CodeWriter structWriter = structuresWriter.fragment();
|
CodeWriter structWriter = structuresWriter.fragment();
|
||||||
CodeWriter fieldsWriter = structuresWriter.fragment();
|
|
||||||
|
|
||||||
structWriter.print("typedef struct ").print(name).println(" {").indent();
|
structWriter.print("typedef struct ").print(name).println(" {").indent();
|
||||||
|
|
||||||
|
@ -244,38 +244,19 @@ public class ClassGenerator {
|
||||||
|
|
||||||
int layoutIndex = currentLayoutIndex;
|
int layoutIndex = currentLayoutIndex;
|
||||||
|
|
||||||
FieldReference[] staticFields = new FieldReference[cls.getFields().size()];
|
|
||||||
int staticIndex = 0;
|
|
||||||
FieldReference[] instanceFields = new FieldReference[cls.getFields().size()];
|
FieldReference[] instanceFields = new FieldReference[cls.getFields().size()];
|
||||||
int instanceIndex = 0;
|
int instanceIndex = 0;
|
||||||
for (FieldHolder field : cls.getFields()) {
|
for (FieldHolder field : cls.getFields()) {
|
||||||
if (field.hasModifier(ElementModifier.STATIC)) {
|
if (field.hasModifier(ElementModifier.STATIC)) {
|
||||||
String fieldName = context.getNames().forStaticField(field.getReference());
|
continue;
|
||||||
fieldsWriter.print("static ").printStrictType(field.getType()).print(" ").print(fieldName)
|
}
|
||||||
.println(";");
|
String fieldName = context.getNames().forMemberField(field.getReference());
|
||||||
if (isReferenceType(field.getType())) {
|
structWriter.printStrictType(field.getType()).print(" ").print(fieldName).println(";");
|
||||||
staticFields[staticIndex++] = field.getReference();
|
if (isReferenceType(field.getType())) {
|
||||||
}
|
instanceFields[instanceIndex++] = 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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (staticIndex > 0) {
|
|
||||||
staticGcRoots.add(Arrays.copyOf(staticFields, staticIndex));
|
|
||||||
}
|
|
||||||
if (instanceIndex > 0) {
|
if (instanceIndex > 0) {
|
||||||
classLayoutOffsets.put(cls.getName(), layoutIndex);
|
classLayoutOffsets.put(cls.getName(), layoutIndex);
|
||||||
layouts.add(Arrays.copyOf(instanceFields, instanceIndex));
|
layouts.add(Arrays.copyOf(instanceFields, instanceIndex));
|
||||||
|
@ -285,6 +266,36 @@ public class ClassGenerator {
|
||||||
structWriter.outdent().print("} ").print(name).println(";");
|
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) {
|
private static Object getDefaultValue(ValueType type) {
|
||||||
if (type instanceof ValueType.Primitive) {
|
if (type instanceof ValueType.Primitive) {
|
||||||
ValueType.Primitive primitive = (ValueType.Primitive) type;
|
ValueType.Primitive primitive = (ValueType.Primitive) type;
|
||||||
|
|
|
@ -78,7 +78,7 @@ import org.teavm.runtime.RuntimeClass;
|
||||||
import org.teavm.runtime.RuntimeObject;
|
import org.teavm.runtime.RuntimeObject;
|
||||||
|
|
||||||
public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
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);
|
"allocate", RuntimeClass.class, Address.class);
|
||||||
private static final MethodReference ALLOC_ARRAY_METHOD = new MethodReference(Allocator.class,
|
private static final MethodReference ALLOC_ARRAY_METHOD = new MethodReference(Allocator.class,
|
||||||
"allocateArray", RuntimeClass.class, int.class, Address.class);
|
"allocateArray", RuntimeClass.class, int.class, Address.class);
|
||||||
|
|
|
@ -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,
|
||||||
|
"<init>", 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -104,6 +104,7 @@ import org.teavm.interop.StaticInit;
|
||||||
import org.teavm.model.AnnotationHolder;
|
import org.teavm.model.AnnotationHolder;
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
import org.teavm.model.CallLocation;
|
import org.teavm.model.CallLocation;
|
||||||
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
|
@ -314,7 +315,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||||
WasmFunction initFunction = new WasmFunction("__start__");
|
WasmFunction initFunction = new WasmFunction("__start__");
|
||||||
|
|
||||||
VirtualTableProvider vtableProvider = createVirtualTableProvider(classes);
|
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);
|
BinaryWriter binaryWriter = new BinaryWriter(256);
|
||||||
NameProvider names = new NameProvider(controller.getUnprocessedClassSource());
|
NameProvider names = new NameProvider(controller.getUnprocessedClassSource());
|
||||||
WasmClassGenerator classGenerator = new WasmClassGenerator(classes, controller.getUnprocessedClassSource(),
|
WasmClassGenerator classGenerator = new WasmClassGenerator(classes, controller.getUnprocessedClassSource(),
|
||||||
|
|
|
@ -26,15 +26,15 @@ import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ClassReaderSource;
|
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.ListableClassReaderSource;
|
import org.teavm.model.ListableClassReaderSource;
|
||||||
|
|
||||||
public class TagRegistry {
|
public class TagRegistry {
|
||||||
private Map<String, List<Range>> ranges = new HashMap<>();
|
private Map<String, List<Range>> ranges = new HashMap<>();
|
||||||
|
|
||||||
public TagRegistry(ListableClassReaderSource classSource) {
|
public TagRegistry(ListableClassReaderSource classSource, ClassHierarchy classHierarchy) {
|
||||||
List<String> roots = new ArrayList<>();
|
List<String> roots = new ArrayList<>();
|
||||||
Map<String, Set<String>> implementedBy = new HashMap<>();
|
Map<String, Set<String>> implementedBy = new HashMap<>();
|
||||||
|
|
||||||
|
@ -45,8 +45,7 @@ public class TagRegistry {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (String iface : cls.getInterfaces()) {
|
for (String iface : cls.getInterfaces()) {
|
||||||
String topmostImplementor = findTopmostImplementor(classSource, className, iface);
|
markImplementor(classHierarchy, className, iface, implementedBy);
|
||||||
markImplementor(classSource, topmostImplementor, iface, implementedBy);
|
|
||||||
}
|
}
|
||||||
if (cls.getParent() == null || cls.getParent().equals(cls.getName())) {
|
if (cls.getParent() == null || cls.getParent().equals(cls.getName())) {
|
||||||
roots.add(className);
|
roots.add(className);
|
||||||
|
@ -58,8 +57,7 @@ public class TagRegistry {
|
||||||
Map<String, Range> simpleRanges = new HashMap<>();
|
Map<String, Range> simpleRanges = new HashMap<>();
|
||||||
int current = 0;
|
int current = 0;
|
||||||
for (String root : roots) {
|
for (String root : roots) {
|
||||||
assignRange(current, hierarchy, root, simpleRanges);
|
current = 1 + assignRange(current, hierarchy, root, simpleRanges);
|
||||||
++current;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String className : classSource.getClassNames()) {
|
for (String className : classSource.getClassNames()) {
|
||||||
|
@ -71,7 +69,7 @@ public class TagRegistry {
|
||||||
Set<String> implementorRoots = implementedBy.get(cls.getName());
|
Set<String> implementorRoots = implementedBy.get(cls.getName());
|
||||||
if (implementorRoots != null) {
|
if (implementorRoots != null) {
|
||||||
List<Range> ifaceRanges = implementorRoots.stream()
|
List<Range> ifaceRanges = implementorRoots.stream()
|
||||||
.map(implementor -> simpleRanges.get(implementor))
|
.map(simpleRanges::get)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.sorted(Comparator.comparing(range -> range.lower))
|
.sorted(Comparator.comparing(range -> range.lower))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
@ -86,37 +84,40 @@ public class TagRegistry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String findTopmostImplementor(ClassReaderSource classSource, String className, String ifaceName) {
|
private String findTopmostImplementor(ClassHierarchy hierarchy, String className, String ifaceName) {
|
||||||
ClassReader cls = classSource.get(className);
|
ClassReader cls = hierarchy.getClassSource().get(className);
|
||||||
if (cls == null) {
|
if (cls == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cls.getParent() != null) {
|
if (cls.getParent() != null) {
|
||||||
String candidate = findTopmostImplementor(classSource, cls.getParent(), ifaceName);
|
String candidate = findTopmostImplementor(hierarchy, cls.getParent(), ifaceName);
|
||||||
if (candidate != null) {
|
if (candidate != null) {
|
||||||
return candidate;
|
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<String, Set<String>> implementedBy) {
|
Map<String, Set<String>> implementedBy) {
|
||||||
|
className = findTopmostImplementor(hierarchy, className, ifaceName);
|
||||||
|
|
||||||
if (!implementedBy.computeIfAbsent(ifaceName, key -> new LinkedHashSet<>()).add(className)) {
|
if (!implementedBy.computeIfAbsent(ifaceName, key -> new LinkedHashSet<>()).add(className)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassReader iface = classSource.get(ifaceName);
|
ClassReader iface = hierarchy.getClassSource().get(ifaceName);
|
||||||
if (iface == null) {
|
if (iface == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String superIface : iface.getInterfaces()) {
|
for (String superIface : iface.getInterfaces()) {
|
||||||
markImplementor(classSource, className, superIface, implementedBy);
|
markImplementor(hierarchy, className, superIface, implementedBy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -115,9 +115,11 @@ public class RegisterAllocator {
|
||||||
case FLOAT:
|
case FLOAT:
|
||||||
return 2;
|
return 2;
|
||||||
case DOUBLE:
|
case DOUBLE:
|
||||||
return 2;
|
|
||||||
default:
|
|
||||||
return 3;
|
return 3;
|
||||||
|
case OBJECT:
|
||||||
|
return 4;
|
||||||
|
default:
|
||||||
|
return 5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -213,11 +213,11 @@ public class Fiber {
|
||||||
new Fiber(runner, daemon).start();
|
new Fiber(runner, daemon).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void startMain() {
|
static void startMain(String[] args) {
|
||||||
start(Fiber::runMain, false);
|
start(() -> runMain(args), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static native void runMain();
|
static native void runMain(String[] args);
|
||||||
|
|
||||||
private void start() {
|
private void start() {
|
||||||
Fiber former = current;
|
Fiber former = current;
|
||||||
|
|
|
@ -52,38 +52,37 @@ static JavaArray* teavm_resourceMapKeys(TeaVM_ResourceMap *map) {
|
||||||
return array;
|
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) {
|
static size_t teavm_mbSize(char16_t* javaChars, int32_t javaCharsCount) {
|
||||||
size_t sz = 0;
|
size_t sz = 0;
|
||||||
char buffer[6];
|
char buffer[__STDC_UTF_16__];
|
||||||
|
mbstate_t state = {0};
|
||||||
for (int32_t i = 0; i < javaCharsCount; ++i) {
|
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;
|
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) {
|
static char* teavm_stringToC(void* obj) {
|
||||||
if (obj == NULL) {
|
if (obj == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -98,15 +97,47 @@ static char* teavm_stringToC(void* obj) {
|
||||||
|
|
||||||
int32_t j = 0;
|
int32_t j = 0;
|
||||||
char* dst = result;
|
char* dst = result;
|
||||||
|
mbstate_t state = {0};
|
||||||
for (int32_t i = 0; i < charArray->size; ++i) {
|
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';
|
*dst = '\0';
|
||||||
return result;
|
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) {
|
static inline void teavm_free(void* s) {
|
||||||
if (s != NULL) {
|
if (s != NULL) {
|
||||||
free(s);
|
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;
|
||||||
|
}
|
|
@ -141,6 +141,8 @@ typedef struct {
|
||||||
static int32_t teavm_hashCode(JavaString*);
|
static int32_t teavm_hashCode(JavaString*);
|
||||||
static int32_t teavm_equals(JavaString*, JavaString*);
|
static int32_t teavm_equals(JavaString*, JavaString*);
|
||||||
static JavaArray* teavm_allocateStringArray(int32_t size);
|
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) {
|
static TeaVM_ResourceMapEntry* teavm_lookupResource(TeaVM_ResourceMap *map, JavaString* string) {
|
||||||
uint32_t hashCode = teavm_hashCode(string);
|
uint32_t hashCode = teavm_hashCode(string);
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.samples.benchmark.teavm;
|
package org.teavm.samples.benchmark.teavm;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import org.jbox2d.collision.shapes.CircleShape;
|
import org.jbox2d.collision.shapes.CircleShape;
|
||||||
import org.jbox2d.collision.shapes.PolygonShape;
|
import org.jbox2d.collision.shapes.PolygonShape;
|
||||||
import org.jbox2d.collision.shapes.Shape;
|
import org.jbox2d.collision.shapes.Shape;
|
||||||
|
@ -42,6 +43,8 @@ public final class Gtk3BenchmarkStarter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
System.out.println(Arrays.asList(args));
|
||||||
|
|
||||||
Gtk.init(null, null);
|
Gtk.init(null, null);
|
||||||
Gtk.Window window = Gtk.windowNew(Gtk.WINDOW_TOPLEVEL);
|
Gtk.Window window = Gtk.windowNew(Gtk.WINDOW_TOPLEVEL);
|
||||||
GLib.signalConnect(window, "delete-event",
|
GLib.signalConnect(window, "delete-event",
|
||||||
|
|
75
tests/src/test/java/org/teavm/vm/RttiTest.java
Normal file
75
tests/src/test/java/org/teavm/vm/RttiTest.java
Normal file
|
@ -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 {
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user