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 d1c292261..69d18a0de 100644 --- a/core/src/main/java/org/teavm/backend/c/CTarget.java +++ b/core/src/main/java/org/teavm/backend/c/CTarget.java @@ -42,6 +42,8 @@ import org.teavm.backend.c.generate.GenerationContext; import org.teavm.backend.c.generate.NameProvider; import org.teavm.backend.c.generate.StringPool; import org.teavm.backend.c.generate.StringPoolGenerator; +import org.teavm.backend.c.generators.ArrayGenerator; +import org.teavm.backend.c.generators.Generator; import org.teavm.backend.c.intrinsic.AddressIntrinsic; import org.teavm.backend.c.intrinsic.AllocatorIntrinsic; import org.teavm.backend.c.intrinsic.ExceptionHandlingIntrinsic; @@ -206,8 +208,11 @@ public class CTarget implements TeaVMTarget { intrinsics.add(new ExceptionHandlingIntrinsic()); intrinsics.add(new FunctionIntrinsic(characteristics)); + List generators = new ArrayList<>(); + generators.add(new ArrayGenerator()); + GenerationContext context = new GenerationContext(vtableProvider, characteristics, stringPool, nameProvider, - controller.getDiagnostics(), classes, intrinsics); + controller.getDiagnostics(), classes, intrinsics, generators); try (PrintWriter writer = new PrintWriter(new OutputStreamWriter( buildTarget.createResource(outputName), "UTF-8"))) { 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 0ef1a76d8..d8c5e0dd2 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 @@ -25,12 +25,16 @@ import java.util.Set; import org.teavm.ast.RegularMethodNode; import org.teavm.ast.decompilation.Decompiler; import org.teavm.backend.c.analyze.Characteristics; +import org.teavm.backend.c.generators.Generator; +import org.teavm.backend.c.generators.GeneratorContext; +import org.teavm.diagnostics.Diagnostics; import org.teavm.interop.Address; import org.teavm.interop.DelegateTo; import org.teavm.interop.Structure; import org.teavm.model.AnnotationHolder; import org.teavm.model.ClassHolder; import org.teavm.model.ClassReader; +import org.teavm.model.ClassReaderSource; import org.teavm.model.ElementModifier; import org.teavm.model.FieldHolder; import org.teavm.model.FieldReader; @@ -70,7 +74,8 @@ public class ClassGenerator { continue; } if (method.hasModifier(ElementModifier.NATIVE) - && method.getAnnotations().get(DelegateTo.class.getName()) == null) { + && method.getAnnotations().get(DelegateTo.class.getName()) == null + && context.getGenerator(method.getReference()) == null) { continue; } @@ -388,6 +393,11 @@ public class ClassGenerator { tag = 0; sizeExpr = "sizeof(" + CodeWriter.strictTypeAsString(type) + ")"; flags |= RuntimeClass.PRIMITIVE; + if (type instanceof ValueType.Primitive) { + flags |= getPrimitiveFlag((ValueType.Primitive) type) << RuntimeClass.PRIMITIVE_SHIFT; + } else { + flags |= RuntimeClass.VOID_PRIMITIVE << RuntimeClass.PRIMITIVE_SHIFT; + } itemTypeExpr = "NULL"; } @@ -416,6 +426,29 @@ public class ClassGenerator { writer.print(".").print(classFieldName("layout")).println(" = " + layout); } + private int getPrimitiveFlag(ValueType.Primitive type) { + switch (type.getKind()) { + case BOOLEAN: + return RuntimeClass.BOOLEAN_PRIMITIVE; + case BYTE: + return RuntimeClass.BYTE_PRIMITIVE; + case SHORT: + return RuntimeClass.SHORT_PRIMITIVE; + case CHARACTER: + return RuntimeClass.CHAR_PRIMITIVE; + case INTEGER: + return RuntimeClass.INT_PRIMITIVE; + case LONG: + return RuntimeClass.LONG_PRIMITIVE; + case FLOAT: + return RuntimeClass.FLOAT_PRIMITIVE; + case DOUBLE: + return RuntimeClass.DOUBLE_PRIMITIVE; + default: + throw new AssertionError(); + } + } + private String classFieldName(String field) { return context.getNames().forMemberField(new FieldReference(RuntimeClass.class.getName(), field)); } @@ -456,7 +489,9 @@ public class ClassGenerator { public void generateClass(ClassHolder cls) { for (MethodHolder method : cls.getMethods()) { if (method.hasModifier(ElementModifier.NATIVE)) { - tryDelegateToMethod(cls, method); + if (!tryDelegateToMethod(cls, method)) { + tryUsingGenerator(method); + } continue; } @@ -491,19 +526,21 @@ public class ClassGenerator { && cls.getMethod(new MethodDescriptor("", ValueType.VOID)) != null; } - private void tryDelegateToMethod(ClassHolder cls, MethodHolder method) { + private boolean tryDelegateToMethod(ClassHolder cls, MethodHolder method) { AnnotationHolder delegateToAnnot = method.getAnnotations().get(DelegateTo.class.getName()); if (delegateToAnnot == null) { - return; + return false; } String methodName = delegateToAnnot.getValue("value").getString(); for (MethodHolder candidate : cls.getMethods()) { if (candidate.getName().equals(methodName)) { delegateToMethod(method, candidate); - break; + return true; } } + + return false; } private void delegateToMethod(MethodHolder callingMethod, MethodHolder delegateMethod) { @@ -537,6 +574,42 @@ public class ClassGenerator { writer.outdent().println("}"); } + private void tryUsingGenerator(MethodHolder method) { + MethodReference methodRef = method.getReference(); + Generator generator = context.getGenerator(methodRef); + if (generator == null) { + return; + } + + boolean isStatic = method.hasModifier(ElementModifier.STATIC); + codeGenerator.generateMethodSignature(methodRef, isStatic, true); + writer.println(" {").indent(); + + generator.generate(new GeneratorContext() { + @Override + public NameProvider names() { + return context.getNames(); + } + + @Override + public Diagnostics getDiagnotics() { + return context.getDiagnostics(); + } + + @Override + public ClassReaderSource getClassSource() { + return context.getClassSource(); + } + + @Override + public String getParameterName(int index) { + return index == 0 ? "_this_" : "local_" + index; + } + }, writer, methodRef); + + writer.outdent().println("}"); + } + public static String nameOfType(ValueType type) { if (type instanceof ValueType.Primitive) { switch (((ValueType.Primitive) type).getKind()) { diff --git a/core/src/main/java/org/teavm/backend/c/generate/GenerationContext.java b/core/src/main/java/org/teavm/backend/c/generate/GenerationContext.java index b0779b6e6..8449ad938 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/GenerationContext.java +++ b/core/src/main/java/org/teavm/backend/c/generate/GenerationContext.java @@ -20,6 +20,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import org.teavm.backend.c.analyze.Characteristics; +import org.teavm.backend.c.generators.Generator; import org.teavm.backend.c.intrinsic.Intrinsic; import org.teavm.diagnostics.Diagnostics; import org.teavm.model.ClassReaderSource; @@ -34,11 +35,12 @@ public class GenerationContext { private Diagnostics diagnostics; private ClassReaderSource classSource; private List intrinsics; + private List generators; private Map intrinsicCache = new HashMap<>(); public GenerationContext(VirtualTableProvider virtualTableProvider, Characteristics characteristics, StringPool stringPool, NameProvider names, Diagnostics diagnostics, ClassReaderSource classSource, - List intrinsics) { + List intrinsics, List generators) { this.virtualTableProvider = virtualTableProvider; this.characteristics = characteristics; this.stringPool = stringPool; @@ -46,6 +48,7 @@ public class GenerationContext { this.diagnostics = diagnostics; this.classSource = classSource; this.intrinsics = new ArrayList<>(intrinsics); + this.generators = new ArrayList<>(generators); } public VirtualTableProvider getVirtualTableProvider() { @@ -76,4 +79,13 @@ public class GenerationContext { return intrinsicCache.computeIfAbsent(method, m -> intrinsics.stream().filter(i -> i.canHandle(m)).findFirst().orElse(null)); } + + public Generator getGenerator(MethodReference method) { + for (Generator generator : generators) { + if (generator.canHandle(method)) { + return generator; + } + } + return null; + } } diff --git a/core/src/main/java/org/teavm/backend/c/generators/ArrayGenerator.java b/core/src/main/java/org/teavm/backend/c/generators/ArrayGenerator.java new file mode 100644 index 000000000..0b8986109 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/c/generators/ArrayGenerator.java @@ -0,0 +1,91 @@ +/* + * Copyright 2018 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.backend.c.generators; + +import java.lang.reflect.Array; +import org.teavm.backend.c.generate.CodeWriter; +import org.teavm.model.ClassReader; +import org.teavm.model.FieldReference; +import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; +import org.teavm.runtime.RuntimeClass; + +public class ArrayGenerator implements Generator { + private static final int[] primitives = { + RuntimeClass.BYTE_PRIMITIVE, + RuntimeClass.SHORT_PRIMITIVE, + RuntimeClass.CHAR_PRIMITIVE, + RuntimeClass.INT_PRIMITIVE, + RuntimeClass.LONG_PRIMITIVE, + RuntimeClass.FLOAT_PRIMITIVE, + RuntimeClass.DOUBLE_PRIMITIVE, + RuntimeClass.BOOLEAN_PRIMITIVE + }; + private static final String[] primitiveWrappers = { "Byte", "Short", "Character", "Integer", "Long", + "Float", "Double", "Boolean" }; + private static final ValueType[] primitiveTypes = { ValueType.BYTE, ValueType.SHORT, ValueType.CHARACTER, + ValueType.INTEGER, ValueType.LONG, ValueType.FLOAT, ValueType.DOUBLE, ValueType.BOOLEAN }; + + @Override + public boolean canHandle(MethodReference method) { + if (!method.getClassName().equals(Array.class.getName())) { + return false; + } + + switch (method.getName()) { + case "getImpl": + return true; + default: + return false; + } + } + + @Override + public void generate(GeneratorContext context, CodeWriter writer, MethodReference method) { + String array = context.getParameterName(1); + String index = context.getParameterName(2); + String componentTypeField = context.names().forMemberField( + new FieldReference(RuntimeClass.class.getName(), "itemType")); + String flagsField = context.names().forMemberField( + new FieldReference(RuntimeClass.class.getName(), "flags")); + + writer.println("JavaClass* componentType = (JavaClass*) CLASS_OF(" + array + ")" + + "->" + componentTypeField + ";"); + writer.println("int32_t flags = componentType->" + flagsField + ";"); + writer.println("if (flags & " + RuntimeClass.PRIMITIVE + ") {").indent(); + + writer.println("switch ((flags >> " + RuntimeClass.PRIMITIVE_SHIFT + ") & " + + RuntimeClass.PRIMITIVE_MASK + ") {").indent(); + for (int i = 0; i < primitiveWrappers.length; ++i) { + String wrapper = "java.lang." + primitiveWrappers[i]; + MethodReference methodRef = new MethodReference(wrapper, "valueOf", + primitiveTypes[i], ValueType.object(wrapper)); + ClassReader cls = context.getClassSource().get(methodRef.getClassName()); + if (cls == null || cls.getMethod(methodRef.getDescriptor()) == null) { + continue; + } + + String type = CodeWriter.strictTypeAsString(primitiveTypes[i]); + writer.println("case " + primitives[i] + ":").indent(); + writer.println("return " + context.names().forMethod(methodRef) + "(ARRAY_AT(" + array + ", " + + type + ", " + index + "));"); + writer.outdent(); + } + writer.outdent().println("}").outdent().println("}"); + + writer.println("return ARRAY_AT(" + array + ", void*, " + index + ");"); + } +} diff --git a/core/src/main/java/org/teavm/backend/c/generators/Generator.java b/core/src/main/java/org/teavm/backend/c/generators/Generator.java new file mode 100644 index 000000000..ccb69d5e3 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/c/generators/Generator.java @@ -0,0 +1,25 @@ +/* + * Copyright 2018 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.backend.c.generators; + +import org.teavm.backend.c.generate.CodeWriter; +import org.teavm.model.MethodReference; + +public interface Generator { + boolean canHandle(MethodReference method); + + void generate(GeneratorContext context, CodeWriter writer, MethodReference method); +} diff --git a/core/src/main/java/org/teavm/backend/c/generators/GeneratorContext.java b/core/src/main/java/org/teavm/backend/c/generators/GeneratorContext.java new file mode 100644 index 000000000..2f4853750 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/c/generators/GeneratorContext.java @@ -0,0 +1,30 @@ +/* + * Copyright 2018 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.backend.c.generators; + +import org.teavm.backend.c.generate.NameProvider; +import org.teavm.diagnostics.Diagnostics; +import org.teavm.model.ClassReaderSource; + +public interface GeneratorContext { + NameProvider names(); + + Diagnostics getDiagnotics(); + + ClassReaderSource getClassSource(); + + String getParameterName(int index); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/Example.java b/core/src/main/java/org/teavm/backend/wasm/Example.java index 8cee27c0e..4c7caeaa8 100644 --- a/core/src/main/java/org/teavm/backend/wasm/Example.java +++ b/core/src/main/java/org/teavm/backend/wasm/Example.java @@ -15,6 +15,7 @@ */ package org.teavm.backend.wasm; +import java.lang.reflect.Array; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; @@ -38,6 +39,7 @@ public final class Example { testArrayIsObject(); testIsAssignableFrom(); testExceptions(); + testArrayReflection(); testBigInteger(); testGC(); } @@ -214,6 +216,23 @@ public final class Example { } } + private static void testArrayReflection() { + Object[] arrays = new Object[] { + new int[] { 23, 42 }, + new byte[] { (byte) 1, (byte) 2, (byte) 3 }, + new String[] { "foo", "bar" }, + }; + + for (Object array : arrays) { + int sz = Array.getLength(array); + System.out.println("Array type: " + array.getClass().getName() + ", length " + sz); + for (int i = 0; i < sz; ++i) { + Object item = Array.get(array, i); + System.out.println(" [" + i + "] = " + item + ": " + item.getClass().getName()); + } + } + } + private static void doesNotThrow() { } diff --git a/core/src/main/java/org/teavm/runtime/RuntimeClass.java b/core/src/main/java/org/teavm/runtime/RuntimeClass.java index 5bab39fd2..f804eca84 100644 --- a/core/src/main/java/org/teavm/runtime/RuntimeClass.java +++ b/core/src/main/java/org/teavm/runtime/RuntimeClass.java @@ -23,6 +23,18 @@ public class RuntimeClass extends RuntimeObject { public static final int PRIMITIVE = 2; public static final int ENUM = 4; + public static final int PRIMITIVE_SHIFT = 3; + public static final int PRIMITIVE_MASK = 15; + public static final int BOOLEAN_PRIMITIVE = 0; + public static final int BYTE_PRIMITIVE = 1; + public static final int SHORT_PRIMITIVE = 2; + public static final int CHAR_PRIMITIVE = 3; + public static final int INT_PRIMITIVE = 4; + public static final int LONG_PRIMITIVE = 5; + public static final int FLOAT_PRIMITIVE = 6; + public static final int DOUBLE_PRIMITIVE = 7; + public static final int VOID_PRIMITIVE = 8; + public int size; public int flags; public int tag;