C backend: add support for Array.get

This commit is contained in:
Alexey Andreev 2018-03-21 22:44:22 +03:00
parent f079b5fbcc
commit dc227e1e42
8 changed files with 274 additions and 7 deletions

View File

@ -42,6 +42,8 @@ import org.teavm.backend.c.generate.GenerationContext;
import org.teavm.backend.c.generate.NameProvider; import org.teavm.backend.c.generate.NameProvider;
import org.teavm.backend.c.generate.StringPool; import org.teavm.backend.c.generate.StringPool;
import org.teavm.backend.c.generate.StringPoolGenerator; 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.AddressIntrinsic;
import org.teavm.backend.c.intrinsic.AllocatorIntrinsic; import org.teavm.backend.c.intrinsic.AllocatorIntrinsic;
import org.teavm.backend.c.intrinsic.ExceptionHandlingIntrinsic; import org.teavm.backend.c.intrinsic.ExceptionHandlingIntrinsic;
@ -206,8 +208,11 @@ public class CTarget implements TeaVMTarget {
intrinsics.add(new ExceptionHandlingIntrinsic()); intrinsics.add(new ExceptionHandlingIntrinsic());
intrinsics.add(new FunctionIntrinsic(characteristics)); intrinsics.add(new FunctionIntrinsic(characteristics));
List<Generator> generators = new ArrayList<>();
generators.add(new ArrayGenerator());
GenerationContext context = new GenerationContext(vtableProvider, characteristics, stringPool, nameProvider, GenerationContext context = new GenerationContext(vtableProvider, characteristics, stringPool, nameProvider,
controller.getDiagnostics(), classes, intrinsics); controller.getDiagnostics(), classes, intrinsics, generators);
try (PrintWriter writer = new PrintWriter(new OutputStreamWriter( try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(
buildTarget.createResource(outputName), "UTF-8"))) { buildTarget.createResource(outputName), "UTF-8"))) {

View File

@ -25,12 +25,16 @@ import java.util.Set;
import org.teavm.ast.RegularMethodNode; import org.teavm.ast.RegularMethodNode;
import org.teavm.ast.decompilation.Decompiler; import org.teavm.ast.decompilation.Decompiler;
import org.teavm.backend.c.analyze.Characteristics; 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.Address;
import org.teavm.interop.DelegateTo; import org.teavm.interop.DelegateTo;
import org.teavm.interop.Structure; import org.teavm.interop.Structure;
import org.teavm.model.AnnotationHolder; import org.teavm.model.AnnotationHolder;
import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolder;
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.FieldHolder; import org.teavm.model.FieldHolder;
import org.teavm.model.FieldReader; import org.teavm.model.FieldReader;
@ -70,7 +74,8 @@ public class ClassGenerator {
continue; continue;
} }
if (method.hasModifier(ElementModifier.NATIVE) 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; continue;
} }
@ -388,6 +393,11 @@ public class ClassGenerator {
tag = 0; tag = 0;
sizeExpr = "sizeof(" + CodeWriter.strictTypeAsString(type) + ")"; sizeExpr = "sizeof(" + CodeWriter.strictTypeAsString(type) + ")";
flags |= RuntimeClass.PRIMITIVE; 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"; itemTypeExpr = "NULL";
} }
@ -416,6 +426,29 @@ public class ClassGenerator {
writer.print(".").print(classFieldName("layout")).println(" = " + layout); 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) { private String classFieldName(String field) {
return context.getNames().forMemberField(new FieldReference(RuntimeClass.class.getName(), field)); return context.getNames().forMemberField(new FieldReference(RuntimeClass.class.getName(), field));
} }
@ -456,7 +489,9 @@ public class ClassGenerator {
public void generateClass(ClassHolder cls) { public void generateClass(ClassHolder cls) {
for (MethodHolder method : cls.getMethods()) { for (MethodHolder method : cls.getMethods()) {
if (method.hasModifier(ElementModifier.NATIVE)) { if (method.hasModifier(ElementModifier.NATIVE)) {
tryDelegateToMethod(cls, method); if (!tryDelegateToMethod(cls, method)) {
tryUsingGenerator(method);
}
continue; continue;
} }
@ -491,19 +526,21 @@ public class ClassGenerator {
&& cls.getMethod(new MethodDescriptor("<clinit>", ValueType.VOID)) != null; && cls.getMethod(new MethodDescriptor("<clinit>", 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()); AnnotationHolder delegateToAnnot = method.getAnnotations().get(DelegateTo.class.getName());
if (delegateToAnnot == null) { if (delegateToAnnot == null) {
return; return false;
} }
String methodName = delegateToAnnot.getValue("value").getString(); String methodName = delegateToAnnot.getValue("value").getString();
for (MethodHolder candidate : cls.getMethods()) { for (MethodHolder candidate : cls.getMethods()) {
if (candidate.getName().equals(methodName)) { if (candidate.getName().equals(methodName)) {
delegateToMethod(method, candidate); delegateToMethod(method, candidate);
break; return true;
} }
} }
return false;
} }
private void delegateToMethod(MethodHolder callingMethod, MethodHolder delegateMethod) { private void delegateToMethod(MethodHolder callingMethod, MethodHolder delegateMethod) {
@ -537,6 +574,42 @@ public class ClassGenerator {
writer.outdent().println("}"); 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) { public static String nameOfType(ValueType type) {
if (type instanceof ValueType.Primitive) { if (type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) type).getKind()) { switch (((ValueType.Primitive) type).getKind()) {

View File

@ -20,6 +20,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.teavm.backend.c.analyze.Characteristics; import org.teavm.backend.c.analyze.Characteristics;
import org.teavm.backend.c.generators.Generator;
import org.teavm.backend.c.intrinsic.Intrinsic; import org.teavm.backend.c.intrinsic.Intrinsic;
import org.teavm.diagnostics.Diagnostics; import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.ClassReaderSource; import org.teavm.model.ClassReaderSource;
@ -34,11 +35,12 @@ public class GenerationContext {
private Diagnostics diagnostics; private Diagnostics diagnostics;
private ClassReaderSource classSource; private ClassReaderSource classSource;
private List<Intrinsic> intrinsics; private List<Intrinsic> intrinsics;
private List<Generator> generators;
private Map<MethodReference, Intrinsic> intrinsicCache = new HashMap<>(); private Map<MethodReference, Intrinsic> intrinsicCache = new HashMap<>();
public GenerationContext(VirtualTableProvider virtualTableProvider, Characteristics characteristics, public GenerationContext(VirtualTableProvider virtualTableProvider, Characteristics characteristics,
StringPool stringPool, NameProvider names, Diagnostics diagnostics, ClassReaderSource classSource, StringPool stringPool, NameProvider names, Diagnostics diagnostics, ClassReaderSource classSource,
List<Intrinsic> intrinsics) { List<Intrinsic> intrinsics, List<Generator> generators) {
this.virtualTableProvider = virtualTableProvider; this.virtualTableProvider = virtualTableProvider;
this.characteristics = characteristics; this.characteristics = characteristics;
this.stringPool = stringPool; this.stringPool = stringPool;
@ -46,6 +48,7 @@ public class GenerationContext {
this.diagnostics = diagnostics; this.diagnostics = diagnostics;
this.classSource = classSource; this.classSource = classSource;
this.intrinsics = new ArrayList<>(intrinsics); this.intrinsics = new ArrayList<>(intrinsics);
this.generators = new ArrayList<>(generators);
} }
public VirtualTableProvider getVirtualTableProvider() { public VirtualTableProvider getVirtualTableProvider() {
@ -76,4 +79,13 @@ public class GenerationContext {
return intrinsicCache.computeIfAbsent(method, return intrinsicCache.computeIfAbsent(method,
m -> intrinsics.stream().filter(i -> i.canHandle(m)).findFirst().orElse(null)); 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;
}
} }

View File

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

View File

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

View File

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

View File

@ -15,6 +15,7 @@
*/ */
package org.teavm.backend.wasm; package org.teavm.backend.wasm;
import java.lang.reflect.Array;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -38,6 +39,7 @@ public final class Example {
testArrayIsObject(); testArrayIsObject();
testIsAssignableFrom(); testIsAssignableFrom();
testExceptions(); testExceptions();
testArrayReflection();
testBigInteger(); testBigInteger();
testGC(); 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() { private static void doesNotThrow() {
} }

View File

@ -23,6 +23,18 @@ public class RuntimeClass extends RuntimeObject {
public static final int PRIMITIVE = 2; public static final int PRIMITIVE = 2;
public static final int ENUM = 4; 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 size;
public int flags; public int flags;
public int tag; public int tag;