InvokeDynamic support

This commit is contained in:
Alexey Andreev 2015-07-26 15:46:49 +03:00
parent 50b42ab092
commit d5f5e2633b
14 changed files with 341 additions and 82 deletions

View File

@ -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);
}

View File

@ -307,6 +307,13 @@ public class DataFlowGraphBuilder implements InstructionReader {
}
}
@Override
public void invokeDynamic(VariableReader receiver, VariableReader instance, MethodDescriptor method,
List<? extends VariableReader> arguments, MethodHandle bootstrapMethod,
List<RuntimeConstant> bootstrapArguments) {
// Should be eliminated by bootstrap method substitutor
}
@Override
public void isInstance(VariableReader receiver, VariableReader value, ValueType type) {
}

View File

@ -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<MethodReference, MethodReader> methodReaderCache;
private Mapper<FieldReference, FieldReader> fieldReaderCache;
private Mapper<MethodReference, MethodHolder> methodReaderCache;
private Mapper<FieldReference, FieldHolder> fieldReaderCache;
private CachedMapper<MethodReference, MethodDependency> methodCache;
private CachedMapper<FieldReference, FieldDependency> fieldCache;
private CachedMapper<String, ClassDependency> classCache;
@ -64,6 +75,7 @@ public class DependencyChecker implements DependencyInfo {
private DependencyAgent agent;
List<DependencyNode> nodes = new ArrayList<>();
List<BitSet> typeBitSets = new ArrayList<>();
Map<MethodReference, BootstrapMethodSubstitutor> 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);
}
}

View File

@ -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<String, ClassHolder> generatedClasses = new HashMap<>();
private List<ClassHolderTransformer> transformers = new ArrayList<>();
private CachedMapper<String, ClassReader> cache = new CachedMapper<>(preimage -> findAndTransformClass(preimage));
private Map<String, ClassHolder> 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) {

View File

@ -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<Instruction> 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<? extends TryCatchBlockReader> 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<? extends VariableReader> arguments, MethodHandle bootstrapMethod,
List<RuntimeConstant> bootstrapArguments) {
// Should be eliminated by processInvokeDynamic method
}
@Override
public void initClass(final String className) {
CallLocation callLocation = new CallLocation(caller.getMethod(), currentLocation);

View File

@ -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<ValueEmitter> arguments;
private MethodHandle bootstrapMethod;
private List<RuntimeConstant> bootstrapArguments;
private DependencyAgent agent;
private DependencyNode instanceNode;
private List<DependencyNode> argumentNodes = new ArrayList<>();
DynamicCallSite(MethodDescriptor calledMethod, ValueEmitter instance, List<ValueEmitter> arguments,
MethodHandle bootstrapMethod, List<RuntimeConstant> bootstrapArguments, DependencyAgent agent,
DependencyNode instanceNode, List<DependencyNode> 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<ValueEmitter> getArguments() {
return arguments;
}
public List<RuntimeConstant> getBootstrapArguments() {
return bootstrapArguments;
}
public DependencyAgent getAgent() {
return agent;
}
public ValueEmitter getInstance() {
return instance;
}
public DependencyNode getInstanceNode() {
return instanceNode;
}
public List<DependencyNode> getArgumentNodes() {
return argumentNodes;
}
}

View File

@ -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;

View File

@ -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<Deque<ClassReader>> state = new ArrayDeque<>();
private Set<ClassReader> 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);

View File

@ -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);
}

View File

@ -258,4 +258,8 @@ public final class ProgramEmitter {
return new ProgramEmitter(program, block);
}
public static ProgramEmitter create(Program program) {
return new ProgramEmitter(program, null);
}
}

View File

@ -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('(');

View File

@ -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;

View File

@ -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]));
}

View File

@ -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> T[] map(T[] array, Function<T> 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> {
T apply(T value);
}
}