mirror of
https://github.com/konsoletyper/teavm.git
synced 2024-11-27 01:30:35 +08:00
C backend: add support for Array.get
This commit is contained in:
parent
f079b5fbcc
commit
dc227e1e42
@ -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<Generator> 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"))) {
|
||||
|
@ -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("<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());
|
||||
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()) {
|
||||
|
@ -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<Intrinsic> intrinsics;
|
||||
private List<Generator> generators;
|
||||
private Map<MethodReference, Intrinsic> intrinsicCache = new HashMap<>();
|
||||
|
||||
public GenerationContext(VirtualTableProvider virtualTableProvider, Characteristics characteristics,
|
||||
StringPool stringPool, NameProvider names, Diagnostics diagnostics, ClassReaderSource classSource,
|
||||
List<Intrinsic> intrinsics) {
|
||||
List<Intrinsic> intrinsics, List<Generator> 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;
|
||||
}
|
||||
}
|
||||
|
@ -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 + ");");
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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() {
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user