diff --git a/.idea/misc.xml b/.idea/misc.xml
index ec13cf2f5..6f86e5810 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -45,6 +45,7 @@
+
diff --git a/core/src/main/java/org/teavm/metaprogramming/impl/AliasFinder.java b/core/src/main/java/org/teavm/metaprogramming/impl/AliasFinder.java
new file mode 100644
index 000000000..038e2161b
--- /dev/null
+++ b/core/src/main/java/org/teavm/metaprogramming/impl/AliasFinder.java
@@ -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 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 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) {
+ }
+ }
+}
diff --git a/core/src/main/java/org/teavm/metaprogramming/impl/CompositeMethodGenerator.java b/core/src/main/java/org/teavm/metaprogramming/impl/CompositeMethodGenerator.java
new file mode 100644
index 000000000..38c57da76
--- /dev/null
+++ b/core/src/main/java/org/teavm/metaprogramming/impl/CompositeMethodGenerator.java
@@ -0,0 +1,1121 @@
+/*
+ * 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.lang.reflect.Array;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.teavm.diagnostics.Diagnostics;
+import org.teavm.metaprogramming.ReflectClass;
+import org.teavm.metaprogramming.Value;
+import org.teavm.metaprogramming.impl.AliasFinder.ArrayElement;
+import org.teavm.metaprogramming.impl.reflect.ReflectClassImpl;
+import org.teavm.metaprogramming.impl.reflect.ReflectFieldImpl;
+import org.teavm.metaprogramming.impl.reflect.ReflectMethodImpl;
+import org.teavm.metaprogramming.reflect.ReflectField;
+import org.teavm.metaprogramming.reflect.ReflectMethod;
+import org.teavm.model.BasicBlock;
+import org.teavm.model.BasicBlockReader;
+import org.teavm.model.CallLocation;
+import org.teavm.model.ElementModifier;
+import org.teavm.model.FieldReference;
+import org.teavm.model.Incoming;
+import org.teavm.model.IncomingReader;
+import org.teavm.model.Instruction;
+import org.teavm.model.InstructionLocation;
+import org.teavm.model.InvokeDynamicInstruction;
+import org.teavm.model.MethodDescriptor;
+import org.teavm.model.MethodHandle;
+import org.teavm.model.MethodReference;
+import org.teavm.model.Phi;
+import org.teavm.model.PhiReader;
+import org.teavm.model.Program;
+import org.teavm.model.ProgramReader;
+import org.teavm.model.RuntimeConstant;
+import org.teavm.model.TryCatchBlock;
+import org.teavm.model.TryCatchBlockReader;
+import org.teavm.model.ValueType;
+import org.teavm.model.Variable;
+import org.teavm.model.VariableReader;
+import org.teavm.model.instructions.ArrayElementType;
+import org.teavm.model.instructions.ArrayLengthInstruction;
+import org.teavm.model.instructions.AssignInstruction;
+import org.teavm.model.instructions.BinaryBranchingCondition;
+import org.teavm.model.instructions.BinaryBranchingInstruction;
+import org.teavm.model.instructions.BinaryInstruction;
+import org.teavm.model.instructions.BinaryOperation;
+import org.teavm.model.instructions.BranchingCondition;
+import org.teavm.model.instructions.BranchingInstruction;
+import org.teavm.model.instructions.CastInstruction;
+import org.teavm.model.instructions.CastIntegerDirection;
+import org.teavm.model.instructions.CastIntegerInstruction;
+import org.teavm.model.instructions.CastNumberInstruction;
+import org.teavm.model.instructions.ClassConstantInstruction;
+import org.teavm.model.instructions.CloneArrayInstruction;
+import org.teavm.model.instructions.ConstructArrayInstruction;
+import org.teavm.model.instructions.ConstructInstruction;
+import org.teavm.model.instructions.ConstructMultiArrayInstruction;
+import org.teavm.model.instructions.DoubleConstantInstruction;
+import org.teavm.model.instructions.FloatConstantInstruction;
+import org.teavm.model.instructions.GetElementInstruction;
+import org.teavm.model.instructions.GetFieldInstruction;
+import org.teavm.model.instructions.InitClassInstruction;
+import org.teavm.model.instructions.InstructionReader;
+import org.teavm.model.instructions.IntegerConstantInstruction;
+import org.teavm.model.instructions.IntegerSubtype;
+import org.teavm.model.instructions.InvocationType;
+import org.teavm.model.instructions.InvokeInstruction;
+import org.teavm.model.instructions.IsInstanceInstruction;
+import org.teavm.model.instructions.JumpInstruction;
+import org.teavm.model.instructions.LongConstantInstruction;
+import org.teavm.model.instructions.MonitorEnterInstruction;
+import org.teavm.model.instructions.MonitorExitInstruction;
+import org.teavm.model.instructions.NegateInstruction;
+import org.teavm.model.instructions.NullCheckInstruction;
+import org.teavm.model.instructions.NullConstantInstruction;
+import org.teavm.model.instructions.NumericOperandType;
+import org.teavm.model.instructions.PutElementInstruction;
+import org.teavm.model.instructions.PutFieldInstruction;
+import org.teavm.model.instructions.RaiseInstruction;
+import org.teavm.model.instructions.StringConstantInstruction;
+import org.teavm.model.instructions.SwitchInstruction;
+import org.teavm.model.instructions.SwitchTableEntry;
+import org.teavm.model.instructions.SwitchTableEntryReader;
+import org.teavm.model.instructions.UnwrapArrayInstruction;
+
+public class CompositeMethodGenerator {
+ private Diagnostics diagnostics;
+ Program program;
+ InstructionLocation location;
+ InstructionLocation forcedLocation;
+ int blockIndex;
+ int returnBlockIndex;
+ private Variable resultVar;
+ private Phi resultPhi;
+ private Map phiBlockMap = new HashMap<>();
+ VariableContext varContext;
+
+ CompositeMethodGenerator(VariableContext varContext) {
+ this(varContext, new Program());
+ program.createBasicBlock();
+ }
+
+ CompositeMethodGenerator(VariableContext varContext, Program program) {
+ this.diagnostics = MetaprogrammingImpl.agent.getDiagnostics();
+ this.program = program;
+ this.varContext = varContext;
+ }
+
+ public void addProgram(ProgramReader template, List capturedValues) {
+ location = null;
+ resultVar = null;
+ resultPhi = null;
+ AliasFinder aliasFinder = new AliasFinder();
+ aliasFinder.findAliases(template);
+
+ CapturedValue[] capturedValueArray = new CapturedValue[template.variableCount()];
+ for (int i = 0; i < capturedValues.size(); ++i) {
+ capturedValueArray[i + 1] = capturedValues.get(i);
+ }
+
+ TemplateSubstitutor substitutor = new TemplateSubstitutor(capturedValueArray, aliasFinder.getAliases(),
+ aliasFinder.getArrayElements(), program.basicBlockCount() - 1,
+ program.variableCount() - capturedValues.size());
+
+ for (int i = 0; i < template.basicBlockCount(); ++i) {
+ program.createBasicBlock();
+ }
+ returnBlockIndex = program.basicBlockCount() - 1;
+
+ for (int i = capturedValues.size(); i < template.variableCount(); ++i) {
+ program.createVariable();
+ }
+
+ int startBlock = blockIndex;
+ for (int i = 0; i < template.basicBlockCount(); ++i) {
+ BasicBlockReader templateBlock = template.basicBlockAt(i);
+ if (i > 0) {
+ blockIndex = substitutor.blockOffset + i;
+ }
+ BasicBlock targetBlock = program.basicBlockAt(blockIndex);
+
+ for (PhiReader templatePhi : templateBlock.readPhis()) {
+ Phi phi = new Phi();
+ for (IncomingReader templateIncoming : templatePhi.readIncomings()) {
+ Incoming incoming = new Incoming();
+ incoming.setSource(substitutor.block(templateIncoming.getSource()));
+ incoming.setValue(substitutor.var(templateIncoming.getValue()));
+ phi.getIncomings().add(incoming);
+ }
+ phi.setReceiver(substitutor.var(templatePhi.getReceiver()));
+ targetBlock.getPhis().add(phi);
+ }
+
+ for (TryCatchBlockReader templateTryCatch : templateBlock.readTryCatchBlocks()) {
+ TryCatchBlock tryCatch = new TryCatchBlock();
+ tryCatch.setExceptionType(templateTryCatch.getExceptionType());
+ tryCatch.setExceptionVariable(substitutor.var(templateTryCatch.getExceptionVariable()));
+ tryCatch.setHandler(substitutor.block(templateTryCatch.getHandler()));
+ targetBlock.getTryCatchBlocks().add(tryCatch);
+ }
+
+ templateBlock.readAllInstructions(substitutor);
+ phiBlockMap.put(targetBlock, currentBlock());
+ }
+
+ for (int i = 0; i < template.basicBlockCount(); ++i) {
+ BasicBlock block = program.basicBlockAt(i == 0 ? startBlock : substitutor.blockOffset + i);
+ for (Phi phi : block.getPhis()) {
+ for (Incoming incoming : phi.getIncomings()) {
+ BasicBlock mappedBlock = phiBlockMap.get(incoming.getSource());
+ if (mappedBlock != null) {
+ incoming.setSource(mappedBlock);
+ }
+ }
+ }
+ }
+
+ phiBlockMap.clear();
+ blockIndex = substitutor.blockOffset + template.basicBlockCount();
+ }
+
+ public Variable getResultVar() {
+ return resultVar;
+ }
+
+ public BasicBlock currentBlock() {
+ return program.basicBlockAt(blockIndex);
+ }
+
+ void add(Instruction insn) {
+ insn.setLocation(forcedLocation != null ? forcedLocation : location);
+ program.basicBlockAt(blockIndex).getInstructions().add(insn);
+ }
+
+ Variable captureValue(CapturedValue captured) {
+ Object value = captured.obj;
+ if (value == null) {
+ NullConstantInstruction insn = new NullConstantInstruction();
+ insn.setReceiver(program.createVariable());
+ add(insn);
+ return insn.getReceiver();
+ } else if (value instanceof Integer) {
+ IntegerConstantInstruction insn = new IntegerConstantInstruction();
+ insn.setReceiver(program.createVariable());
+ insn.setConstant((Integer) value);
+ add(insn);
+ Variable result = insn.getReceiver();
+ if (!captured.primitive) {
+ result = box(result, ValueType.INTEGER);
+ }
+ return result;
+ } else if (value instanceof Long) {
+ LongConstantInstruction insn = new LongConstantInstruction();
+ insn.setReceiver(program.createVariable());
+ insn.setConstant((Long) value);
+ add(insn);
+ Variable result = insn.getReceiver();
+ if (!captured.primitive) {
+ result = box(result, ValueType.LONG);
+ }
+ return result;
+ } else if (value instanceof Float) {
+ FloatConstantInstruction insn = new FloatConstantInstruction();
+ insn.setReceiver(program.createVariable());
+ insn.setConstant((Float) value);
+ add(insn);
+ Variable result = insn.getReceiver();
+ if (!captured.primitive) {
+ result = box(result, ValueType.FLOAT);
+ }
+ return result;
+ } else if (value instanceof Double) {
+ DoubleConstantInstruction insn = new DoubleConstantInstruction();
+ insn.setReceiver(program.createVariable());
+ insn.setConstant((Double) value);
+ add(insn);
+ Variable result = insn.getReceiver();
+ if (!captured.primitive) {
+ result = box(result, ValueType.DOUBLE);
+ }
+ return result;
+ } else if (value instanceof String) {
+ StringConstantInstruction insn = new StringConstantInstruction();
+ insn.setReceiver(program.createVariable());
+ insn.setConstant((String) value);
+ add(insn);
+ return insn.getReceiver();
+ } else if (value instanceof ValueType) {
+ ClassConstantInstruction insn = new ClassConstantInstruction();
+ insn.setReceiver(program.createVariable());
+ insn.setConstant((ValueType) value);
+ add(insn);
+ return insn.getReceiver();
+ } else if (value instanceof Class>) {
+ ClassConstantInstruction insn = new ClassConstantInstruction();
+ insn.setReceiver(program.createVariable());
+ insn.setConstant(ValueType.parse((Class>) value));
+ add(insn);
+ return insn.getReceiver();
+ } else if (value instanceof ValueImpl) {
+ return varContext.emitVariable((ValueImpl>) value, new CallLocation(MetaprogrammingImpl.templateMethod,
+ location));
+ } else if (value instanceof LazyValueImpl) {
+ return lazy((LazyValueImpl>) value);
+ } else if (value instanceof ReflectFieldImpl) {
+ ReflectFieldImpl reflectField = (ReflectFieldImpl) value;
+ diagnostics.error(new CallLocation(MetaprogrammingImpl.templateMethod, location),
+ "Can't reference this ReflectField {{f0}} directly except for calling special "
+ + "methods on it", reflectField.field.getReference());
+ NullConstantInstruction insn = new NullConstantInstruction();
+ insn.setReceiver(program.createVariable());
+ add(insn);
+ return insn.getReceiver();
+ } else if (value instanceof ReflectMethodImpl) {
+ ReflectMethodImpl reflectMethod = (ReflectMethodImpl) value;
+ diagnostics.error(new CallLocation(MetaprogrammingImpl.templateMethod, location),
+ "Can't reference this ReflectMethod {{m0}} directly except for calling special methods on it",
+ reflectMethod.method.getReference());
+ NullConstantInstruction insn = new NullConstantInstruction();
+ insn.setReceiver(program.createVariable());
+ add(insn);
+ return insn.getReceiver();
+ } else if (value.getClass().getComponentType() != null) {
+ diagnostics.error(new CallLocation(MetaprogrammingImpl.templateMethod, location),
+ "Can't reference this array directly except for fetching by constant index");
+ NullConstantInstruction insn = new NullConstantInstruction();
+ insn.setReceiver(program.createVariable());
+ add(insn);
+ return insn.getReceiver();
+ } else {
+ diagnostics.error(new CallLocation(MetaprogrammingImpl.templateMethod, location), "Wrong captured value");
+ NullConstantInstruction insn = new NullConstantInstruction();
+ insn.setReceiver(program.createVariable());
+ add(insn);
+ return insn.getReceiver();
+ }
+ }
+
+ Variable lazy(LazyValueImpl> lazyImpl) {
+ CompositeMethodGenerator nestedGenerator = new CompositeMethodGenerator(varContext, program);
+ nestedGenerator.blockIndex = blockIndex;
+ nestedGenerator.location = location;
+ nestedGenerator.forcedLocation = lazyImpl.forcedLocation;
+ MetaprogrammingImpl.generator = nestedGenerator;
+ Value> result = lazyImpl.computation.compute();
+ blockIndex = nestedGenerator.blockIndex;
+ MetaprogrammingImpl.generator = this;
+
+ if (result instanceof ValueImpl) {
+ return ((ValueImpl>) result).innerValue;
+ } else if (result instanceof LazyValueImpl) {
+ return lazy((LazyValueImpl) result);
+ } else if (result != null) {
+ throw new IllegalStateException("Unknown value type: " + result.getClass().getName());
+ } else {
+ return null;
+ }
+ }
+
+ Variable box(Variable var, ValueType type) {
+ if (type instanceof ValueType.Primitive) {
+ switch (((ValueType.Primitive) type).getKind()) {
+ case BOOLEAN:
+ return box(var, boolean.class, Boolean.class);
+ case BYTE:
+ return box(var, byte.class, Byte.class);
+ case SHORT:
+ return box(var, short.class, Short.class);
+ case CHARACTER:
+ return box(var, char.class, Character.class);
+ case INTEGER:
+ return box(var, int.class, Integer.class);
+ case LONG:
+ return box(var, long.class, Long.class);
+ case FLOAT:
+ return box(var, float.class, Float.class);
+ case DOUBLE:
+ return box(var, double.class, Double.class);
+ }
+ }
+ return var;
+ }
+
+ private Variable box(Variable var, Class> primitive, Class> wrapper) {
+ InvokeInstruction insn = new InvokeInstruction();
+ insn.setMethod(new MethodReference(wrapper, "valueOf", primitive, wrapper));
+ insn.setType(InvocationType.SPECIAL);
+ insn.getArguments().add(var);
+ var = program.createVariable();
+ insn.setReceiver(var);
+ add(insn);
+ return var;
+ }
+
+ Variable unbox(Variable var, ValueType type) {
+ if (type instanceof ValueType.Primitive) {
+ switch (((ValueType.Primitive) type).getKind()) {
+ case BOOLEAN:
+ return unbox(var, boolean.class, Boolean.class);
+ case BYTE:
+ return unbox(var, byte.class, Byte.class);
+ case SHORT:
+ return unbox(var, short.class, Short.class);
+ case CHARACTER:
+ return unbox(var, char.class, Character.class);
+ case INTEGER:
+ return unbox(var, int.class, Integer.class);
+ case LONG:
+ return unbox(var, long.class, Long.class);
+ case FLOAT:
+ return unbox(var, float.class, Float.class);
+ case DOUBLE:
+ return unbox(var, double.class, Double.class);
+ }
+ } else if (!type.isObject(Object.class)) {
+ CastInstruction castInsn = new CastInstruction();
+ castInsn.setValue(var);
+ castInsn.setReceiver(program.createVariable());
+ castInsn.setTargetType(type);
+ var = castInsn.getReceiver();
+ add(castInsn);
+ }
+ return var;
+ }
+
+ private Variable unbox(Variable var, Class> primitive, Class> wrapper) {
+ CastInstruction castInsn = new CastInstruction();
+ castInsn.setValue(var);
+ castInsn.setReceiver(program.createVariable());
+ castInsn.setTargetType(ValueType.parse(wrapper));
+ add(castInsn);
+
+ InvokeInstruction insn = new InvokeInstruction();
+ insn.setMethod(new MethodReference(wrapper, primitive.getName() + "Value", primitive));
+ insn.setType(InvocationType.VIRTUAL);
+ insn.setInstance(castInsn.getReceiver());
+ var = program.createVariable();
+ insn.setReceiver(var);
+ add(insn);
+ return var;
+ }
+
+ public Program getProgram() {
+ return program;
+ }
+
+ private class TemplateSubstitutor implements InstructionReader {
+ private int blockOffset;
+ private int variableOffset;
+ int[] variableMapping;
+ CapturedValue[] capturedValues;
+ ArrayElement[] arrayElements;
+
+ TemplateSubstitutor(CapturedValue[] capturedValues, int[] variableMapping, ArrayElement[] arrayElements,
+ int blockOffset, int variableOffset) {
+ this.capturedValues = capturedValues;
+ this.variableMapping = variableMapping;
+ this.arrayElements = arrayElements;
+ this.blockOffset = blockOffset;
+ this.variableOffset = variableOffset;
+ }
+
+ @Override
+ public void location(InstructionLocation location) {
+ CompositeMethodGenerator.this.location = location;
+ }
+
+ @Override
+ public void nop() {
+ }
+
+ public Variable var(VariableReader variable) {
+ if (variable == null) {
+ return null;
+ }
+ int index = variableMapping[variable.getIndex()];
+ if (capturedValues[index] != null) {
+ return captureValue(capturedValues[index]);
+ }
+ ArrayElement elem = arrayElements[index];
+ if (elem != null) {
+ int arrayVar = variableMapping[elem.array];
+ if (capturedValues[arrayVar] != null) {
+ Object capturedArray = capturedValues[arrayVar].obj;
+ boolean primitive = capturedArray.getClass().getComponentType().isPrimitive();
+ return captureValue(new CapturedValue(Array.get(capturedArray, elem.index), primitive));
+ }
+ }
+ return program.variableAt(variableOffset + variable.getIndex());
+ }
+
+ public BasicBlock block(BasicBlockReader block) {
+ if (block == null) {
+ return null;
+ }
+ return program.basicBlockAt(blockOffset + block.getIndex());
+ }
+
+ @Override
+ public void classConstant(VariableReader receiver, ValueType cst) {
+ ClassConstantInstruction insn = new ClassConstantInstruction();
+ insn.setConstant(cst);
+ insn.setReceiver(var(receiver));
+ add(insn);
+ }
+
+ @Override
+ public void nullConstant(VariableReader receiver) {
+ NullConstantInstruction insn = new NullConstantInstruction();
+ insn.setReceiver(var(receiver));
+ add(insn);
+ }
+
+ @Override
+ public void integerConstant(VariableReader receiver, int cst) {
+ IntegerConstantInstruction insn = new IntegerConstantInstruction();
+ insn.setConstant(cst);
+ insn.setReceiver(var(receiver));
+ add(insn);
+ }
+
+ @Override
+ public void longConstant(VariableReader receiver, long cst) {
+ LongConstantInstruction insn = new LongConstantInstruction();
+ insn.setConstant(cst);
+ insn.setReceiver(var(receiver));
+ add(insn);
+ }
+
+ @Override
+ public void floatConstant(VariableReader receiver, float cst) {
+ FloatConstantInstruction insn = new FloatConstantInstruction();
+ insn.setConstant(cst);
+ insn.setReceiver(var(receiver));
+ add(insn);
+ }
+
+ @Override
+ public void doubleConstant(VariableReader receiver, double cst) {
+ DoubleConstantInstruction insn = new DoubleConstantInstruction();
+ insn.setConstant(cst);
+ insn.setReceiver(var(receiver));
+ add(insn);
+ }
+
+ @Override
+ public void stringConstant(VariableReader receiver, String cst) {
+ StringConstantInstruction insn = new StringConstantInstruction();
+ insn.setConstant(cst);
+ insn.setReceiver(var(receiver));
+ add(insn);
+ }
+
+ @Override
+ public void binary(BinaryOperation op, VariableReader receiver, VariableReader first, VariableReader second,
+ NumericOperandType type) {
+ BinaryInstruction insn = new BinaryInstruction(op, type);
+ insn.setReceiver(var(receiver));
+ insn.setFirstOperand(var(first));
+ insn.setSecondOperand(var(second));
+ add(insn);
+ }
+
+ @Override
+ public void negate(VariableReader receiver, VariableReader operand, NumericOperandType type) {
+ NegateInstruction insn = new NegateInstruction(type);
+ insn.setReceiver(var(receiver));
+ insn.setOperand(var(operand));
+ add(insn);
+ }
+
+ @Override
+ public void assign(VariableReader receiver, VariableReader assignee) {
+ int index = variableMapping[assignee.getIndex()];
+ if (capturedValues[index] != null) {
+ return;
+ }
+
+ AssignInstruction insn = new AssignInstruction();
+ insn.setReceiver(var(receiver));
+ insn.setAssignee(var(assignee));
+ if (insn.getReceiver() != insn.getAssignee()) {
+ add(insn);
+ }
+ }
+
+ @Override
+ public void cast(VariableReader receiver, VariableReader value, ValueType targetType) {
+ CastInstruction insn = new CastInstruction();
+ insn.setTargetType(targetType);
+ insn.setValue(var(value));
+ insn.setReceiver(var(receiver));
+ add(insn);
+ }
+
+ @Override
+ public void cast(VariableReader receiver, VariableReader value, NumericOperandType sourceType,
+ NumericOperandType targetType) {
+ CastNumberInstruction insn = new CastNumberInstruction(sourceType, targetType);
+ insn.setValue(var(value));
+ insn.setReceiver(var(receiver));
+ add(insn);
+ }
+
+ @Override
+ public void cast(VariableReader receiver, VariableReader value, IntegerSubtype type,
+ CastIntegerDirection targetType) {
+ CastIntegerInstruction insn = new CastIntegerInstruction(type, targetType);
+ insn.setValue(var(value));
+ insn.setReceiver(var(receiver));
+ add(insn);
+ }
+
+ @Override
+ public void jumpIf(BranchingCondition cond, VariableReader operand, BasicBlockReader consequent,
+ BasicBlockReader alternative) {
+ BranchingInstruction insn = new BranchingInstruction(cond);
+ insn.setOperand(var(operand));
+ insn.setConsequent(block(consequent));
+ insn.setAlternative(block(alternative));
+ add(insn);
+ }
+
+ @Override
+ public void jumpIf(BinaryBranchingCondition cond, VariableReader first, VariableReader second,
+ BasicBlockReader consequent, BasicBlockReader alternative) {
+ BinaryBranchingInstruction insn = new BinaryBranchingInstruction(cond);
+ insn.setFirstOperand(var(first));
+ insn.setSecondOperand(var(second));
+ insn.setConsequent(block(consequent));
+ insn.setAlternative(block(alternative));
+ add(insn);
+ }
+
+ @Override
+ public void jump(BasicBlockReader target) {
+ JumpInstruction insn = new JumpInstruction();
+ insn.setTarget(block(target));
+ add(insn);
+ }
+
+ @Override
+ public void choose(VariableReader condition, List extends SwitchTableEntryReader> table,
+ BasicBlockReader defaultTarget) {
+ SwitchInstruction insn = new SwitchInstruction();
+ insn.setCondition(var(condition));
+ insn.setDefaultTarget(block(defaultTarget));
+ for (SwitchTableEntryReader entry : table) {
+ SwitchTableEntry insnEntry = new SwitchTableEntry();
+ insnEntry.setCondition(entry.getCondition());
+ insnEntry.setTarget(block(entry.getTarget()));
+ insn.getEntries().add(insnEntry);
+ }
+ add(insn);
+ }
+
+ @Override
+ public void exit(VariableReader valueToReturn) {
+ BasicBlock target = program.basicBlockAt(returnBlockIndex);
+
+ if (valueToReturn != null) {
+ if (resultVar == null) {
+ resultVar = program.createVariable();
+ resultPhi = new Phi();
+ resultPhi.setReceiver(resultVar);
+ target.getPhis().add(resultPhi);
+ }
+ Incoming incoming = new Incoming();
+ incoming.setSource(program.basicBlockAt(blockIndex));
+ incoming.setValue(var(valueToReturn));
+ resultPhi.getIncomings().add(incoming);
+ }
+
+ JumpInstruction insn = new JumpInstruction();
+ insn.setTarget(target);
+ add(insn);
+ }
+
+ @Override
+ public void raise(VariableReader exception) {
+ RaiseInstruction insn = new RaiseInstruction();
+ insn.setException(var(exception));
+ add(insn);
+ }
+
+ @Override
+ public void createArray(VariableReader receiver, ValueType itemType, VariableReader size) {
+ ConstructArrayInstruction insn = new ConstructArrayInstruction();
+ insn.setReceiver(var(receiver));
+ insn.setItemType(itemType);
+ insn.setSize(var(size));
+ add(insn);
+ }
+
+ @Override
+ public void createArray(VariableReader receiver, ValueType itemType,
+ List extends VariableReader> dimensions) {
+ ConstructMultiArrayInstruction insn = new ConstructMultiArrayInstruction();
+ insn.setReceiver(var(receiver));
+ insn.setItemType(itemType);
+ insn.getDimensions().addAll(dimensions.stream().map(this::var).collect(Collectors.toList()));
+ add(insn);
+ }
+
+ @Override
+ public void create(VariableReader receiver, String type) {
+ ConstructInstruction insn = new ConstructInstruction();
+ insn.setReceiver(var(receiver));
+ insn.setType(type);
+ add(insn);
+ }
+
+ @Override
+ public void getField(VariableReader receiver, VariableReader instance, FieldReference field,
+ ValueType fieldType) {
+ GetFieldInstruction insn = new GetFieldInstruction();
+ insn.setField(field);
+ insn.setFieldType(fieldType);
+ insn.setInstance(var(instance));
+ insn.setReceiver(var(receiver));
+ add(insn);
+ }
+
+ @Override
+ public void putField(VariableReader instance, FieldReference field, VariableReader value,
+ ValueType fieldType) {
+ PutFieldInstruction insn = new PutFieldInstruction();
+ insn.setField(field);
+ insn.setFieldType(fieldType);
+ insn.setInstance(var(instance));
+ insn.setValue(var(value));
+ add(insn);
+ }
+
+ @Override
+ public void arrayLength(VariableReader receiver, VariableReader array) {
+ ArrayLengthInstruction insn = new ArrayLengthInstruction();
+ insn.setArray(var(array));
+ insn.setReceiver(var(receiver));
+ add(insn);
+ }
+
+ @Override
+ public void cloneArray(VariableReader receiver, VariableReader array) {
+ CloneArrayInstruction insn = new CloneArrayInstruction();
+ insn.setArray(var(array));
+ insn.setReceiver(var(receiver));
+ add(insn);
+ }
+
+ @Override
+ public void unwrapArray(VariableReader receiver, VariableReader array, ArrayElementType elementType) {
+ int arrayIndex = variableMapping[array.getIndex()];
+ if (capturedValues[arrayIndex] != null) {
+ return;
+ }
+
+ UnwrapArrayInstruction insn = new UnwrapArrayInstruction(elementType);
+ insn.setArray(var(array));
+ insn.setReceiver(var(receiver));
+ add(insn);
+ }
+
+ @Override
+ public void getElement(VariableReader receiver, VariableReader array, VariableReader index) {
+ int arrayIndex = variableMapping[array.getIndex()];
+
+ ArrayElement elem = arrayElements[receiver.getIndex()];
+ if (elem != null && capturedValues[arrayIndex] != null) {
+ AssignInstruction insn = new AssignInstruction();
+ insn.setAssignee(var(receiver));
+ insn.setReceiver(program.variableAt(variableOffset + receiver.getIndex()));
+ add(insn);
+ return;
+ }
+
+ GetElementInstruction insn = new GetElementInstruction();
+ insn.setArray(var(array));
+ insn.setIndex(var(index));
+ insn.setReceiver(var(receiver));
+ add(insn);
+ }
+
+ @Override
+ public void putElement(VariableReader array, VariableReader index, VariableReader value) {
+ PutElementInstruction insn = new PutElementInstruction();
+ insn.setArray(var(array));
+ insn.setIndex(var(index));
+ insn.setValue(var(value));
+ add(insn);
+ }
+
+ @Override
+ public void invoke(VariableReader receiver, VariableReader instance, MethodReference method,
+ List extends VariableReader> arguments, InvocationType type) {
+ if (type == InvocationType.VIRTUAL && instance != null) {
+ if (method.getClassName().equals(Value.class.getName())) {
+ if (method.getName().equals("get")) {
+ AssignInstruction insn = new AssignInstruction();
+ insn.setReceiver(var(receiver));
+ insn.setAssignee(var(instance));
+ add(insn);
+ return;
+ } else {
+ diagnostics.error(new CallLocation(MetaprogrammingImpl.templateMethod, location),
+ "Can't call method {{m0}} in runtime domain", method);
+ }
+ } else if (method.getClassName().equals(ReflectField.class.getName())) {
+ if (replaceFieldGetSet(receiver, instance, method, arguments)) {
+ return;
+ }
+ } else if (method.getClassName().equals(ReflectMethod.class.getName())) {
+ if (replaceMethodInvocation(receiver, instance, method, arguments)) {
+ return;
+ }
+ } else if (method.getClassName().equals(ReflectClass.class.getName())) {
+ if (replaceClassInvocation(receiver, instance, method, arguments)) {
+ return;
+ }
+ }
+ }
+ InvokeInstruction insn = new InvokeInstruction();
+ insn.setInstance(var(instance));
+ insn.setReceiver(var(receiver));
+ insn.setMethod(method);
+ insn.setType(type);
+ insn.getArguments().addAll(arguments.stream().map(this::var).collect(Collectors.toList()));
+ add(insn);
+ }
+
+ private boolean replaceFieldGetSet(VariableReader receiver, VariableReader instance, MethodReference method,
+ List extends VariableReader> arguments) {
+ int instanceIndex = variableMapping[instance.getIndex()];
+ if (capturedValues[instanceIndex] == null) {
+ diagnostics.error(new CallLocation(MetaprogrammingImpl.templateMethod, location), "Can call {{m0}}"
+ + " method only on a reflected field captured by lambda from outer context", method);
+ return false;
+ }
+
+ Object value = capturedValues[instanceIndex].obj;
+ if (!(value instanceof ReflectFieldImpl)) {
+ diagnostics.error(new CallLocation(MetaprogrammingImpl.templateMethod, location),
+ "Wrong call to {{m0}} method ", method);
+ return false;
+ }
+
+ ReflectFieldImpl field = (ReflectFieldImpl) value;
+ switch (method.getName()) {
+ case "get": {
+ Variable var = program.createVariable();
+ GetFieldInstruction insn = new GetFieldInstruction();
+ insn.setInstance(!field.field.hasModifier(ElementModifier.STATIC) ? var(arguments.get(0)) : null);
+ insn.setReceiver(var);
+ insn.setField(field.getBackingField().getReference());
+ insn.setFieldType(field.getBackingField().getType());
+ add(insn);
+
+ var = box(var, field.getBackingField().getType());
+
+ AssignInstruction assign = new AssignInstruction();
+ assign.setAssignee(var);
+ assign.setReceiver(var(receiver));
+ add(assign);
+
+ return true;
+ }
+ case "set": {
+ PutFieldInstruction insn = new PutFieldInstruction();
+ insn.setInstance(!field.field.hasModifier(ElementModifier.STATIC) ? var(arguments.get(0)) : null);
+ insn.setValue(unbox(var(arguments.get(1)), field.getBackingField().getType()));
+ insn.setField(field.getBackingField().getReference());
+ insn.setFieldType(field.getBackingField().getType());
+ add(insn);
+ return true;
+ }
+ default:
+ diagnostics.error(new CallLocation(MetaprogrammingImpl.templateMethod, location), "Can only "
+ + "call {{m0}} method from runtime domain", method);
+ return false;
+ }
+ }
+
+ private boolean replaceMethodInvocation(VariableReader receiver, VariableReader instance,
+ MethodReference method, List extends VariableReader> arguments) {
+ int instanceIndex = variableMapping[instance.getIndex()];
+ if (capturedValues[instanceIndex] == null) {
+ diagnostics.error(new CallLocation(MetaprogrammingImpl.templateMethod, location),
+ "Can call {{m0}} method only on a reflected field captured by lambda from outer context",
+ method);
+ return false;
+ }
+
+ Object value = capturedValues[instanceIndex].obj;
+ if (!(value instanceof ReflectMethodImpl)) {
+ diagnostics.error(new CallLocation(MetaprogrammingImpl.templateMethod, location),
+ "Wrong call to {{m0}} method ", method);
+ return false;
+ }
+
+ ReflectMethodImpl reflectMethod = (ReflectMethodImpl) value;
+ switch (method.getName()) {
+ case "invoke": {
+ InvokeInstruction insn = new InvokeInstruction();
+ insn.setInstance(!Modifier.isStatic(reflectMethod.getModifiers()) ? var(arguments.get(0)) : null);
+ insn.setType(Modifier.isStatic(reflectMethod.getModifiers()) ? InvocationType.SPECIAL
+ : InvocationType.VIRTUAL);
+ insn.setMethod(reflectMethod.method.getReference());
+ emitArguments(var(arguments.get(1)), reflectMethod, insn.getArguments());
+ add(insn);
+
+ if (receiver != null) {
+ if (reflectMethod.method.getResultType() == ValueType.VOID) {
+ NullConstantInstruction nullInsn = new NullConstantInstruction();
+ nullInsn.setReceiver(var(receiver));
+ add(nullInsn);
+ } else {
+ Variable var = program.createVariable();
+ insn.setReceiver(var);
+ var = box(var, reflectMethod.method.getResultType());
+
+ AssignInstruction assign = new AssignInstruction();
+ assign.setAssignee(var);
+ assign.setReceiver(var(receiver));
+ add(assign);
+ }
+ }
+
+ return true;
+ }
+ case "construct": {
+ ConstructInstruction constructInsn = new ConstructInstruction();
+ constructInsn.setReceiver(receiver != null ? var(receiver) : program.createVariable());
+ constructInsn.setType(reflectMethod.method.getOwnerName());
+ add(constructInsn);
+
+ InvokeInstruction insn = new InvokeInstruction();
+ insn.setInstance(constructInsn.getReceiver());
+ insn.setType(InvocationType.SPECIAL);
+ insn.setMethod(reflectMethod.method.getReference());
+ emitArguments(var(arguments.get(0)), reflectMethod, insn.getArguments());
+ add(insn);
+
+ return true;
+ }
+ default:
+ diagnostics.error(new CallLocation(MetaprogrammingImpl.templateMethod, location),
+ "Can only call {{m0}} method from runtime domain", method);
+ return false;
+ }
+ }
+
+ private boolean replaceClassInvocation(VariableReader receiver, VariableReader instance,
+ MethodReference method, List extends VariableReader> arguments) {
+ int instanceIndex = variableMapping[instance.getIndex()];
+ if (capturedValues[instanceIndex] == null) {
+ diagnostics.error(new CallLocation(MetaprogrammingImpl.templateMethod, location),
+ "Can call {{m0}} method only on a reflected class captured by lambda from outer context",
+ method);
+ return false;
+ }
+
+ Object value = capturedValues[instanceIndex].obj;
+ if (!(value instanceof ReflectClassImpl)) {
+ diagnostics.error(new CallLocation(MetaprogrammingImpl.templateMethod, location),
+ "Wrong call to {{m0}} method ", method);
+ return false;
+ }
+
+ ReflectClassImpl> reflectClass = (ReflectClassImpl>) value;
+ switch (method.getName()) {
+ case "isInstance": {
+ IsInstanceInstruction insn = new IsInstanceInstruction();
+ insn.setReceiver(receiver != null ? var(receiver) : program.createVariable());
+ insn.setValue(var(arguments.get(0)));
+ insn.setType(reflectClass.type);
+ add(insn);
+ return true;
+ }
+ case "cast": {
+ CastInstruction insn = new CastInstruction();
+ insn.setReceiver(receiver != null ? var(receiver) : program.createVariable());
+ insn.setValue(var(arguments.get(0)));
+ insn.setTargetType(reflectClass.type);
+ add(insn);
+ return true;
+ }
+ case "asJavaClass": {
+ ClassConstantInstruction insn = new ClassConstantInstruction();
+ insn.setReceiver(receiver != null ? var(receiver) : program.createVariable());
+ insn.setConstant(reflectClass.type);
+ add(insn);
+ return true;
+ }
+ case "createArray": {
+ ConstructArrayInstruction insn = new ConstructArrayInstruction();
+ insn.setItemType(reflectClass.type);
+ insn.setSize(var(arguments.get(0)));
+ insn.setReceiver(receiver != null ? var(receiver) : program.createVariable());
+ add(insn);
+ return true;
+ }
+ case "getArrayLength": {
+ ArrayLengthInstruction insn = new ArrayLengthInstruction();
+ insn.setArray(unwrapArray(reflectClass.type, var(arguments.get(0))));
+ insn.setReceiver(receiver != null ? var(receiver) : program.createVariable());
+ add(insn);
+ return true;
+ }
+ case "getArrayElement": {
+ GetElementInstruction insn = new GetElementInstruction();
+ insn.setArray(unwrapArray(reflectClass.type, var(arguments.get(0))));
+ insn.setIndex(var(arguments.get(1)));
+ insn.setReceiver(program.createVariable());
+ add(insn);
+
+ AssignInstruction assign = new AssignInstruction();
+ assign.setAssignee(box(insn.getReceiver(), ((ValueType.Array) reflectClass.type).getItemType()));
+ assign.setReceiver(receiver != null ? var(receiver) : program.createVariable());
+ add(assign);
+
+ return true;
+ }
+ default:
+ diagnostics.error(new CallLocation(MetaprogrammingImpl.templateMethod, location),
+ "Can only call {{m0}} method from runtime domain", method);
+ return false;
+ }
+ }
+
+ private void emitArguments(Variable argumentsVar, ReflectMethodImpl reflectMethod,
+ List arguments) {
+ UnwrapArrayInstruction unwrapInsn = new UnwrapArrayInstruction(ArrayElementType.OBJECT);
+ unwrapInsn.setArray(argumentsVar);
+ unwrapInsn.setReceiver(program.createVariable());
+ add(unwrapInsn);
+ argumentsVar = unwrapInsn.getReceiver();
+
+ for (int i = 0; i < reflectMethod.getParameterCount(); ++i) {
+ IntegerConstantInstruction indexInsn = new IntegerConstantInstruction();
+ indexInsn.setConstant(i);
+ indexInsn.setReceiver(program.createVariable());
+ add(indexInsn);
+
+ GetElementInstruction extractArgInsn = new GetElementInstruction();
+ extractArgInsn.setArray(argumentsVar);
+ extractArgInsn.setIndex(indexInsn.getReceiver());
+ extractArgInsn.setReceiver(program.createVariable());
+ add(extractArgInsn);
+
+ arguments.add(unbox(extractArgInsn.getReceiver(),
+ reflectMethod.method.parameterType(i)));
+ }
+ }
+
+ private Variable unwrapArray(ValueType type, Variable array) {
+ CastInstruction cast = new CastInstruction();
+ cast.setTargetType(ValueType.arrayOf(type));
+ cast.setValue(array);
+ cast.setReceiver(program.createVariable());
+ add(cast);
+
+ UnwrapArrayInstruction unwrap = new UnwrapArrayInstruction(asArrayType(type));
+ unwrap.setArray(cast.getReceiver());
+ unwrap.setReceiver(program.createVariable());
+ add(unwrap);
+
+ return unwrap.getReceiver();
+ }
+
+ private ArrayElementType asArrayType(ValueType type) {
+ if (type instanceof ValueType.Primitive) {
+ switch (((ValueType.Primitive) type).getKind()) {
+ case BOOLEAN:
+ case BYTE:
+ return ArrayElementType.BYTE;
+ case SHORT:
+ return ArrayElementType.SHORT;
+ case CHARACTER:
+ return ArrayElementType.CHAR;
+ case INTEGER:
+ return ArrayElementType.INT;
+ case LONG:
+ return ArrayElementType.LONG;
+ case FLOAT:
+ return ArrayElementType.FLOAT;
+ case DOUBLE:
+ return ArrayElementType.DOUBLE;
+ }
+ }
+ return ArrayElementType.OBJECT;
+ }
+
+ @Override
+ public void invokeDynamic(VariableReader receiver, VariableReader instance, MethodDescriptor method,
+ List extends VariableReader> arguments, MethodHandle bootstrapMethod,
+ List bootstrapArguments) {
+ InvokeDynamicInstruction insn = new InvokeDynamicInstruction();
+ insn.setBootstrapMethod(bootstrapMethod);
+ insn.setInstance(var(instance));
+ insn.setReceiver(var(receiver));
+ insn.setMethod(method);
+ insn.getArguments().addAll(arguments.stream().map(this::var).collect(Collectors.toList()));
+ insn.getBootstrapArguments().addAll(bootstrapArguments);
+ add(insn);
+ }
+
+ @Override
+ public void isInstance(VariableReader receiver, VariableReader value, ValueType type) {
+ IsInstanceInstruction insn = new IsInstanceInstruction();
+ insn.setReceiver(var(receiver));
+ insn.setValue(var(value));
+ insn.setType(type);
+ add(insn);
+ }
+
+ @Override
+ public void initClass(String className) {
+ InitClassInstruction insn = new InitClassInstruction();
+ insn.setClassName(className);
+ add(insn);
+ }
+
+ @Override
+ public void nullCheck(VariableReader receiver, VariableReader value) {
+ NullCheckInstruction insn = new NullCheckInstruction();
+ insn.setReceiver(var(receiver));
+ insn.setValue(var(value));
+ add(insn);
+ }
+
+ @Override
+ public void monitorEnter(VariableReader objectRef) {
+ MonitorEnterInstruction insn = new MonitorEnterInstruction();
+ insn.setObjectRef(var(objectRef));
+ add(insn);
+ }
+
+ @Override
+ public void monitorExit(VariableReader objectRef) {
+ MonitorExitInstruction insn = new MonitorExitInstruction();
+ insn.setObjectRef(var(objectRef));
+ add(insn);
+ }
+ }
+}
diff --git a/core/src/main/java/org/teavm/metaprogramming/impl/LazyValueImpl.java b/core/src/main/java/org/teavm/metaprogramming/impl/LazyValueImpl.java
new file mode 100644
index 000000000..35392fb24
--- /dev/null
+++ b/core/src/main/java/org/teavm/metaprogramming/impl/LazyValueImpl.java
@@ -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 implements Value {
+ boolean evaluated;
+ VariableContext context;
+ LazyComputation computation;
+ ValueType type;
+ InstructionLocation forcedLocation;
+
+ public LazyValueImpl(VariableContext context, LazyComputation 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");
+ }
+}
diff --git a/core/src/main/java/org/teavm/metaprogramming/impl/MetaprogrammingImpl.java b/core/src/main/java/org/teavm/metaprogramming/impl/MetaprogrammingImpl.java
index 9cca79a31..6f292733a 100644
--- a/core/src/main/java/org/teavm/metaprogramming/impl/MetaprogrammingImpl.java
+++ b/core/src/main/java/org/teavm/metaprogramming/impl/MetaprogrammingImpl.java
@@ -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 Value emit(Computation computation) {
- unsupported();
- return null;
+ if (computation instanceof ValueImpl>) {
+ @SuppressWarnings("unchecked")
+ ValueImpl valueImpl = (ValueImpl) 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 Value lazyFragment(LazyComputation computation) {
- unsupported();
- return null;
+ return lazyFragment(ValueType.object("java.lang.Object"), computation);
}
public static Value lazy(Computation computation) {
- unsupported();
- return null;
+ Fragment fragment = (Fragment) computation;
+ return lazyFragment(fragment.method.getReturnType(), () -> emit(computation));
}
- public static Scope currentScope() {
- unsupported();
- return null;
+ private static Value lazyFragment(ValueType type, LazyComputation 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 Value proxy(Class type, InvocationHandler 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());
+ }
+ };
}
diff --git a/metaprogramming-api/pom.xml b/metaprogramming-api/pom.xml
index 17925f49c..122af3be8 100644
--- a/metaprogramming-api/pom.xml
+++ b/metaprogramming-api/pom.xml
@@ -27,9 +27,19 @@
teavm-metaprogramming-api
+ bundle
+
TeaVM metaprogramming API
Declaration of interfaces and annotations for TeaVM metaprogramming
+
+
+ junit
+ junit
+ test
+
+
+
@@ -47,6 +57,17 @@
org.apache.maven.plugins
maven-javadoc-plugin
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ test-jar
+
+
+
+
org.apache.felix
maven-bundle-plugin
diff --git a/metaprogramming-api/src/main/java/org/teavm/metaprogramming/Metaprogramming.java b/metaprogramming-api/src/main/java/org/teavm/metaprogramming/Metaprogramming.java
index 09bbd1dd1..87074320a 100644
--- a/metaprogramming-api/src/main/java/org/teavm/metaprogramming/Metaprogramming.java
+++ b/metaprogramming-api/src/main/java/org/teavm/metaprogramming/Metaprogramming.java
@@ -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");
diff --git a/metaprogramming-api/src/main/java/org/teavm/metaprogramming/Scope.java b/metaprogramming-api/src/main/java/org/teavm/metaprogramming/Scope.java
deleted file mode 100644
index 192f4a96d..000000000
--- a/metaprogramming-api/src/main/java/org/teavm/metaprogramming/Scope.java
+++ /dev/null
@@ -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);
-}
diff --git a/metaprogramming-api/src/test/java/org/teavm/metaprogramming/test/MetaprogrammingTest.java b/metaprogramming-api/src/test/java/org/teavm/metaprogramming/test/MetaprogrammingTest.java
new file mode 100644
index 000000000..e769e90ea
--- /dev/null
+++ b/metaprogramming-api/src/test/java/org/teavm/metaprogramming/test/MetaprogrammingTest.java
@@ -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