mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Add several tests for metaprogramming
This commit is contained in:
parent
3e562aa08a
commit
518e13cf07
|
@ -2,7 +2,7 @@
|
|||
<configuration default="false" name="run-tests" type="JUnit" factoryName="JUnit">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="org.teavm.classlib.java.lang.*" />
|
||||
<option name="PATTERN" value="org.teavm.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
|
|
|
@ -28,17 +28,25 @@ import org.teavm.metaprogramming.impl.reflect.ReflectClassImpl;
|
|||
import org.teavm.metaprogramming.impl.reflect.ReflectContext;
|
||||
import org.teavm.metaprogramming.impl.reflect.ReflectFieldImpl;
|
||||
import org.teavm.metaprogramming.impl.reflect.ReflectMethodImpl;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.InstructionLocation;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.Variable;
|
||||
import org.teavm.model.instructions.DoubleConstantInstruction;
|
||||
import org.teavm.model.instructions.ExitInstruction;
|
||||
import org.teavm.model.instructions.FloatConstantInstruction;
|
||||
import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
import org.teavm.model.instructions.InvokeInstruction;
|
||||
import org.teavm.model.instructions.LongConstantInstruction;
|
||||
import org.teavm.model.instructions.NullConstantInstruction;
|
||||
import org.teavm.model.util.InstructionTransitionExtractor;
|
||||
|
||||
public final class MetaprogrammingImpl {
|
||||
static ClassLoader classLoader;
|
||||
|
@ -217,6 +225,62 @@ public final class MetaprogrammingImpl {
|
|||
agent.submitClass(cls);
|
||||
}
|
||||
|
||||
public static void close() {
|
||||
InstructionTransitionExtractor transitionExtractor = new InstructionTransitionExtractor();
|
||||
BasicBlock block = generator.currentBlock();
|
||||
Instruction lastInstruction = block.getLastInstruction();
|
||||
if (lastInstruction != null) {
|
||||
lastInstruction.acceptVisitor(transitionExtractor);
|
||||
}
|
||||
if (transitionExtractor.getTargets() != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Variable var;
|
||||
if (returnType instanceof ValueType.Void) {
|
||||
var = null;
|
||||
} else if (returnType instanceof ValueType.Primitive) {
|
||||
var = generator.program.createVariable();
|
||||
switch (((ValueType.Primitive) returnType).getKind()) {
|
||||
case BOOLEAN:
|
||||
case BYTE:
|
||||
case SHORT:
|
||||
case CHARACTER:
|
||||
case INTEGER: {
|
||||
IntegerConstantInstruction constantInsn = new IntegerConstantInstruction();
|
||||
constantInsn.setReceiver(var);
|
||||
generator.add(constantInsn);
|
||||
break;
|
||||
}
|
||||
case LONG: {
|
||||
LongConstantInstruction constantInsn = new LongConstantInstruction();
|
||||
constantInsn.setReceiver(var);
|
||||
generator.add(constantInsn);
|
||||
break;
|
||||
}
|
||||
case FLOAT: {
|
||||
FloatConstantInstruction constantInsn = new FloatConstantInstruction();
|
||||
constantInsn.setReceiver(var);
|
||||
generator.add(constantInsn);
|
||||
break;
|
||||
}
|
||||
case DOUBLE: {
|
||||
DoubleConstantInstruction constantInsn = new DoubleConstantInstruction();
|
||||
constantInsn.setReceiver(var);
|
||||
generator.add(constantInsn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
NullConstantInstruction constantInsn = new NullConstantInstruction();
|
||||
var = generator.program.createVariable();
|
||||
constantInsn.setReceiver(var);
|
||||
generator.add(constantInsn);
|
||||
}
|
||||
|
||||
returnValue(var);
|
||||
}
|
||||
|
||||
private static void unsupported() {
|
||||
throw new UnsupportedOperationException("This operation is only supported from TeaVM compile-time "
|
||||
+ "environment");
|
||||
|
|
|
@ -146,6 +146,7 @@ class UsageGenerator {
|
|||
|
||||
try {
|
||||
proxyMethod.invoke(null, proxyArgs);
|
||||
MetaprogrammingImpl.close();
|
||||
Program program = MetaprogrammingImpl.generator.getProgram();
|
||||
//new BoxingEliminator().optimize(program);
|
||||
|
||||
|
|
|
@ -18,5 +18,5 @@ package org.teavm.metaprogramming;
|
|||
import org.teavm.metaprogramming.reflect.ReflectMethod;
|
||||
|
||||
public interface InvocationHandler<T> {
|
||||
void invoke(Value<T> proxy, ReflectMethod method, Value<Object>[] args);
|
||||
Computation<?> invoke(Value<T> proxy, ReflectMethod method, Value<Object>[] args);
|
||||
}
|
||||
|
|
|
@ -71,8 +71,7 @@ public final class Metaprogramming {
|
|||
}
|
||||
|
||||
public static <T> ReflectClass<T[]> arrayClass(ReflectClass<T> componentType) {
|
||||
unsupported();
|
||||
return null;
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public static ReflectClass<?> createClass(byte[] bytecode) {
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2016 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.metaprogramming.test;
|
||||
|
||||
import static org.teavm.metaprogramming.Metaprogramming.emit;
|
||||
import org.teavm.metaprogramming.CompileTime;
|
||||
import org.teavm.metaprogramming.Value;
|
||||
|
||||
@CompileTime
|
||||
public class MetaprogrammingGenerator2 {
|
||||
public Value<String> addParentheses(String value) {
|
||||
return emit(() -> "[" + value + "]");
|
||||
}
|
||||
}
|
|
@ -16,7 +16,13 @@
|
|||
package org.teavm.metaprogramming.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.teavm.metaprogramming.Metaprogramming.arrayClass;
|
||||
import static org.teavm.metaprogramming.Metaprogramming.emit;
|
||||
import static org.teavm.metaprogramming.Metaprogramming.exit;
|
||||
import static org.teavm.metaprogramming.Metaprogramming.findClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.junit.SkipJVM;
|
||||
|
@ -25,12 +31,15 @@ import org.teavm.metaprogramming.CompileTime;
|
|||
import org.teavm.metaprogramming.Meta;
|
||||
import org.teavm.metaprogramming.ReflectClass;
|
||||
import org.teavm.metaprogramming.Value;
|
||||
import org.teavm.metaprogramming.reflect.ReflectField;
|
||||
import org.teavm.metaprogramming.reflect.ReflectMethod;
|
||||
import org.teavm.metaprogramming.test.subpackage.MetaprogrammingGenerator;
|
||||
|
||||
@CompileTime
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@SkipJVM
|
||||
public class MetaprogrammingTest {
|
||||
@Test
|
||||
@SkipJVM
|
||||
public void works() {
|
||||
assertEquals("java.lang.Object".length() + 2, classNameLength(Object.class, 2));
|
||||
assertEquals("java.lang.Integer".length() + 3, classNameLength(Integer.valueOf(5).getClass(), 3));
|
||||
|
@ -42,4 +51,299 @@ public class MetaprogrammingTest {
|
|||
int length = cls.getName().length();
|
||||
exit(() -> length + add.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getsField() {
|
||||
Context ctx = new Context();
|
||||
ctx.a = 2;
|
||||
ctx.b = 3;
|
||||
|
||||
assertEquals(2, getField(ctx.getClass(), ctx));
|
||||
}
|
||||
|
||||
@Meta
|
||||
private static native Object getField(Class<?> cls, Object obj);
|
||||
private static void getField(ReflectClass<Object> cls, Value<Object> obj) {
|
||||
ReflectField field = cls.getField("a");
|
||||
exit(() -> field.get(obj));
|
||||
}
|
||||
@Test
|
||||
public void setsField() {
|
||||
Context ctx = new Context();
|
||||
setField(ctx.getClass(), ctx, 3);
|
||||
|
||||
assertEquals(3, ctx.a);
|
||||
}
|
||||
|
||||
@Meta
|
||||
private static native void setField(Class<?> cls, Object obj, Object value);
|
||||
private static void setField(ReflectClass<Object> cls, Value<Object> obj, Value<Object> value) {
|
||||
ReflectField field = cls.getField("a");
|
||||
emit(() -> field.set(obj, value));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodInvoked() {
|
||||
assertEquals("debug!", callDebug(A.class, new A()));
|
||||
assertEquals("missing", callDebug(B.class, new B()));
|
||||
assertEquals("missing", callDebug(A.class, new A(), "foo", 23));
|
||||
assertEquals("debug!foo:23", callDebug(B.class, new B(), "foo", 23));
|
||||
}
|
||||
|
||||
@Meta
|
||||
private static native String callDebug(Class<?> cls, Object obj);
|
||||
private static void callDebug(ReflectClass<?> cls, Value<Object> obj) {
|
||||
ReflectMethod method = cls.getMethod("debug");
|
||||
if (method == null) {
|
||||
exit(() -> "missing");
|
||||
} else {
|
||||
exit(() -> method.invoke(obj.get()));
|
||||
}
|
||||
}
|
||||
|
||||
@Meta
|
||||
private static native String callDebug(Class<?> cls, Object obj, String a, int b);
|
||||
private static void callDebug(ReflectClass<?> cls, Value<Object> obj, Value<String> a, Value<Integer> b) {
|
||||
ReflectClass<String> stringClass = findClass(String.class);
|
||||
ReflectClass<Integer> intClass = findClass(int.class);
|
||||
ReflectMethod method = cls.getMethod("debug", stringClass, intClass);
|
||||
if (method == null) {
|
||||
exit(() -> "missing");
|
||||
} else {
|
||||
exit(() -> method.invoke(obj.get(), a.get(), b.get()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorInvoked() {
|
||||
assertEquals(C.class.getName(), callConstructor(C.class).getClass().getName());
|
||||
assertNull(callConstructor(D.class));
|
||||
|
||||
assertNull(callConstructor(C.class, "foo", 23));
|
||||
|
||||
D instance = (D) callConstructor(D.class, "foo", 23);
|
||||
assertEquals(D.class.getName(), instance.getClass().getName());
|
||||
assertEquals("foo", instance.a);
|
||||
assertEquals(23, instance.b);
|
||||
}
|
||||
|
||||
@Meta
|
||||
private static native Object callConstructor(Class<?> type);
|
||||
private static void callConstructor(ReflectClass<?> type) {
|
||||
ReflectMethod ctor = type.getMethod("<init>");
|
||||
if (ctor != null) {
|
||||
exit(() -> ctor.construct());
|
||||
} else {
|
||||
exit(() -> null);
|
||||
}
|
||||
}
|
||||
|
||||
@Meta
|
||||
private static native Object callConstructor(Class<?> type, String a, int b);
|
||||
private static void callConstructor(ReflectClass<?> type, Value<String> a, Value<Integer> b) {
|
||||
ReflectClass<String> stringClass = findClass(String.class);
|
||||
ReflectClass<Integer> intClass = findClass(int.class);
|
||||
ReflectMethod ctor = type.getMethod("<init>", stringClass, intClass);
|
||||
if (ctor != null) {
|
||||
exit(() -> ctor.construct(a, b));
|
||||
} else {
|
||||
exit(() -> null);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void capturesArray() {
|
||||
assertEquals("23:foo", captureArray(23, "foo"));
|
||||
}
|
||||
|
||||
@Meta
|
||||
private static native String captureArray(int a, String b);
|
||||
private static void captureArray(Value<Integer> a, Value<String> b) {
|
||||
Value<?>[] array = { a, emit(() -> ":"), b };
|
||||
exit(() -> String.valueOf(array[0].get()) + array[1].get() + array[2].get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isInstanceWorks() {
|
||||
assertTrue(isInstance("foo", String.class));
|
||||
assertFalse(isInstance(23, String.class));
|
||||
}
|
||||
|
||||
@Meta
|
||||
private static native boolean isInstance(Object obj, Class<?> type);
|
||||
private static void isInstance(Value<Object> obj, ReflectClass<?> type) {
|
||||
exit(() -> type.isInstance(obj.get()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void capturesNull() {
|
||||
assertEquals("foo:", captureArgument("foo"));
|
||||
}
|
||||
|
||||
@Meta
|
||||
private static native String captureArgument(String a);
|
||||
private static void captureArgument(Value<String> a) {
|
||||
exit(() -> a.get() + ":");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void annotationsWork() {
|
||||
assertEquals(""
|
||||
+ "foo:23:Object\n"
|
||||
+ "foo=!:42:String:int\n"
|
||||
+ "f=!:23\n",
|
||||
readAnnotations(WithAnnotations.class, new WithAnnotations()));
|
||||
}
|
||||
|
||||
@Meta
|
||||
private static native String readAnnotations(Class<?> cls, Object obj);
|
||||
private static void readAnnotations(ReflectClass<Object> cls, Value<Object> obj) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(describeAnnotation(cls.getAnnotation(TestAnnotation.class))).append('\n');
|
||||
for (ReflectMethod method : cls.getDeclaredMethods()) {
|
||||
TestAnnotation annot = method.getAnnotation(TestAnnotation.class);
|
||||
if (annot == null) {
|
||||
continue;
|
||||
}
|
||||
sb.append(method.getName()).append('=').append(describeAnnotation(annot)).append('\n');
|
||||
}
|
||||
for (ReflectField field : cls.getDeclaredFields()) {
|
||||
TestAnnotation annot = field.getAnnotation(TestAnnotation.class);
|
||||
if (annot == null) {
|
||||
continue;
|
||||
}
|
||||
sb.append(field.getName()).append('=').append(describeAnnotation(annot)).append('\n');
|
||||
}
|
||||
String result = sb.toString();
|
||||
exit(() -> result);
|
||||
}
|
||||
|
||||
private static String describeAnnotation(TestAnnotation annot) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(annot.a()).append(':').append(annot.b());
|
||||
for (Class<?> cls : annot.c()) {
|
||||
sb.append(':').append(cls.getSimpleName());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compileTimeAnnotationRespectsPackage() {
|
||||
assertEquals("(foo)", compileTimePackage(true));
|
||||
}
|
||||
|
||||
@Meta
|
||||
private static native String compileTimePackage(boolean ignoreMe);
|
||||
private static void compileTimePackage(Value<Boolean> ignoreMe) {
|
||||
Value<String> result = new MetaprogrammingGenerator().addParentheses("foo");
|
||||
exit(() -> result.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compileTimeAnnotationRespectsClass() {
|
||||
assertEquals("[foo]", compileTimeClass(true));
|
||||
}
|
||||
|
||||
@Meta
|
||||
private static native String compileTimeClass(boolean ignoreMe);
|
||||
private static void compileTimeClass(Value<Boolean> ignoreMe) {
|
||||
Value<String> result = new MetaprogrammingGenerator2().addParentheses("foo");
|
||||
exit(() -> result.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compileTimeAnnotationRespectsNestedClass() {
|
||||
assertEquals("{foo}", compileTimeNestedClass(true));
|
||||
}
|
||||
|
||||
@Meta
|
||||
private static native String compileTimeNestedClass(boolean ignoreMe);
|
||||
private static void compileTimeNestedClass(Value<Boolean> ignoreMe) {
|
||||
Value<String> result = new MetaprogrammingGenerator3().addParentheses("foo");
|
||||
exit(() -> result.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emitsClassLiteralFromReflectClass() {
|
||||
assertEquals(String[].class.getName(), emitClassLiteral(String.class));
|
||||
}
|
||||
|
||||
@Meta
|
||||
private static native String emitClassLiteral(Class<?> cls);
|
||||
private static void emitClassLiteral(ReflectClass<?> cls) {
|
||||
ReflectClass<?> arrayClass = arrayClass(cls);
|
||||
exit(() -> arrayClass.asJavaClass().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createsArrayViaReflection() {
|
||||
Object array = createArrayOfType(String.class, 10);
|
||||
assertEquals(String[].class, array.getClass());
|
||||
assertEquals(10, ((String[]) array).length);
|
||||
}
|
||||
|
||||
@Meta
|
||||
private static native Object createArrayOfType(Class<?> cls, int size);
|
||||
private static void createArrayOfType(ReflectClass<?> cls, Value<Integer> size) {
|
||||
exit(() -> cls.createArray(size.get()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getsArrayElementViaReflection() {
|
||||
assertEquals("foo", getArrayElement(String[].class, new String[] { "foo" }, 0));
|
||||
}
|
||||
|
||||
@Meta
|
||||
private static native Object getArrayElement(Class<?> type, Object array, int index);
|
||||
private static void getArrayElement(ReflectClass<?> type, Value<Object> array, Value<Integer> index) {
|
||||
exit(() -> type.getArrayElement(array.get(), index.get()));
|
||||
}
|
||||
|
||||
static class Context {
|
||||
public int a;
|
||||
public int b;
|
||||
}
|
||||
|
||||
class A {
|
||||
public String debug() {
|
||||
return "debug!";
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
public String debug(String a, int b) {
|
||||
return "debug!" + a + ":" + b;
|
||||
}
|
||||
}
|
||||
static class C {
|
||||
public C() {
|
||||
}
|
||||
}
|
||||
|
||||
static class D {
|
||||
String a;
|
||||
int b;
|
||||
|
||||
public D(String a, int b) {
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
}
|
||||
}
|
||||
|
||||
@TestAnnotation(a = "foo", c = Object.class)
|
||||
static class WithAnnotations {
|
||||
@TestAnnotation(c = {})
|
||||
int f;
|
||||
|
||||
@TestAnnotation(b = 42, c = { String.class, int.class })
|
||||
int foo() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static class MetaprogrammingGenerator3 {
|
||||
public Value<String> addParentheses(String value) {
|
||||
return emit(() -> "{" + value + "}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright 2016 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.metaprogramming.test;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface TestAnnotation {
|
||||
String a() default "!";
|
||||
|
||||
int b() default 23;
|
||||
|
||||
Class<?>[] c();
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2016 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.metaprogramming.test.subpackage;
|
||||
|
||||
import static org.teavm.metaprogramming.Metaprogramming.emit;
|
||||
import org.teavm.metaprogramming.Value;
|
||||
|
||||
public class MetaprogrammingGenerator {
|
||||
public Value<String> addParentheses(String value) {
|
||||
return emit(() -> "(" + value + ")");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
@CompileTime
|
||||
package org.teavm.metaprogramming.test.subpackage;
|
||||
|
||||
import org.teavm.metaprogramming.CompileTime;
|
Loading…
Reference in New Issue
Block a user