C: fix bugs in RTTI. Support args parameter of main method

This commit is contained in:
Alexey Andreev 2019-04-04 17:20:28 +03:00
parent 56cb14e30c
commit ec8bae1d40
14 changed files with 304 additions and 82 deletions

View File

@ -206,7 +206,11 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
@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;
}

View File

@ -87,7 +87,11 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
@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);")

View File

@ -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<MethodReference> VIRTUAL_METHODS = new HashSet<>(Arrays.asList(
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 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<? extends ValueType> 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(");");
}
}

View File

@ -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,12 +244,37 @@ 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)) {
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 (instanceIndex > 0) {
classLayoutOffsets.put(cls.getName(), layoutIndex);
layouts.add(Arrays.copyOf(instanceFields, instanceIndex));
currentLayoutIndex += instanceIndex + 1;
}
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(";");
@ -264,25 +289,11 @@ public class ClassGenerator {
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) {
classLayoutOffsets.put(cls.getName(), layoutIndex);
layouts.add(Arrays.copyOf(instanceFields, instanceIndex));
currentLayoutIndex += instanceIndex + 1;
}
structWriter.outdent().print("} ").print(name).println(";");
}
private static Object getDefaultValue(ValueType type) {

View File

@ -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);

View File

@ -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);
}
}
}

View File

@ -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(),

View File

@ -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<String, List<Range>> ranges = new HashMap<>();
public TagRegistry(ListableClassReaderSource classSource) {
public TagRegistry(ListableClassReaderSource classSource, ClassHierarchy classHierarchy) {
List<String> roots = new ArrayList<>();
Map<String, Set<String>> 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<String, Range> 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<String> implementorRoots = implementedBy.get(cls.getName());
if (implementorRoots != null) {
List<Range> 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<String, Set<String>> 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);
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -52,35 +52,34 @@ 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;
}
@ -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;
}

View File

@ -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);

View File

@ -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",

View 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 {
}
}