Anonymous object transformation
This commit is contained in:
@@ -19,17 +19,13 @@ package org.jetbrains.jet.codegen.inline;
|
|||||||
import com.intellij.openapi.util.Pair;
|
import com.intellij.openapi.util.Pair;
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.jetbrains.jet.OutputFile;
|
import org.jetbrains.jet.OutputFile;
|
||||||
import org.jetbrains.jet.codegen.*;
|
import org.jetbrains.jet.codegen.*;
|
||||||
import org.jetbrains.jet.codegen.state.GenerationState;
|
import org.jetbrains.jet.codegen.state.GenerationState;
|
||||||
import org.jetbrains.jet.codegen.state.JetTypeMapper;
|
import org.jetbrains.jet.codegen.state.JetTypeMapper;
|
||||||
import org.jetbrains.org.objectweb.asm.*;
|
import org.jetbrains.org.objectweb.asm.*;
|
||||||
import org.jetbrains.org.objectweb.asm.commons.Method;
|
import org.jetbrains.org.objectweb.asm.commons.Method;
|
||||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode;
|
import org.jetbrains.org.objectweb.asm.tree.*;
|
||||||
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode;
|
|
||||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode;
|
|
||||||
import org.jetbrains.org.objectweb.asm.tree.VarInsnNode;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -42,44 +38,39 @@ public class LambdaTransformer {
|
|||||||
|
|
||||||
protected final JetTypeMapper typeMapper;
|
protected final JetTypeMapper typeMapper;
|
||||||
|
|
||||||
private final MethodNode constructor;
|
private MethodNode constructor;
|
||||||
|
|
||||||
private final MethodNode invoke;
|
|
||||||
|
|
||||||
private final MethodNode bridge;
|
|
||||||
|
|
||||||
private final InliningContext inliningContext;
|
private final InliningContext inliningContext;
|
||||||
|
|
||||||
private final Type oldLambdaType;
|
private final Type oldObjectType;
|
||||||
|
|
||||||
private final Type newLambdaType;
|
private final Type newLambdaType;
|
||||||
|
|
||||||
private int classAccess;
|
private final ClassReader reader;
|
||||||
private String signature;
|
|
||||||
private String superName;
|
private String superName;
|
||||||
private String[] interfaces;
|
|
||||||
private final boolean isSameModule;
|
private final boolean isSameModule;
|
||||||
|
|
||||||
private Map<String, List<String>> fieldNames = new HashMap<String, List<String>>();
|
private final Map<String, List<String>> fieldNames = new HashMap<String, List<String>>();
|
||||||
|
|
||||||
public LambdaTransformer(String lambdaInternalName, InliningContext inliningContext, boolean isSameModule, Type newLambdaType) {
|
public LambdaTransformer(@NotNull String objectInternalName, @NotNull InliningContext inliningContext, boolean isSameModule, @NotNull Type newLambdaType) {
|
||||||
this.isSameModule = isSameModule;
|
this.isSameModule = isSameModule;
|
||||||
this.state = inliningContext.state;
|
this.state = inliningContext.state;
|
||||||
this.typeMapper = state.getTypeMapper();
|
this.typeMapper = state.getTypeMapper();
|
||||||
this.inliningContext = inliningContext;
|
this.inliningContext = inliningContext;
|
||||||
this.oldLambdaType = Type.getObjectType(lambdaInternalName);
|
this.oldObjectType = Type.getObjectType(objectInternalName);
|
||||||
this.newLambdaType = newLambdaType;
|
this.newLambdaType = newLambdaType;
|
||||||
|
|
||||||
//try to find just compiled classes then in dependencies
|
//try to find just compiled classes then in dependencies
|
||||||
ClassReader reader;
|
|
||||||
try {
|
try {
|
||||||
OutputFile outputFile = state.getFactory().get(lambdaInternalName + ".class");
|
OutputFile outputFile = state.getFactory().get(objectInternalName + ".class");
|
||||||
if (outputFile != null) {
|
if (outputFile != null) {
|
||||||
reader = new ClassReader(outputFile.asByteArray());
|
reader = new ClassReader(outputFile.asByteArray());
|
||||||
} else {
|
} else {
|
||||||
VirtualFile file = InlineCodegenUtil.findVirtualFile(state.getProject(), lambdaInternalName);
|
VirtualFile file = InlineCodegenUtil.findVirtualFile(state.getProject(), objectInternalName);
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
throw new RuntimeException("Couldn't find virtual file for " + lambdaInternalName);
|
throw new RuntimeException("Couldn't find virtual file for " + objectInternalName);
|
||||||
}
|
}
|
||||||
reader = new ClassReader(file.getInputStream());
|
reader = new ClassReader(file.getInputStream());
|
||||||
}
|
}
|
||||||
@@ -87,75 +78,106 @@ public class LambdaTransformer {
|
|||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO rewrite to one step
|
|
||||||
constructor = getMethodNode(reader, true, false);
|
|
||||||
invoke = getMethodNode(reader, false, false);
|
|
||||||
bridge = getMethodNode(reader, false, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildInvokeParams(ParametersBuilder builder) {
|
private void buildInvokeParamsFor(@NotNull ParametersBuilder builder, @NotNull MethodNode node) {
|
||||||
builder.addThis(oldLambdaType, false);
|
builder.addThis(oldObjectType, false);
|
||||||
|
|
||||||
Type[] types = Type.getArgumentTypes(invoke.desc);
|
Type[] types = Type.getArgumentTypes(node.desc);
|
||||||
for (Type type : types) {
|
for (Type type : types) {
|
||||||
builder.addNextParameter(type, false, null);
|
builder.addNextParameter(type, false, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public InlineResult doTransform(ConstructorInvocation invocation, FieldRemapper parentRemapper) {
|
@NotNull
|
||||||
ClassBuilder classBuilder = createClassBuilder();
|
public InlineResult doTransform(@NotNull ConstructorInvocation invocation, @NotNull FieldRemapper parentRemapper) {
|
||||||
|
final ClassBuilder classBuilder = createClassBuilder();
|
||||||
|
final List<MethodNode> methodsToTransform = new ArrayList<MethodNode>();
|
||||||
|
final List<FieldNode> fieldToAdd = new ArrayList<FieldNode>();
|
||||||
|
reader.accept(new ClassVisitor(InlineCodegenUtil.API, classBuilder.getVisitor()) {
|
||||||
|
|
||||||
//TODO: public visibility for inline function
|
@Override
|
||||||
classBuilder.defineClass(null,
|
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||||
V1_6,
|
//TODO: public visibility for inline function
|
||||||
classAccess,
|
LambdaTransformer.this.superName = superName;
|
||||||
newLambdaType.getInternalName(),
|
classBuilder.defineClass(null,
|
||||||
signature,
|
V1_6,
|
||||||
superName,
|
access,
|
||||||
interfaces
|
newLambdaType.getInternalName(),
|
||||||
);
|
signature,
|
||||||
|
superName,
|
||||||
|
interfaces
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: load synthetic class kind from the transformed class and write the same kind to the copy of that class here
|
@Override
|
||||||
// See AsmUtil.writeKotlinSyntheticClassAnnotation
|
public MethodVisitor visitMethod(
|
||||||
|
int access, String name, String desc, String signature, String[] exceptions
|
||||||
|
) {
|
||||||
|
MethodNode node = new MethodNode(access, name, desc, signature, exceptions);
|
||||||
|
if (name.equals("<init>")){
|
||||||
|
if (constructor != null)
|
||||||
|
throw new RuntimeException("Lambda, SAM or anonymous object should have only one constructor");
|
||||||
|
|
||||||
ParametersBuilder builder = ParametersBuilder.newBuilder();
|
constructor = node;
|
||||||
Parameters parameters = getLambdaParameters(builder, invocation);
|
} else {
|
||||||
|
methodsToTransform.add(node);
|
||||||
MethodVisitor invokeVisitor = newMethod(classBuilder, invoke);
|
|
||||||
|
|
||||||
RegeneratedLambdaFieldRemapper remapper =
|
|
||||||
new RegeneratedLambdaFieldRemapper(oldLambdaType.getInternalName(), newLambdaType.getInternalName(),
|
|
||||||
parameters, invocation.getCapturedLambdasToInline(),
|
|
||||||
parentRemapper);
|
|
||||||
|
|
||||||
MethodInliner inliner = new MethodInliner(invoke, parameters, inliningContext.subInline(inliningContext.nameGenerator.subGenerator("lambda")),
|
|
||||||
remapper, isSameModule, "Transformer for " + invocation.getOwnerInternalName());
|
|
||||||
InlineResult result = inliner.doInline(invokeVisitor, new LocalVarRemapper(parameters, 0), false);
|
|
||||||
invokeVisitor.visitMaxs(-1, -1);
|
|
||||||
|
|
||||||
generateConstructorAndFields(classBuilder, builder, invocation);
|
|
||||||
|
|
||||||
if (bridge != null) {
|
|
||||||
MethodVisitor invokeBridge = newMethod(classBuilder, bridge);
|
|
||||||
bridge.accept(new MethodVisitor(InlineCodegenUtil.API, invokeBridge) {
|
|
||||||
@Override
|
|
||||||
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
|
|
||||||
if (owner.equals(oldLambdaType.getInternalName())) {
|
|
||||||
super.visitMethodInsn(opcode, newLambdaType.getInternalName(), name, desc, itf);
|
|
||||||
} else {
|
|
||||||
super.visitMethodInsn(opcode, owner, name, desc, itf);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FieldVisitor visitField(
|
||||||
|
int access, String name, String desc, String signature, Object value
|
||||||
|
) {
|
||||||
|
addUniqueField(name);
|
||||||
|
FieldNode fieldNode = new FieldNode(access, name, desc, signature, value);
|
||||||
|
fieldToAdd.add(fieldNode);
|
||||||
|
return fieldNode;
|
||||||
|
}
|
||||||
|
}, ClassReader.SKIP_FRAMES);
|
||||||
|
|
||||||
|
ParametersBuilder capturedBuilder = ParametersBuilder.newBuilder();
|
||||||
|
extractParametersMappingAndPatchConstructor(constructor, capturedBuilder, invocation);
|
||||||
|
|
||||||
|
InlineResult result = InlineResult.create();
|
||||||
|
for (MethodNode next : methodsToTransform) {
|
||||||
|
MethodVisitor visitor = newMethod(classBuilder, next);
|
||||||
|
InlineResult funResult = inlineMethod(invocation, parentRemapper, visitor, next, capturedBuilder);
|
||||||
|
result.addAllClassesToRemove(funResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generateConstructorAndFields(classBuilder, capturedBuilder, invocation);
|
||||||
|
|
||||||
classBuilder.done();
|
classBuilder.done();
|
||||||
|
|
||||||
invocation.setNewLambdaType(newLambdaType);
|
invocation.setNewLambdaType(newLambdaType);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private InlineResult inlineMethod(
|
||||||
|
@NotNull ConstructorInvocation invocation,
|
||||||
|
@NotNull FieldRemapper parentRemapper,
|
||||||
|
@NotNull MethodVisitor resultVisitor,
|
||||||
|
@NotNull MethodNode sourceNode,
|
||||||
|
@NotNull ParametersBuilder capturedBuilder
|
||||||
|
) {
|
||||||
|
|
||||||
|
Parameters parameters = getMethodParametersWithCaptured(capturedBuilder, sourceNode);
|
||||||
|
|
||||||
|
RegeneratedLambdaFieldRemapper remapper =
|
||||||
|
new RegeneratedLambdaFieldRemapper(oldObjectType.getInternalName(), newLambdaType.getInternalName(),
|
||||||
|
parameters, invocation.getCapturedLambdasToInline(),
|
||||||
|
parentRemapper);
|
||||||
|
|
||||||
|
MethodInliner inliner = new MethodInliner(sourceNode, parameters, inliningContext.subInline(inliningContext.nameGenerator.subGenerator("lambda")),
|
||||||
|
remapper, isSameModule, "Transformer for " + invocation.getOwnerInternalName());
|
||||||
|
InlineResult result = inliner.doInline(resultVisitor, new LocalVarRemapper(parameters, 0), false);
|
||||||
|
resultVisitor.visitMaxs(-1, -1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private void generateConstructorAndFields(@NotNull ClassBuilder classBuilder, @NotNull ParametersBuilder builder, @NotNull ConstructorInvocation invocation) {
|
private void generateConstructorAndFields(@NotNull ClassBuilder classBuilder, @NotNull ParametersBuilder builder, @NotNull ConstructorInvocation invocation) {
|
||||||
List<CapturedParamInfo> infos = builder.buildCaptured();
|
List<CapturedParamInfo> infos = builder.buildCaptured();
|
||||||
List<Pair<String, Type>> newConstructorSignature = new ArrayList<Pair<String, Type>>();
|
List<Pair<String, Type>> newConstructorSignature = new ArrayList<Pair<String, Type>>();
|
||||||
@@ -174,32 +196,43 @@ public class LambdaTransformer {
|
|||||||
invocation.setNewConstructorDescriptor(newConstructor.getDescriptor());
|
invocation.setNewConstructorDescriptor(newConstructor.getDescriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Parameters getLambdaParameters(ParametersBuilder builder, ConstructorInvocation invocation) {
|
@NotNull
|
||||||
buildInvokeParams(builder);
|
private Parameters getMethodParametersWithCaptured(
|
||||||
extractParametersMapping(constructor, builder, invocation);
|
@NotNull ParametersBuilder capturedBuilder,
|
||||||
|
@NotNull MethodNode sourceNode
|
||||||
|
) {
|
||||||
|
ParametersBuilder builder = ParametersBuilder.newBuilder();
|
||||||
|
buildInvokeParamsFor(builder, sourceNode);
|
||||||
|
for (CapturedParamInfo param : capturedBuilder.getCapturedParams()) {
|
||||||
|
builder.addCapturedParamCopy(param);
|
||||||
|
}
|
||||||
return builder.buildParameters();
|
return builder.buildParameters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
private ClassBuilder createClassBuilder() {
|
private ClassBuilder createClassBuilder() {
|
||||||
return new RemappingClassBuilder(state.getFactory().forLambdaInlining(newLambdaType, inliningContext.call.getCallElement().getContainingFile()),
|
return new RemappingClassBuilder(state.getFactory().forLambdaInlining(newLambdaType, inliningContext.call.getCallElement().getContainingFile()),
|
||||||
new TypeRemapper(inliningContext.typeMapping));
|
new TypeRemapper(inliningContext.typeMapping));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MethodVisitor newMethod(ClassBuilder builder, MethodNode original) {
|
@NotNull
|
||||||
|
private static MethodVisitor newMethod(@NotNull ClassBuilder builder, @NotNull MethodNode original) {
|
||||||
return builder.newMethod(
|
return builder.newMethod(
|
||||||
null,
|
null,
|
||||||
original.access,
|
original.access,
|
||||||
original.name,
|
original.name,
|
||||||
original.desc,
|
original.desc,
|
||||||
original.signature,
|
original.signature,
|
||||||
null //TODO: change signature to list
|
original.exceptions.toArray(new String [original.exceptions.size()])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void extractParametersMapping(MethodNode constructor, ParametersBuilder builder, final ConstructorInvocation invocation) {
|
private void extractParametersMappingAndPatchConstructor(
|
||||||
|
@NotNull MethodNode constructor,
|
||||||
|
@NotNull ParametersBuilder builder,
|
||||||
|
@NotNull final ConstructorInvocation invocation
|
||||||
|
) {
|
||||||
Map<Integer, LambdaInfo> indexToLambda = invocation.getLambdasToInline();
|
Map<Integer, LambdaInfo> indexToLambda = invocation.getLambdasToInline();
|
||||||
|
|
||||||
AbstractInsnNode cur = constructor.instructions.getFirst();
|
|
||||||
List<LambdaInfo> capturedLambdas = new ArrayList<LambdaInfo>(); //captured var of inlined parameter
|
List<LambdaInfo> capturedLambdas = new ArrayList<LambdaInfo>(); //captured var of inlined parameter
|
||||||
CapturedParamOwner owner = new CapturedParamOwner() {
|
CapturedParamOwner owner = new CapturedParamOwner() {
|
||||||
@Override
|
@Override
|
||||||
@@ -208,21 +241,27 @@ public class LambdaTransformer {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
AbstractInsnNode cur = constructor.instructions.getFirst();
|
||||||
|
//load captured parameters (NB: there is also could be object fields)
|
||||||
while (cur != null) {
|
while (cur != null) {
|
||||||
if (cur.getType() == AbstractInsnNode.FIELD_INSN) {
|
if (cur.getType() == AbstractInsnNode.FIELD_INSN) {
|
||||||
FieldInsnNode fieldNode = (FieldInsnNode) cur;
|
FieldInsnNode fieldNode = (FieldInsnNode) cur;
|
||||||
CapturedParamInfo info = builder.addCapturedParam(fieldNode.name, Type.getType(fieldNode.desc), false, null, owner);
|
CapturedParamInfo info = builder.addCapturedParam(owner, fieldNode.name, Type.getType(fieldNode.desc), false, null);
|
||||||
|
|
||||||
assert fieldNode.getPrevious() instanceof VarInsnNode : "Previous instruction should be VarInsnNode but was " + fieldNode.getPrevious();
|
boolean isPrevVarNode = fieldNode.getPrevious() instanceof VarInsnNode;
|
||||||
VarInsnNode previous = (VarInsnNode) fieldNode.getPrevious();
|
boolean isPrevPrevVarNode = isPrevVarNode && fieldNode.getPrevious().getPrevious() instanceof VarInsnNode;
|
||||||
int varIndex = previous.var;
|
if (isPrevPrevVarNode) {
|
||||||
LambdaInfo lambdaInfo = indexToLambda.get(varIndex);
|
VarInsnNode node = (VarInsnNode) fieldNode.getPrevious().getPrevious();
|
||||||
if (lambdaInfo != null) {
|
if (node.var == 0) {
|
||||||
info.setLambda(lambdaInfo);
|
VarInsnNode previous = (VarInsnNode) fieldNode.getPrevious();
|
||||||
capturedLambdas.add(lambdaInfo);
|
int varIndex = previous.var;
|
||||||
|
LambdaInfo lambdaInfo = indexToLambda.get(varIndex);
|
||||||
|
if (lambdaInfo != null) {
|
||||||
|
info.setLambda(lambdaInfo);
|
||||||
|
capturedLambdas.add(lambdaInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addUniqueField(info.getOriginalFieldName());
|
|
||||||
}
|
}
|
||||||
cur = cur.getNext();
|
cur = cur.getNext();
|
||||||
}
|
}
|
||||||
@@ -234,9 +273,9 @@ public class LambdaTransformer {
|
|||||||
for (LambdaInfo info : capturedLambdas) {
|
for (LambdaInfo info : capturedLambdas) {
|
||||||
for (CapturedParamInfo var : info.getCapturedVars()) {
|
for (CapturedParamInfo var : info.getCapturedVars()) {
|
||||||
CapturedParamInfo recapturedParamInfo = builder.addCapturedParam(var, getNewFieldName(var.getOriginalFieldName()));
|
CapturedParamInfo recapturedParamInfo = builder.addCapturedParam(var, getNewFieldName(var.getOriginalFieldName()));
|
||||||
StackValue composed = StackValue.composed(StackValue.local(0, oldLambdaType),
|
StackValue composed = StackValue.composed(StackValue.local(0, oldObjectType),
|
||||||
StackValue.field(var.getType(),
|
StackValue.field(var.getType(),
|
||||||
oldLambdaType, /*TODO owner type*/
|
oldObjectType, /*TODO owner type*/
|
||||||
recapturedParamInfo.getNewFieldName(), false)
|
recapturedParamInfo.getNewFieldName(), false)
|
||||||
);
|
);
|
||||||
recapturedParamInfo.setRemapValue(composed);
|
recapturedParamInfo.setRemapValue(composed);
|
||||||
@@ -249,39 +288,6 @@ public class LambdaTransformer {
|
|||||||
invocation.setCapturedLambdasToInline(capturedLambdasToInline);
|
invocation.setCapturedLambdasToInline(capturedLambdasToInline);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public MethodNode getMethodNode(@NotNull ClassReader reader, final boolean findConstructor, final boolean findBridge) {
|
|
||||||
final MethodNode[] methodNode = new MethodNode[1];
|
|
||||||
reader.accept(new ClassVisitor(InlineCodegenUtil.API) {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
|
||||||
super.visit(version, access, name, signature, superName, interfaces);
|
|
||||||
LambdaTransformer.this.classAccess = access;
|
|
||||||
LambdaTransformer.this.signature = signature;
|
|
||||||
LambdaTransformer.this.superName = superName;
|
|
||||||
LambdaTransformer.this.interfaces = interfaces;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
|
||||||
boolean isConstructorMethod = "<init>".equals(name);
|
|
||||||
boolean isBridge = (access & Opcodes.ACC_BRIDGE) != 0;
|
|
||||||
if (findConstructor && isConstructorMethod || (!findConstructor && !isConstructorMethod && (isBridge == findBridge))) {
|
|
||||||
assert methodNode[0] == null : "Wrong lambda/sam structure: " + methodNode[0].name + " conflicts with " + name;
|
|
||||||
return methodNode[0] = new MethodNode(access, name, desc, signature, exceptions);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
|
|
||||||
|
|
||||||
if (methodNode[0] == null && !findBridge) {
|
|
||||||
throw new RuntimeException("Couldn't find operation method of lambda/sam class " + oldLambdaType.getInternalName() + ": findConstructor = " + findConstructor);
|
|
||||||
}
|
|
||||||
|
|
||||||
return methodNode[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public String getNewFieldName(@NotNull String oldName) {
|
public String getNewFieldName(@NotNull String oldName) {
|
||||||
if (oldName.equals("this$0")) {
|
if (oldName.equals("this$0")) {
|
||||||
|
|||||||
@@ -58,13 +58,21 @@ public class ParametersBuilder {
|
|||||||
return addCapturedParameter(info);
|
return addCapturedParameter(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public CapturedParamInfo addCapturedParamCopy(
|
||||||
|
@NotNull CapturedParamInfo copyFrom
|
||||||
|
) {
|
||||||
|
CapturedParamInfo info = copyFrom.newIndex(nextCaptured);
|
||||||
|
return addCapturedParameter(info);
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public CapturedParamInfo addCapturedParam(
|
public CapturedParamInfo addCapturedParam(
|
||||||
|
@NotNull CapturedParamOwner containingLambda,
|
||||||
@NotNull String fieldName,
|
@NotNull String fieldName,
|
||||||
@NotNull Type type,
|
@NotNull Type type,
|
||||||
boolean skipped,
|
boolean skipped,
|
||||||
@Nullable ParameterInfo original,
|
@Nullable ParameterInfo original
|
||||||
@NotNull CapturedParamOwner containingLambda
|
|
||||||
) {
|
) {
|
||||||
CapturedParamInfo info =
|
CapturedParamInfo info =
|
||||||
new CapturedParamInfo(CapturedParamDesc.createDesc(containingLambda, fieldName, type), skipped, nextCaptured,
|
new CapturedParamInfo(CapturedParamDesc.createDesc(containingLambda, fieldName, type), skipped, nextCaptured,
|
||||||
@@ -105,4 +113,8 @@ public class ParametersBuilder {
|
|||||||
public Parameters buildParameters() {
|
public Parameters buildParameters() {
|
||||||
return new Parameters(buildWithStubs(), buildCapturedWithStubs());
|
return new Parameters(buildWithStubs(), buildCapturedWithStubs());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<CapturedParamInfo> getCapturedParams() {
|
||||||
|
return capturedParams;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import test.*
|
||||||
|
|
||||||
|
fun box() : String {
|
||||||
|
val o = "O"
|
||||||
|
return doWork {
|
||||||
|
val k = "K"
|
||||||
|
val s = object : A<String>() {
|
||||||
|
override fun getO(): String {
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getK(): String {
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.getO() + s.getK()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
abstract class A<R> {
|
||||||
|
abstract fun getO() : R
|
||||||
|
|
||||||
|
abstract fun getK() : R
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline fun <R> doWork(job: ()-> R) : R {
|
||||||
|
return job()
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import test.*
|
||||||
|
|
||||||
|
fun box() : String {
|
||||||
|
val o = "O"
|
||||||
|
val result = doWork {
|
||||||
|
val k = "K"
|
||||||
|
val s = object : A<String>("11") {
|
||||||
|
override fun getO(): String {
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getK(): String {
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.getO() + s.getK() + s.param
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != "OK11") return "fail $result"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
abstract class A<R>(val param: R) {
|
||||||
|
abstract fun getO() : R
|
||||||
|
|
||||||
|
abstract fun getK() : R
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline fun <R> doWork(job: ()-> R) : R {
|
||||||
|
return job()
|
||||||
|
}
|
||||||
+10
@@ -36,6 +36,16 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxCodegenT
|
|||||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/boxInline"), Pattern.compile("^([^\\.]+)$"), false);
|
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/boxInline"), Pattern.compile("^([^\\.]+)$"), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("anonymousObjectOnCallSite")
|
||||||
|
public void testAnonymousObjectOnCallSite() throws Exception {
|
||||||
|
doTestMultiFileWithInlineCheck("compiler/testData/codegen/boxInline/anonymousObjectOnCallSite");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("anonymousObjectSuperParams")
|
||||||
|
public void testAnonymousObjectSuperParams() throws Exception {
|
||||||
|
doTestMultiFileWithInlineCheck("compiler/testData/codegen/boxInline/anonymousObjectSuperParams");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("builders")
|
@TestMetadata("builders")
|
||||||
public void testBuilders() throws Exception {
|
public void testBuilders() throws Exception {
|
||||||
doTestMultiFileWithInlineCheck("compiler/testData/codegen/boxInline/builders");
|
doTestMultiFileWithInlineCheck("compiler/testData/codegen/boxInline/builders");
|
||||||
|
|||||||
+10
@@ -36,6 +36,16 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
|
|||||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/boxInline"), Pattern.compile("^([^\\.]+)$"), false);
|
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/boxInline"), Pattern.compile("^([^\\.]+)$"), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("anonymousObjectOnCallSite")
|
||||||
|
public void testAnonymousObjectOnCallSite() throws Exception {
|
||||||
|
doBoxTestWithInlineCheck("compiler/testData/codegen/boxInline/anonymousObjectOnCallSite");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("anonymousObjectSuperParams")
|
||||||
|
public void testAnonymousObjectSuperParams() throws Exception {
|
||||||
|
doBoxTestWithInlineCheck("compiler/testData/codegen/boxInline/anonymousObjectSuperParams");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("builders")
|
@TestMetadata("builders")
|
||||||
public void testBuilders() throws Exception {
|
public void testBuilders() throws Exception {
|
||||||
doBoxTestWithInlineCheck("compiler/testData/codegen/boxInline/builders");
|
doBoxTestWithInlineCheck("compiler/testData/codegen/boxInline/builders");
|
||||||
|
|||||||
Reference in New Issue
Block a user