Start porting metaprogramming API implementation

This commit is contained in:
Alexey Andreev 2016-02-23 13:44:23 +03:00
parent 92dbed2593
commit 5e7311d8cc
12 changed files with 1737 additions and 35 deletions

View File

@ -45,6 +45,7 @@
<file type="gwt" url="file://$PROJECT_DIR$/samples/benchmark" />
<file type="Osmorc" url="file://$PROJECT_DIR$/classlib" />
<file type="Osmorc" url="file://$PROJECT_DIR$/core" />
<file type="Osmorc" url="file://$PROJECT_DIR$/metaprogramming-api" />
<file type="Osmorc" url="file://$PROJECT_DIR$/platform" />
<file type="Osmorc" url="file://$PROJECT_DIR$/tools/chrome-rdp" />
<file type="Osmorc" url="file://$PROJECT_DIR$/tools/core" />

View File

@ -0,0 +1,308 @@
/*
* 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.impl;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.teavm.common.DisjointSet;
import org.teavm.model.BasicBlockReader;
import org.teavm.model.FieldReference;
import org.teavm.model.InstructionLocation;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodHandle;
import org.teavm.model.MethodReference;
import org.teavm.model.PhiReader;
import org.teavm.model.ProgramReader;
import org.teavm.model.RuntimeConstant;
import org.teavm.model.ValueType;
import org.teavm.model.VariableReader;
import org.teavm.model.instructions.ArrayElementType;
import org.teavm.model.instructions.BinaryBranchingCondition;
import org.teavm.model.instructions.BinaryOperation;
import org.teavm.model.instructions.BranchingCondition;
import org.teavm.model.instructions.CastIntegerDirection;
import org.teavm.model.instructions.InstructionReader;
import org.teavm.model.instructions.IntegerSubtype;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.NumericOperandType;
import org.teavm.model.instructions.SwitchTableEntryReader;
/**
*
* @author Alexey Andreev
*/
public class AliasFinder {
int[] aliases;
Object[] constants;
ArrayElement[] arrayElements;
public void findAliases(ProgramReader program) {
DisjointSet set = new DisjointSet();
for (int i = 0; i < program.variableCount(); ++i) {
set.create();
}
AliasReader reader = new AliasReader(set, program.variableCount());
for (int i = 0; i < program.basicBlockCount(); ++i) {
BasicBlockReader block = program.basicBlockAt(i);
block.readAllInstructions(reader);
for (PhiReader phi : block.readPhis()) {
Set<Integer> inputs = phi.readIncomings().stream()
.map(incoming -> incoming.getValue().getIndex())
.collect(Collectors.toSet());
if (inputs.size() == 1) {
set.union(inputs.iterator().next(), phi.getReceiver().getIndex());
}
}
}
int[] map = new int[set.size()];
Arrays.fill(map, -1);
int[] variables = new int[program.variableCount()];
for (int i = 0; i < variables.length; ++i) {
int v = set.find(i);
int master = map[v];
if (master == -1) {
master = i;
map[v] = master;
}
variables[i] = master;
}
aliases = variables;
constants = reader.constants;
arrayElements = reader.arrayElements;
for (int i = 0; i < arrayElements.length; ++i) {
ArrayElement elem = arrayElements[i];
if (elem == null) {
continue;
}
elem.index = aliases[elem.index];
if (constants[elem.index] instanceof Integer) {
elem.index = (Integer) constants[elem.index];
} else {
arrayElements[i] = null;
}
}
}
public static class ArrayElement {
public int array;
public int index;
}
public int[] getAliases() {
return aliases.clone();
}
public Object[] getConstants() {
return constants.clone();
}
public ArrayElement[] getArrayElements() {
return arrayElements.clone();
}
static class AliasReader implements InstructionReader {
DisjointSet disjointSet;
Object[] constants;
ArrayElement[] arrayElements;
AliasReader(DisjointSet disjointSet, int variableCount) {
this.disjointSet = disjointSet;
this.constants = new Object[variableCount];
this.arrayElements = new ArrayElement[variableCount];
}
@Override
public void location(InstructionLocation location) {
}
@Override
public void nop() {
}
@Override
public void classConstant(VariableReader receiver, ValueType cst) {
constants[receiver.getIndex()] = cst;
}
@Override
public void nullConstant(VariableReader receiver) {
}
@Override
public void integerConstant(VariableReader receiver, int cst) {
constants[receiver.getIndex()] = cst;
}
@Override
public void longConstant(VariableReader receiver, long cst) {
constants[receiver.getIndex()] = cst;
}
@Override
public void floatConstant(VariableReader receiver, float cst) {
constants[receiver.getIndex()] = cst;
}
@Override
public void doubleConstant(VariableReader receiver, double cst) {
constants[receiver.getIndex()] = cst;
}
@Override
public void stringConstant(VariableReader receiver, String cst) {
constants[receiver.getIndex()] = cst;
}
@Override
public void binary(BinaryOperation op, VariableReader receiver, VariableReader first, VariableReader second,
NumericOperandType type) {
}
@Override
public void negate(VariableReader receiver, VariableReader operand, NumericOperandType type) {
}
@Override
public void assign(VariableReader receiver, VariableReader assignee) {
disjointSet.union(receiver.getIndex(), assignee.getIndex());
}
@Override
public void cast(VariableReader receiver, VariableReader value, ValueType targetType) {
}
@Override
public void cast(VariableReader receiver, VariableReader value, NumericOperandType sourceType,
NumericOperandType targetType) {
}
@Override
public void cast(VariableReader receiver, VariableReader value, IntegerSubtype type,
CastIntegerDirection targetType) {
}
@Override
public void jumpIf(BranchingCondition cond, VariableReader operand, BasicBlockReader consequent,
BasicBlockReader alternative) {
}
@Override
public void jumpIf(BinaryBranchingCondition cond, VariableReader first, VariableReader second,
BasicBlockReader consequent, BasicBlockReader alternative) {
}
@Override
public void jump(BasicBlockReader target) {
}
@Override
public void choose(VariableReader condition, List<? extends SwitchTableEntryReader> table,
BasicBlockReader defaultTarget) {
}
@Override
public void exit(VariableReader valueToReturn) {
}
@Override
public void raise(VariableReader exception) {
}
@Override
public void createArray(VariableReader receiver, ValueType itemType, VariableReader size) {
}
@Override
public void createArray(VariableReader receiver, ValueType itemType,
List<? extends VariableReader> dimensions) {
}
@Override
public void create(VariableReader receiver, String type) {
}
@Override
public void getField(VariableReader receiver, VariableReader instance, FieldReference field,
ValueType fieldType) {
}
@Override
public void putField(VariableReader instance, FieldReference field, VariableReader value, ValueType fieldType) {
}
@Override
public void arrayLength(VariableReader receiver, VariableReader array) {
}
@Override
public void cloneArray(VariableReader receiver, VariableReader array) {
}
@Override
public void unwrapArray(VariableReader receiver, VariableReader array, ArrayElementType elementType) {
disjointSet.union(receiver.getIndex(), array.getIndex());
}
@Override
public void getElement(VariableReader receiver, VariableReader array, VariableReader index) {
ArrayElement elem = new ArrayElement();
elem.array = array.getIndex();
elem.index = index.getIndex();
arrayElements[receiver.getIndex()] = elem;
}
@Override
public void putElement(VariableReader array, VariableReader index, VariableReader value) {
}
@Override
public void invoke(VariableReader receiver, VariableReader instance, MethodReference method,
List<? extends VariableReader> arguments, InvocationType type) {
}
@Override
public void invokeDynamic(VariableReader receiver, VariableReader instance, MethodDescriptor method,
List<? extends VariableReader> arguments, MethodHandle bootstrapMethod,
List<RuntimeConstant> bootstrapArguments) {
}
@Override
public void isInstance(VariableReader receiver, VariableReader value, ValueType type) {
}
@Override
public void initClass(String className) {
}
@Override
public void nullCheck(VariableReader receiver, VariableReader value) {
}
@Override
public void monitorEnter(VariableReader objectRef) {
}
@Override
public void monitorExit(VariableReader objectRef) {
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,46 @@
/*
* 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.impl;
import org.teavm.metaprogramming.LazyComputation;
import org.teavm.metaprogramming.Value;
import org.teavm.model.InstructionLocation;
import org.teavm.model.ValueType;
/**
*
* @author Alexey Andreev
*/
public class LazyValueImpl<T> implements Value<T> {
boolean evaluated;
VariableContext context;
LazyComputation<T> computation;
ValueType type;
InstructionLocation forcedLocation;
public LazyValueImpl(VariableContext context, LazyComputation<T> computation, ValueType type,
InstructionLocation forcedLocation) {
this.context = context;
this.computation = computation;
this.type = type;
this.forcedLocation = forcedLocation;
}
@Override
public T get() {
throw new IllegalStateException("Can only read this value in emitter domain");
}
}

View File

@ -15,46 +15,151 @@
*/
package org.teavm.metaprogramming.impl;
import org.teavm.dependency.DependencyAgent;
import org.teavm.metaprogramming.Action;
import org.teavm.metaprogramming.Computation;
import org.teavm.metaprogramming.Diagnostics;
import org.teavm.metaprogramming.InvocationHandler;
import org.teavm.metaprogramming.LazyComputation;
import org.teavm.metaprogramming.ReflectClass;
import org.teavm.metaprogramming.Scope;
import org.teavm.metaprogramming.SourceLocation;
import org.teavm.metaprogramming.Value;
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.CallLocation;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassReaderSource;
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.ExitInstruction;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
public final class MetaprogrammingImpl {
static ClassLoader classLoader;
static ClassReaderSource classSource;
static ReflectContext reflectContext;
static DependencyAgent agent;
static VariableContext varContext;
static MethodReference templateMethod;
static CompositeMethodGenerator generator;
static ValueType returnType;
private MetaprogrammingImpl() {
}
public static <T> Value<T> emit(Computation<T> computation) {
unsupported();
return null;
if (computation instanceof ValueImpl<?>) {
@SuppressWarnings("unchecked")
ValueImpl<T> valueImpl = (ValueImpl<T>) computation;
Variable var = varContext.emitVariable(valueImpl, new CallLocation(templateMethod, generator.location));
return new ValueImpl<>(var, varContext, valueImpl.type);
} else if (computation instanceof LazyValueImpl<?>) {
LazyValueImpl<?> valueImpl = (LazyValueImpl<?>) computation;
Variable var = generator.lazy(valueImpl);
return var != null ? new ValueImpl<>(var, varContext, valueImpl.type) : null;
} else {
Fragment fragment = (Fragment) computation;
MethodReader method = classSource.resolve(fragment.method);
generator.addProgram(method.getProgram(), fragment.capturedValues);
return new ValueImpl<>(generator.getResultVar(), varContext, fragment.method.getReturnType());
}
}
public static void emit(Action action) {
unsupported();
Fragment fragment = (Fragment) action;
MethodReader method = classSource.resolve(fragment.method);
generator.addProgram(method.getProgram(), fragment.capturedValues);
}
public static <T> Value<T> lazyFragment(LazyComputation<T> computation) {
unsupported();
return null;
return lazyFragment(ValueType.object("java.lang.Object"), computation);
}
public static <T> Value<T> lazy(Computation<T> computation) {
unsupported();
return null;
Fragment fragment = (Fragment) computation;
return lazyFragment(fragment.method.getReturnType(), () -> emit(computation));
}
public static Scope currentScope() {
unsupported();
return null;
private static <T> Value<T> lazyFragment(ValueType type, LazyComputation<T> computation) {
return new LazyValueImpl<>(varContext, computation, type, generator.forcedLocation);
}
public static void exit(Value<?> value) {
if (value == null) {
returnValue(null);
return;
}
if (value instanceof Fragment) {
Fragment fragment = (Fragment) value;
MethodReader method = classSource.resolve(fragment.method);
generator.addProgram(method.getProgram(), fragment.capturedValues);
generator.blockIndex = generator.returnBlockIndex;
returnValue(unbox(generator.getResultVar()));
} else if (value instanceof ValueImpl) {
ValueImpl<?> valueImpl = (ValueImpl<?>) value;
returnValue(unbox(varContext.emitVariable(valueImpl, new CallLocation(templateMethod,
generator.location))));
} else if (value instanceof LazyValueImpl) {
Variable var = generator.lazy((LazyValueImpl<?>) value);
returnValue(unbox(var));
} else {
throw new IllegalStateException("Unexpected computation type: " + value.getClass().getName());
}
}
static Variable unbox(Variable var) {
if (returnType instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) returnType).getKind()) {
case BOOLEAN:
var = unbox(var, Boolean.class, boolean.class);
break;
case BYTE:
var = unbox(var, Byte.class, byte.class);
break;
case SHORT:
var = unbox(var, Short.class, short.class);
break;
case CHARACTER:
var = unbox(var, Character.class, char.class);
break;
case INTEGER:
var = unbox(var, Integer.class, int.class);
break;
case LONG:
var = unbox(var, Long.class, long.class);
break;
case FLOAT:
var = unbox(var, Float.class, float.class);
break;
case DOUBLE:
var = unbox(var, Double.class, double.class);
break;
}
}
return var;
}
static Variable unbox(Variable var, Class<?> boxed, Class<?> primitive) {
InvokeInstruction insn = new InvokeInstruction();
insn.setInstance(var);
insn.setType(InvocationType.VIRTUAL);
insn.setMethod(new MethodReference(boxed, primitive.getName() + "Value", primitive));
var = generator.program.createVariable();
insn.setReceiver(var);
generator.add(insn);
return var;
}
public static void exit() {
exit(null);
}
public static void location(String fileName, int lineNumber) {
@ -88,8 +193,7 @@ public final class MetaprogrammingImpl {
}
public static ReflectClass<?> createClass(byte[] bytecode) {
unsupported();
return null;
return findClass(agent.submitClassFile(bytecode).replace('/', '.'));
}
public static <T> Value<T> proxy(Class<T> type, InvocationHandler<T> handler) {
@ -101,8 +205,58 @@ public final class MetaprogrammingImpl {
return null;
}
private static void returnValue(Variable var) {
ExitInstruction insn = new ExitInstruction();
insn.setValueToReturn(var);
generator.add(insn);
}
public static Diagnostics getDiagnostics() {
return diagnostics;
}
public void submitClass(ClassHolder cls) {
agent.submitClass(cls);
}
private static void unsupported() {
throw new UnsupportedOperationException("This operation is only supported from TeaVM compile-time "
+ "environment");
}
private static Diagnostics diagnostics = new Diagnostics() {
@Override
public void error(SourceLocation location, String error, Object... params) {
convertParams(params);
agent.getDiagnostics().error(convertLocation(location), error, params);
}
@Override
public void warning(SourceLocation location, String error, Object... params) {
convertParams(params);
agent.getDiagnostics().warning(convertLocation(location), error, params);
}
private void convertParams(Object[] params) {
for (int i = 0; i < params.length; ++i) {
if (params[i] instanceof ReflectMethodImpl) {
params[i] = ((ReflectMethodImpl) params[i]).method.getReference();
} else if (params[i] instanceof ReflectClassImpl) {
params[i] = ((ReflectClassImpl<?>) params[i]).type;
} else if (params[i] instanceof ReflectFieldImpl) {
params[i] = ((ReflectFieldImpl) params[i]).field.getReference();
} else if (params[i] instanceof Class<?>) {
params[i] = ValueType.parse((Class<?>) params[i]);
}
}
}
private CallLocation convertLocation(SourceLocation location) {
MethodReader method = ((ReflectMethodImpl) location.getMethod()).method;
return location.getFileName() != null
? new CallLocation(method.getReference(),
new InstructionLocation(location.getFileName(), location.getLineNumber()))
: new CallLocation(method.getReference());
}
};
}

View File

@ -27,9 +27,19 @@
</parent>
<artifactId>teavm-metaprogramming-api</artifactId>
<packaging>bundle</packaging>
<name>TeaVM metaprogramming API</name>
<description>Declaration of interfaces and annotations for TeaVM metaprogramming</description>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
@ -47,6 +57,17 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>

View File

@ -39,9 +39,12 @@ public final class Metaprogramming {
return null;
}
public static Scope currentScope() {
public static void exit(Value<?> returnValue) {
unsupported();
}
public static void exit() {
unsupported();
return null;
}
public static void location(String fileName, int lineNumber) {
@ -86,6 +89,11 @@ public final class Metaprogramming {
return null;
}
public static Diagnostics getDiagnostics() {
unsupported();
return null;
}
private static void unsupported() {
throw new UnsupportedOperationException("This operation is only supported from TeaVM compile-time "
+ "environment");

View File

@ -1,20 +0,0 @@
/*
* 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;
public interface Scope {
void exit(Object returnValue);
}

View File

@ -0,0 +1,40 @@
/*
* 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.junit.Assert.assertEquals;
import static org.teavm.metaprogramming.Metaprogramming.exit;
import org.junit.Test;
import org.teavm.metaprogramming.CompileTime;
import org.teavm.metaprogramming.Meta;
import org.teavm.metaprogramming.ReflectClass;
import org.teavm.metaprogramming.Value;
@CompileTime
public class MetaprogrammingTest {
@Test
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));
}
@Meta
static native int classNameLength(Class<?> cls, int add);
static void classNameLength(ReflectClass<Object> cls, Value<Integer> add) {
int length = cls.getName().length();
exit(() -> length + add.get());
}
}

View File

@ -1,13 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="Osmorc" name="OSGi">
<configuration manifestGenerationMode="OsmorcControlled" manifestLocation="" jarfileLocation="teavm-metaprogramming-api-1.0.0-SNAPSHOT.jar" outputPathType="CompilerOutputPath" bndFileLocation="" bundlorFileLocation="" bundleActivator="" bundleSymbolicName="teavm-metaprogramming-api" bundleVersion="1.0.0.SNAPSHOT" ignoreFilePattern="" useProjectDefaultManifestFileLocation="true" alwaysRebuildBundleJAR="false" doNotSynchronizeWithMaven="false">
<additionalProperties>
<property key="Bundle-Description" value="Declaration of interfaces and annotations for TeaVM metaprogramming" />
<property key="Export-Package" value="org.teavm.metaprogramming.*" />
<property key="Bundle-Name" value="TeaVM metaprogramming API" />
</additionalProperties>
<additionalJARContents />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.11" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
</component>
</module>

View File

@ -55,6 +55,13 @@
<classifier>tests</classifier>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-metaprogramming-api</artifactId>
<version>${project.version}</version>
<classifier>tests</classifier>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>

View File

@ -25,6 +25,7 @@
<orderEntry type="module" module-name="teavm-platform" />
<orderEntry type="module" module-name="teavm-jso-apis" />
<orderEntry type="module" module-name="teavm-jso-apis" production-on-test="" />
<orderEntry type="module" module-name="teavm-metaprogramming-api" production-on-test="" />
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.11" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
<orderEntry type="module" module-name="teavm-tooling" scope="TEST" />