From d5f5e2633b6593b256992873dacc3098dd94f298 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sun, 26 Jul 2015 15:46:49 +0300 Subject: [PATCH] InvokeDynamic support --- .../BootstrapMethodSubstitutor.java | 28 ++++ .../dependency/DataFlowGraphBuilder.java | 7 + .../teavm/dependency/DependencyChecker.java | 76 ++++------- .../dependency/DependencyClassSource.java | 14 +- .../dependency/DependencyGraphBuilder.java | 124 +++++++++++++++++- .../org/teavm/dependency/DynamicCallSite.java | 84 ++++++++++++ .../teavm/dependency/MethodDependency.java | 5 +- .../org/teavm/model/ClassReaderSource.java | 11 +- .../java/org/teavm/model/MethodHandle.java | 16 +-- .../org/teavm/model/emit/ProgramEmitter.java | 4 + .../model/util/InstructionStringifier.java | 6 +- .../org/teavm/model/util/ProgramUtils.java | 4 +- .../java/org/teavm/parsing/ProgramParser.java | 13 +- .../teavm/classlib/java/lang/LambdaTest.java | 31 +++++ 14 files changed, 341 insertions(+), 82 deletions(-) create mode 100644 teavm-core/src/main/java/org/teavm/dependency/BootstrapMethodSubstitutor.java create mode 100644 teavm-core/src/main/java/org/teavm/dependency/DynamicCallSite.java create mode 100644 teavm-tests/src/test/java/org/teavm/classlib/java/lang/LambdaTest.java diff --git a/teavm-core/src/main/java/org/teavm/dependency/BootstrapMethodSubstitutor.java b/teavm-core/src/main/java/org/teavm/dependency/BootstrapMethodSubstitutor.java new file mode 100644 index 000000000..d84b20445 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/dependency/BootstrapMethodSubstitutor.java @@ -0,0 +1,28 @@ +/* + * Copyright 2015 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.dependency; + +import org.teavm.model.emit.ProgramEmitter; +import org.teavm.model.emit.ValueEmitter; + +/** + * + * @author Alexey Andreev + */ +@FunctionalInterface +public interface BootstrapMethodSubstitutor { + ValueEmitter substitute(DynamicCallSite callSite, ProgramEmitter pe); +} diff --git a/teavm-core/src/main/java/org/teavm/dependency/DataFlowGraphBuilder.java b/teavm-core/src/main/java/org/teavm/dependency/DataFlowGraphBuilder.java index 572744465..3e52c364d 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DataFlowGraphBuilder.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DataFlowGraphBuilder.java @@ -307,6 +307,13 @@ public class DataFlowGraphBuilder implements InstructionReader { } } + @Override + public void invokeDynamic(VariableReader receiver, VariableReader instance, MethodDescriptor method, + List arguments, MethodHandle bootstrapMethod, + List bootstrapArguments) { + // Should be eliminated by bootstrap method substitutor + } + @Override public void isInstance(VariableReader receiver, VariableReader value, ValueType type) { } diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java index ae451caa2..7608ae471 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java @@ -15,7 +15,16 @@ */ package org.teavm.dependency; -import java.util.*; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; import org.teavm.callgraph.CallGraph; import org.teavm.callgraph.DefaultCallGraph; import org.teavm.callgraph.DefaultCallGraphNode; @@ -29,10 +38,12 @@ import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ClassReader; import org.teavm.model.ClassReaderSource; +import org.teavm.model.FieldHolder; import org.teavm.model.FieldReader; import org.teavm.model.FieldReference; import org.teavm.model.InstructionLocation; import org.teavm.model.MethodDescriptor; +import org.teavm.model.MethodHolder; import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; @@ -47,8 +58,8 @@ public class DependencyChecker implements DependencyInfo { private int classNameSuffix; private DependencyClassSource classSource; private ClassLoader classLoader; - private Mapper methodReaderCache; - private Mapper fieldReaderCache; + private Mapper methodReaderCache; + private Mapper fieldReaderCache; private CachedMapper methodCache; private CachedMapper fieldCache; private CachedMapper classCache; @@ -64,6 +75,7 @@ public class DependencyChecker implements DependencyInfo { private DependencyAgent agent; List nodes = new ArrayList<>(); List typeBitSets = new ArrayList<>(); + Map bootstrapMethodSubstitutors = new HashMap<>(); public DependencyChecker(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services, Diagnostics diagnostics) { @@ -71,10 +83,10 @@ public class DependencyChecker implements DependencyInfo { this.classSource = new DependencyClassSource(classSource, diagnostics); this.classLoader = classLoader; this.services = services; - methodReaderCache = new CachedMapper<>(preimage -> findMethodReader(preimage)); - fieldReaderCache = new CachedMapper<>(preimage -> findFieldReader(preimage)); + methodReaderCache = new CachedMapper<>(preimage -> this.classSource.resolveMutable(preimage)); + fieldReaderCache = new CachedMapper<>(preimage -> this.classSource.resolveMutable(preimage)); methodCache = new CachedMapper<>(preimage -> { - MethodReader method = methodReaderCache.map(preimage); + MethodHolder method = methodReaderCache.map(preimage); if (method != null && !method.getReference().equals(preimage)) { return methodCache.map(method.getReference()); } @@ -268,53 +280,7 @@ public class DependencyChecker implements DependencyInfo { } } - private MethodReader findMethodReader(MethodReference methodRef) { - String clsName = methodRef.getClassName(); - MethodDescriptor desc = methodRef.getDescriptor(); - ClassReader cls = classSource.get(clsName); - if (cls == null) { - return null; - } - MethodReader reader = cls.getMethod(desc); - if (reader != null) { - return reader; - } - if (cls.getParent() != null && cls.getParent().equals(cls.getParent())) { - reader = methodReaderCache.map(new MethodReference(cls.getParent(), desc)); - if (reader != null) { - return reader; - } - } - for (String ifaceName : cls.getInterfaces()) { - reader = methodReaderCache.map(new MethodReference(ifaceName, desc)); - if (reader != null) { - return reader; - } - } - return null; - } - - private FieldReader findFieldReader(FieldReference fieldRef) { - String clsName = fieldRef.getClassName(); - String name = fieldRef.getFieldName(); - while (clsName != null) { - ClassReader cls = classSource.get(clsName); - if (cls == null) { - return null; - } - FieldReader field = cls.getField(name); - if (field != null) { - return field; - } - if (clsName.equals(cls.getParent())) { - break; - } - clsName = cls.getParent(); - } - return null; - } - - private MethodDependency createMethodDep(MethodReference methodRef, MethodReader method) { + private MethodDependency createMethodDep(MethodReference methodRef, MethodHolder method) { ValueType[] arguments = methodRef.getParameterTypes(); int paramCount = arguments.length + 1; DependencyNode[] parameterNodes = new DependencyNode[arguments.length + 1]; @@ -482,4 +448,8 @@ public class DependencyChecker implements DependencyInfo { public CallGraph getCallGraph() { return callGraph; } + + public void addBootstrapMethodSubstitutor(MethodReference method, BootstrapMethodSubstitutor substitutor) { + bootstrapMethodSubstitutors.put(method, substitutor); + } } diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyClassSource.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyClassSource.java index 8e90d5dc8..0f85399c5 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyClassSource.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyClassSource.java @@ -20,9 +20,9 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.teavm.common.CachedMapper; import org.teavm.diagnostics.Diagnostics; import org.teavm.model.ClassHolder; +import org.teavm.model.ClassHolderSource; import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ClassReader; import org.teavm.model.ClassReaderSource; @@ -32,12 +32,12 @@ import org.teavm.model.util.ModelUtils; * * @author Alexey Andreev */ -class DependencyClassSource implements ClassReaderSource { +class DependencyClassSource implements ClassHolderSource { private ClassReaderSource innerSource; private Diagnostics diagnostics; private Map generatedClasses = new HashMap<>(); private List transformers = new ArrayList<>(); - private CachedMapper cache = new CachedMapper<>(preimage -> findAndTransformClass(preimage)); + private Map cache = new HashMap<>(); public DependencyClassSource(ClassReaderSource innerSource, Diagnostics diagnostics) { this.innerSource = innerSource; @@ -45,8 +45,8 @@ class DependencyClassSource implements ClassReaderSource { } @Override - public ClassReader get(String name) { - return cache.map(name); + public ClassHolder get(String name) { + return cache.computeIfAbsent(name, n -> findAndTransformClass(n)); } public void submit(ClassHolder cls) { @@ -54,10 +54,10 @@ class DependencyClassSource implements ClassReaderSource { throw new IllegalArgumentException("Class " + cls.getName() + " is already defined"); } generatedClasses.put(cls.getName(), cls); - cache.invalidate(cls.getName()); + cache.remove(cls.getName()); } - private ClassReader findAndTransformClass(String name) { + private ClassHolder findAndTransformClass(String name) { ClassHolder cls = findClass(name); if (cls != null && !transformers.isEmpty()) { for (ClassHolderTransformer transformer : transformers) { diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java index 79f4191db..c02e6f54c 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -20,9 +20,45 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import org.teavm.callgraph.DefaultCallGraphNode; -import org.teavm.model.*; -import org.teavm.model.instructions.*; +import org.teavm.model.BasicBlock; +import org.teavm.model.BasicBlockReader; +import org.teavm.model.CallLocation; +import org.teavm.model.ClassReader; +import org.teavm.model.ClassReaderSource; +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.MethodHolder; +import org.teavm.model.MethodReference; +import org.teavm.model.Phi; +import org.teavm.model.PhiReader; +import org.teavm.model.Program; +import org.teavm.model.RuntimeConstant; +import org.teavm.model.TryCatchBlockReader; +import org.teavm.model.ValueType; +import org.teavm.model.VariableReader; +import org.teavm.model.emit.ProgramEmitter; +import org.teavm.model.emit.ValueEmitter; +import org.teavm.model.instructions.ArrayElementType; +import org.teavm.model.instructions.AssignInstruction; +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.NullConstantInstruction; +import org.teavm.model.instructions.NumericOperandType; +import org.teavm.model.instructions.SwitchTableEntryReader; import org.teavm.model.util.ListingBuilder; /** @@ -33,7 +69,7 @@ class DependencyGraphBuilder { private DependencyChecker dependencyChecker; private DependencyNode[] nodes; private DependencyNode resultNode; - private ProgramReader program; + private Program program; private DefaultCallGraphNode caller; private InstructionLocation currentLocation; private ExceptionConsumer currentExceptionConsumer; @@ -44,13 +80,15 @@ class DependencyGraphBuilder { public void buildGraph(MethodDependency dep) { caller = dependencyChecker.callGraph.getNode(dep.getReference()); - MethodReader method = dep.getMethod(); + MethodHolder method = dep.method; if (method.getProgram() == null || method.getProgram().basicBlockCount() == 0) { return; } program = method.getProgram(); resultNode = dep.getResult(); + processInvokeDynamic(); + DataFlowGraphBuilder dfgBuilder = new DataFlowGraphBuilder(); boolean[] significantParams = new boolean[dep.getParameterCount()]; significantParams[0] = true; @@ -146,6 +184,77 @@ class DependencyGraphBuilder { } } + private void processInvokeDynamic() { + if (program == null) { + return; + } + System.out.println(new ListingBuilder().buildListing(program, "")); + ProgramEmitter pe = ProgramEmitter.create(program); + for (int i = 0; i < program.basicBlockCount(); ++i) { + BasicBlock block = program.basicBlockAt(i); + for (int j = 0; j < block.getInstructions().size(); ++j) { + Instruction insn = block.getInstructions().get(j); + if (!(insn instanceof InvokeDynamicInstruction)) { + continue; + } + InvokeDynamicInstruction indy = (InvokeDynamicInstruction) insn; + MethodReference bootstrapMethod = new MethodReference(indy.getBootstrapMethod().getClassName(), + indy.getBootstrapMethod().getName(), indy.getBootstrapMethod().signature()); + BootstrapMethodSubstitutor substitutor = dependencyChecker.bootstrapMethodSubstitutors + .get(bootstrapMethod); + if (substitutor == null) { + NullConstantInstruction nullInsn = new NullConstantInstruction(); + nullInsn.setReceiver(indy.getReceiver()); + nullInsn.setLocation(indy.getLocation()); + block.getInstructions().set(j, nullInsn); + CallLocation location = new CallLocation(bootstrapMethod, indy.getLocation()); + dependencyChecker.getDiagnostics().error(location, "Substitutor for this dependency method " + + "was not found"); + continue; + } + + BasicBlock splitBlock = program.createBasicBlock(); + List splitInstructions = block.getInstructions().subList(j + 1, + block.getInstructions().size()); + splitBlock.getInstructions().addAll(splitInstructions); + splitInstructions.clear(); + + for (int k = 0; k < program.basicBlockCount() - 1; ++k) { + BasicBlock replaceBlock = program.basicBlockAt(k); + for (Phi phi : replaceBlock.getPhis()) { + for (Incoming incoming : phi.getIncomings()) { + if (incoming.getSource() == block) { + incoming.setSource(splitBlock); + } + } + } + } + + pe.setBlock(block); + pe.setCurrentLocation(indy.getLocation()); + block.getInstructions().remove(j); + + DynamicCallSite callSite = new DynamicCallSite( + indy.getMethod(), + indy.getInstance() != null ? pe.var(indy.getInstance()) : null, + indy.getArguments().stream().map(arg -> pe.var(arg)).collect(Collectors.toList()), + indy.getBootstrapMethod(), + indy.getBootstrapArguments(), + dependencyChecker.getAgent(), + indy.getInstance() != null ? nodes[indy.getInstance().getIndex()] : null, + indy.getArguments().stream().map(arg -> nodes[arg.getIndex()]).collect(Collectors.toList())); + ValueEmitter result = substitutor.substitute(callSite, pe); + if (result.getVariable() != null && result.getVariable() != indy.getReceiver()) { + AssignInstruction assign = new AssignInstruction(); + assign.setAssignee(result.getVariable()); + assign.setReceiver(indy.getReceiver()); + pe.addInstruction(insn); + } + pe.jump(splitBlock); + } + } + } + private ExceptionConsumer createExceptionConsumer(MethodDependency methodDep, BasicBlockReader block) { List tryCatchBlocks = block.readTryCatchBlocks(); ClassReader[] exceptions = new ClassReader[tryCatchBlocks.size()]; @@ -613,6 +722,13 @@ class DependencyGraphBuilder { } } + @Override + public void invokeDynamic(VariableReader receiver, VariableReader instance, MethodDescriptor method, + List arguments, MethodHandle bootstrapMethod, + List bootstrapArguments) { + // Should be eliminated by processInvokeDynamic method + } + @Override public void initClass(final String className) { CallLocation callLocation = new CallLocation(caller.getMethod(), currentLocation); diff --git a/teavm-core/src/main/java/org/teavm/dependency/DynamicCallSite.java b/teavm-core/src/main/java/org/teavm/dependency/DynamicCallSite.java new file mode 100644 index 000000000..35b9f0113 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/dependency/DynamicCallSite.java @@ -0,0 +1,84 @@ +/* + * Copyright 2015 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.dependency; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.teavm.model.MethodDescriptor; +import org.teavm.model.MethodHandle; +import org.teavm.model.RuntimeConstant; +import org.teavm.model.emit.ValueEmitter; + +/** + * + * @author Alexey Andreev + */ +public class DynamicCallSite { + private MethodDescriptor calledMethod; + private ValueEmitter instance; + private List arguments; + private MethodHandle bootstrapMethod; + private List bootstrapArguments; + private DependencyAgent agent; + private DependencyNode instanceNode; + private List argumentNodes = new ArrayList<>(); + + DynamicCallSite(MethodDescriptor calledMethod, ValueEmitter instance, List arguments, + MethodHandle bootstrapMethod, List bootstrapArguments, DependencyAgent agent, + DependencyNode instanceNode, List argumentNodes) { + this.calledMethod = calledMethod; + this.instance = instance; + this.arguments = Collections.unmodifiableList(new ArrayList<>(arguments)); + this.bootstrapMethod = bootstrapMethod; + this.bootstrapArguments = Collections.unmodifiableList(new ArrayList<>(bootstrapArguments)); + this.agent = agent; + this.instanceNode = instanceNode; + this.argumentNodes = Collections.unmodifiableList(new ArrayList<>(argumentNodes)); + } + + public MethodDescriptor getCalledMethod() { + return calledMethod; + } + + public MethodHandle getBootstrapMethod() { + return bootstrapMethod; + } + + public List getArguments() { + return arguments; + } + + public List getBootstrapArguments() { + return bootstrapArguments; + } + + public DependencyAgent getAgent() { + return agent; + } + + public ValueEmitter getInstance() { + return instance; + } + + public DependencyNode getInstanceNode() { + return instanceNode; + } + + public List getArgumentNodes() { + return argumentNodes; + } +} diff --git a/teavm-core/src/main/java/org/teavm/dependency/MethodDependency.java b/teavm-core/src/main/java/org/teavm/dependency/MethodDependency.java index 4b1b05931..7fb7a38c1 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/MethodDependency.java +++ b/teavm-core/src/main/java/org/teavm/dependency/MethodDependency.java @@ -16,6 +16,7 @@ package org.teavm.dependency; import java.util.Arrays; +import org.teavm.model.MethodHolder; import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; @@ -29,14 +30,14 @@ public class MethodDependency implements MethodDependencyInfo { private int parameterCount; private DependencyNode resultNode; private DependencyNode thrown; - private MethodReader method; + MethodHolder method; private MethodReference reference; private boolean used; DependencyPlugin dependencyPlugin; boolean dependencyPluginAttached; MethodDependency(DependencyChecker dependencyChecker, DependencyNode[] variableNodes, int parameterCount, - DependencyNode resultNode, DependencyNode thrown, MethodReader method, MethodReference reference) { + DependencyNode resultNode, DependencyNode thrown, MethodHolder method, MethodReference reference) { this.dependencyChecker = dependencyChecker; this.variableNodes = Arrays.copyOf(variableNodes, variableNodes.length); this.parameterCount = parameterCount; diff --git a/teavm-core/src/main/java/org/teavm/model/ClassReaderSource.java b/teavm-core/src/main/java/org/teavm/model/ClassReaderSource.java index fc6291772..a98f6de9f 100644 --- a/teavm-core/src/main/java/org/teavm/model/ClassReaderSource.java +++ b/teavm-core/src/main/java/org/teavm/model/ClassReaderSource.java @@ -15,7 +15,11 @@ */ package org.teavm.model; -import java.util.*; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; import java.util.stream.Stream; import java.util.stream.StreamSupport; @@ -52,6 +56,7 @@ public interface ClassReaderSource { private Deque> state = new ArrayDeque<>(); private Set visited = new HashSet<>(); { + state.push(new ArrayDeque<>()); add(name); } @Override public ClassReader next() { @@ -89,14 +94,14 @@ public interface ClassReaderSource { } default MethodReader resolve(MethodReference method) { - return getAncestorClasses(method.getClassName()) + return getAncestors(method.getClassName()) .map(cls -> cls.getMethod(method.getDescriptor())) .filter(candidate -> candidate != null) .findFirst().orElse(null); } default FieldReader resolve(FieldReference field) { - return getAncestorClasses(field.getClassName()) + return getAncestors(field.getClassName()) .map(cls -> cls.getField(field.getFieldName())) .filter(candidate -> candidate != null) .findFirst().orElse(null); diff --git a/teavm-core/src/main/java/org/teavm/model/MethodHandle.java b/teavm-core/src/main/java/org/teavm/model/MethodHandle.java index 21afb3a35..f311497ba 100644 --- a/teavm-core/src/main/java/org/teavm/model/MethodHandle.java +++ b/teavm-core/src/main/java/org/teavm/model/MethodHandle.java @@ -117,8 +117,8 @@ public class MethodHandle { } public static MethodHandle staticCaller(String className, String name, ValueType... arguments) { - ValueType valueType = arguments[0]; - arguments = Arrays.copyOfRange(arguments, 1, arguments.length); + ValueType valueType = arguments[arguments.length - 1]; + arguments = Arrays.copyOfRange(arguments, 0, arguments.length - 1); return new MethodHandle(MethodHandleType.INVOKE_STATIC, className, name, valueType, arguments); } @@ -131,8 +131,8 @@ public class MethodHandle { } public static MethodHandle specialCaller(String className, String name, ValueType... arguments) { - ValueType valueType = arguments[0]; - arguments = Arrays.copyOfRange(arguments, 1, arguments.length); + ValueType valueType = arguments[arguments.length - 1]; + arguments = Arrays.copyOfRange(arguments, 0, arguments.length - 1); return new MethodHandle(MethodHandleType.INVOKE_SPECIAL, className, name, valueType, arguments); } @@ -145,8 +145,8 @@ public class MethodHandle { } public static MethodHandle constructorCaller(String className, String name, ValueType... arguments) { - ValueType valueType = arguments[0]; - arguments = Arrays.copyOfRange(arguments, 1, arguments.length); + ValueType valueType = arguments[arguments.length - 1]; + arguments = Arrays.copyOfRange(arguments, 0, arguments.length - 1); return new MethodHandle(MethodHandleType.INVOKE_CONSTRUCTOR, className, name, valueType, arguments); } @@ -159,8 +159,8 @@ public class MethodHandle { } public static MethodHandle interfaceCaller(String className, String name, ValueType... arguments) { - ValueType valueType = arguments[0]; - arguments = Arrays.copyOfRange(arguments, 1, arguments.length); + ValueType valueType = arguments[arguments.length - 1]; + arguments = Arrays.copyOfRange(arguments, 0, arguments.length - 1); return new MethodHandle(MethodHandleType.INVOKE_INTERFACE, className, name, valueType, arguments); } diff --git a/teavm-core/src/main/java/org/teavm/model/emit/ProgramEmitter.java b/teavm-core/src/main/java/org/teavm/model/emit/ProgramEmitter.java index bc74736b4..07786c1ac 100644 --- a/teavm-core/src/main/java/org/teavm/model/emit/ProgramEmitter.java +++ b/teavm-core/src/main/java/org/teavm/model/emit/ProgramEmitter.java @@ -258,4 +258,8 @@ public final class ProgramEmitter { return new ProgramEmitter(program, block); } + + public static ProgramEmitter create(Program program) { + return new ProgramEmitter(program, null); + } } diff --git a/teavm-core/src/main/java/org/teavm/model/util/InstructionStringifier.java b/teavm-core/src/main/java/org/teavm/model/util/InstructionStringifier.java index 475d94c53..18a5695e7 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/InstructionStringifier.java +++ b/teavm-core/src/main/java/org/teavm/model/util/InstructionStringifier.java @@ -346,8 +346,10 @@ public class InstructionStringifier implements InstructionReader { if (receiver != null) { sb.append("@").append(receiver.getIndex()).append(" := "); } - sb.append("@").append(instance.getIndex()); - sb.append(".").append(method.getName()).append("("); + if (instance != null) { + sb.append("@").append(instance.getIndex()).append("."); + } + sb.append(method.getName()).append("("); sb.append(arguments.stream().map(arg -> "@" + arg.getIndex()).collect(Collectors.joining(", "))); sb.append(") "); sb.append("[").append(convert(bootstrapMethod)).append('('); diff --git a/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java b/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java index 1c4fe6406..8107ebe96 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java +++ b/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java @@ -480,7 +480,9 @@ public final class ProgramUtils { insnCopy.setMethod(method); insnCopy.setBootstrapMethod(bootstrapMethod); insnCopy.getBootstrapArguments().addAll(bootstrapArguments); - insnCopy.setInstance(copyVar(instance)); + if (instance != null) { + insnCopy.setInstance(copyVar(instance)); + } insnCopy.getArguments().addAll(arguments.stream().map(v -> copyVar(v)).collect(Collectors.toList())); insnCopy.setReceiver(receiver != null ? copyVar(receiver) : null); copy = insnCopy; diff --git a/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java b/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java index c0427e946..19a4495ae 100644 --- a/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java +++ b/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java @@ -487,8 +487,18 @@ public class ProgramParser implements VariableDebugInformation { @Override public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { InvokeDynamicInstruction insn = new InvokeDynamicInstruction(); + insn.setBootstrapMethod(parseHandle(bsm)); - insn.setInstance(getVariable(popSingle())); + switch (insn.getBootstrapMethod().getKind()) { + case GET_STATIC_FIELD: + case PUT_STATIC_FIELD: + case INVOKE_STATIC: + case INVOKE_CONSTRUCTOR: + break; + default: + insn.setInstance(getVariable(popSingle())); + break; + } Type[] types = Type.getArgumentTypes(desc); Variable[] args = new Variable[types.length]; int j = args.length; @@ -503,7 +513,6 @@ public class ProgramParser implements VariableDebugInformation { } insn.setMethod(new MethodDescriptor(name, MethodDescriptor.parseSignature(desc))); - insn.setBootstrapMethod(parseHandle(bsm)); for (int i = 0; i < bsmArgs.length; ++i) { insn.getBootstrapArguments().add(convertConstant(bsmArgs[i])); } diff --git a/teavm-tests/src/test/java/org/teavm/classlib/java/lang/LambdaTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/lang/LambdaTest.java new file mode 100644 index 000000000..d96117932 --- /dev/null +++ b/teavm-tests/src/test/java/org/teavm/classlib/java/lang/LambdaTest.java @@ -0,0 +1,31 @@ +package org.teavm.classlib.java.lang; + +import static org.junit.Assert.assertArrayEquals; +import java.lang.reflect.Array; +import org.junit.Test; + +/** + * + * @author Alexey Andreev + */ +public class LambdaTest { + @Test + public void lambdaWorks() { + Integer[] src = { 1, 2, 3 }; + Integer[] array = map(src, (Integer n) -> n * 2 + src[0]); + assertArrayEquals(new Integer[] { 3, 5, 7 }, array); + } + + static T[] map(T[] array, Function f) { + @SuppressWarnings("unchecked") + T[] result = (T[])Array.newInstance(array.getClass().getComponentType(), array.length); + for (int i = 0; i < result.length; ++i) { + result[i] = f.apply(array[i]); + } + return result; + } + + interface Function { + T apply(T value); + } +}