Inline refactoring: more templates for captured fields
Support of complex lambda inlining cases
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2010-2014 JetBrains s.r.o.
|
||||
*
|
||||
* 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.jetbrains.jet.codegen.inline;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.asm4.Type;
|
||||
import org.jetbrains.jet.codegen.context.EnclosedValueDescriptor;
|
||||
|
||||
public class CapturedParamDesc {
|
||||
|
||||
private final CapturedParamOwner containingLambda;
|
||||
|
||||
private final String fieldName;
|
||||
|
||||
private final Type type;
|
||||
|
||||
public CapturedParamDesc(@NotNull CapturedParamOwner containingLambda, @NotNull EnclosedValueDescriptor descriptor) {
|
||||
this.containingLambda = containingLambda;
|
||||
this.type = descriptor.getType();
|
||||
this.fieldName = descriptor.getFieldName();
|
||||
}
|
||||
|
||||
public CapturedParamDesc(@NotNull CapturedParamOwner containingLambda, @NotNull String fieldName, @NotNull Type type) {
|
||||
this.containingLambda = containingLambda;
|
||||
this.fieldName = fieldName;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public CapturedParamOwner getContainingLambda() {
|
||||
return containingLambda;
|
||||
}
|
||||
|
||||
public String getFieldName() {
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
public static CapturedParamDesc createDesc(CapturedParamOwner containingLambdaInfo, String fieldName, Type type) {
|
||||
return new CapturedParamDesc(containingLambdaInfo, fieldName, type);
|
||||
}
|
||||
}
|
||||
@@ -23,26 +23,29 @@ import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
|
||||
|
||||
public class CapturedParamInfo extends ParameterInfo {
|
||||
|
||||
private final String fieldName;
|
||||
public static final CapturedParamInfo STUB = new CapturedParamInfo(CapturedParamDesc.createDesc(new CapturedParamOwner() {
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.getObjectType("STUB");
|
||||
}
|
||||
}, "STUB", Type.getObjectType("STUB")), true, -1, -1);
|
||||
|
||||
public final CapturedParamDesc desc;
|
||||
|
||||
private int shift = 0;
|
||||
|
||||
private LambdaInfo recapturedFrom;
|
||||
|
||||
public static final CapturedParamInfo STUB = new CapturedParamInfo("", AsmTypeConstants.OBJECT_TYPE, true, -1, -1);
|
||||
|
||||
public CapturedParamInfo(@NotNull String fieldName, @NotNull Type type, boolean skipped, int index, int remapIndex) {
|
||||
super(type, skipped, index, remapIndex);
|
||||
this.fieldName = fieldName;
|
||||
public CapturedParamInfo(@NotNull CapturedParamDesc desc, boolean skipped, int index, int remapIndex) {
|
||||
super(desc.getType(), skipped, index, remapIndex);
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public CapturedParamInfo(@NotNull String fieldName, @NotNull Type type, boolean skipped, int index, StackValue remapIndex) {
|
||||
super(type, skipped, index, remapIndex);
|
||||
this.fieldName = fieldName;
|
||||
public CapturedParamInfo(@NotNull CapturedParamDesc desc, boolean skipped, int index, StackValue remapIndex) {
|
||||
super(desc.getType(), skipped, index, remapIndex);
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public String getFieldName() {
|
||||
return fieldName;
|
||||
return desc.getFieldName();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,22 +57,17 @@ public class CapturedParamInfo extends ParameterInfo {
|
||||
this.shift = shift;
|
||||
}
|
||||
|
||||
public LambdaInfo getRecapturedFrom() {
|
||||
return recapturedFrom;
|
||||
}
|
||||
|
||||
public void setRecapturedFrom(LambdaInfo recapturedFrom) {
|
||||
this.recapturedFrom = recapturedFrom;
|
||||
}
|
||||
|
||||
public CapturedParamInfo newIndex(int newIndex) {
|
||||
return clone(newIndex, getRemapValue());
|
||||
}
|
||||
|
||||
public CapturedParamInfo clone(int newIndex, StackValue newRamapIndex) {
|
||||
CapturedParamInfo capturedParamInfo = new CapturedParamInfo(fieldName, type, isSkipped, newIndex, newRamapIndex);
|
||||
CapturedParamInfo capturedParamInfo = new CapturedParamInfo(desc, isSkipped, newIndex, newRamapIndex);
|
||||
capturedParamInfo.setLambda(lambda);
|
||||
capturedParamInfo.setRecapturedFrom(recapturedFrom);
|
||||
return capturedParamInfo;
|
||||
}
|
||||
|
||||
public String getContainingLambdaName() {
|
||||
return desc.getContainingLambda().getType().getInternalName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2010-2014 JetBrains s.r.o.
|
||||
*
|
||||
* 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.jetbrains.jet.codegen.inline;
|
||||
|
||||
import org.jetbrains.asm4.Type;
|
||||
|
||||
public interface CapturedParamOwner {
|
||||
|
||||
Type getType();
|
||||
}
|
||||
@@ -213,11 +213,11 @@ public class InlineCodegen implements ParentCodegenAware, CallGenerator {
|
||||
codegen.getInlineNameGenerator().subGenerator(functionDescriptor.getName().asString()),
|
||||
codegen.getContext(), call, Collections.<String, String>emptyMap(), false, false);
|
||||
|
||||
MethodInliner inliner = new MethodInliner(node, parameters, info, null, new LambdaFieldRemapper(null, null, parameters), isSameModule); //with captured
|
||||
MethodInliner inliner = new MethodInliner(node, parameters, info, new LambdaFieldRemapper(null, null, parameters), isSameModule, "InlineCodegenRoot " + call.getCallElement()); //with captured
|
||||
|
||||
VarRemapper.ParamRemapper remapper = new VarRemapper.ParamRemapper(parameters, initialFrameSize);
|
||||
|
||||
return inliner.doInline(codegen.v, remapper);
|
||||
return inliner.doInline(codegen.v, remapper, new LambdaFieldRemapper(null, null, parameters));
|
||||
}
|
||||
|
||||
private void generateClosuresBodies() {
|
||||
@@ -451,7 +451,7 @@ public class InlineCodegen implements ParentCodegenAware, CallGenerator {
|
||||
StringWriter sw = new StringWriter();
|
||||
p.print(new PrintWriter(sw));
|
||||
sw.flush();
|
||||
return node.name + ": \n " + sw.getBuffer().toString();
|
||||
return node.name + " " + node.desc + ": \n " + sw.getBuffer().toString();
|
||||
}
|
||||
|
||||
private static String descriptorName(DeclarationDescriptor descriptor) {
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2010-2014 JetBrains s.r.o.
|
||||
*
|
||||
* 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.jetbrains.jet.codegen.inline;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.asm4.tree.FieldInsnNode;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class InlinedLambdaRemapper extends LambdaFieldRemapper {
|
||||
|
||||
public InlinedLambdaRemapper(
|
||||
@NotNull String lambdaInternalName,
|
||||
@NotNull LambdaFieldRemapper parent,
|
||||
@NotNull Parameters methodParams
|
||||
) {
|
||||
super(lambdaInternalName, parent, methodParams);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addCapturedFields(
|
||||
LambdaInfo lambdaInfo, ParametersBuilder builder
|
||||
) {
|
||||
parent.addCapturedFields(lambdaInfo, builder);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public CapturedParamInfo findField(
|
||||
@NotNull FieldInsnNode fieldInsnNode,
|
||||
@NotNull Collection<CapturedParamInfo> captured
|
||||
) {
|
||||
return parent.findField(fieldInsnNode, captured);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LambdaFieldRemapper getParent() {
|
||||
return parent.getParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRoot() {
|
||||
return parent.isRoot();
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
class InvokeCall {
|
||||
|
||||
public final int index;
|
||||
private final int index;
|
||||
|
||||
public final LambdaInfo lambdaInfo;
|
||||
|
||||
|
||||
@@ -22,16 +22,14 @@ import org.jetbrains.asm4.Opcodes;
|
||||
import org.jetbrains.asm4.tree.AbstractInsnNode;
|
||||
import org.jetbrains.asm4.tree.FieldInsnNode;
|
||||
import org.jetbrains.asm4.tree.MethodNode;
|
||||
import org.jetbrains.asm4.tree.VarInsnNode;
|
||||
import org.jetbrains.jet.codegen.StackValue;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.jet.codegen.inline.MethodInliner.getPreviousNoLabelNoLine;
|
||||
|
||||
public class LambdaFieldRemapper {
|
||||
|
||||
private String lambdaInternalName;
|
||||
private final String lambdaInternalName;
|
||||
|
||||
protected LambdaFieldRemapper parent;
|
||||
|
||||
@@ -43,42 +41,58 @@ public class LambdaFieldRemapper {
|
||||
params = methodParams;
|
||||
}
|
||||
|
||||
public AbstractInsnNode doTransform(MethodNode node, FieldInsnNode fieldInsnNode, CapturedParamInfo capturedField) {
|
||||
AbstractInsnNode loadThis = getPreviousThis(fieldInsnNode);
|
||||
|
||||
int opcode = fieldInsnNode.getOpcode() == Opcodes.GETFIELD ? capturedField.getType().getOpcode(Opcodes.ILOAD) : capturedField.getType().getOpcode(Opcodes.ISTORE);
|
||||
VarInsnNode newInstruction = new VarInsnNode(opcode, capturedField.getIndex());
|
||||
|
||||
node.instructions.remove(loadThis); //remove aload this
|
||||
node.instructions.insertBefore(fieldInsnNode, newInstruction);
|
||||
node.instructions.remove(fieldInsnNode); //remove aload field
|
||||
|
||||
return newInstruction;
|
||||
public void addCapturedFields(LambdaInfo lambdaInfo, ParametersBuilder builder) {
|
||||
for (CapturedParamInfo info : lambdaInfo.getCapturedVars()) {
|
||||
builder.addCapturedParam(info, info);
|
||||
}
|
||||
}
|
||||
|
||||
protected static AbstractInsnNode getPreviousThis(FieldInsnNode fieldInsnNode) {
|
||||
AbstractInsnNode loadThis = getPreviousNoLabelNoLine(fieldInsnNode);
|
||||
|
||||
assert loadThis.getType() == AbstractInsnNode.VAR_INSN || loadThis.getType() == AbstractInsnNode.FIELD_INSN :
|
||||
"Field access instruction should go after load this but goes after " + loadThis;
|
||||
assert loadThis.getOpcode() == Opcodes.ALOAD || loadThis.getOpcode() == Opcodes.GETSTATIC :
|
||||
"This should be loaded by ALOAD or GETSTATIC but " + loadThis.getOpcode();
|
||||
return loadThis;
|
||||
public boolean canProcess(@NotNull String fieldOwner) {
|
||||
return fieldOwner.equals(getLambdaInternalName());
|
||||
}
|
||||
|
||||
public List<CapturedParamInfo> markRecaptured(List<CapturedParamInfo> originalCaptured, LambdaInfo lambda) {
|
||||
return originalCaptured;
|
||||
public AbstractInsnNode transformIfNeeded(
|
||||
@NotNull List<AbstractInsnNode> capturedFieldAccess,
|
||||
int currentInstruction,
|
||||
@NotNull MethodNode node
|
||||
) {
|
||||
if (capturedFieldAccess.size() == 1) {
|
||||
//just aload
|
||||
return null;
|
||||
}
|
||||
|
||||
AbstractInsnNode transformed = null;
|
||||
boolean checkParent = !isRoot() && currentInstruction < capturedFieldAccess.size() - 1;
|
||||
if (checkParent) {
|
||||
transformed = parent.transformIfNeeded(capturedFieldAccess, currentInstruction + 1, node);
|
||||
}
|
||||
|
||||
if (transformed == null) {
|
||||
//if parent couldn't transform
|
||||
FieldInsnNode insnNode = (FieldInsnNode) capturedFieldAccess.get(currentInstruction);
|
||||
if (canProcess(insnNode.owner)) {
|
||||
insnNode.name = "$$$" + insnNode.name;
|
||||
insnNode.setOpcode(Opcodes.GETSTATIC);
|
||||
|
||||
for (int i = 0; i < currentInstruction; i++) {
|
||||
AbstractInsnNode toRemove = capturedFieldAccess.get(i);
|
||||
node.instructions.remove(toRemove);
|
||||
}
|
||||
transformed = capturedFieldAccess.get(capturedFieldAccess.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return transformed;
|
||||
}
|
||||
|
||||
|
||||
public boolean canProcess(@NotNull String owner, @NotNull String currentLambdaType) {
|
||||
return owner.equals(currentLambdaType);
|
||||
public CapturedParamInfo findField(@NotNull FieldInsnNode fieldInsnNode) {
|
||||
return findField(fieldInsnNode, params.getCaptured());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public CapturedParamInfo findField(@NotNull FieldInsnNode fieldInsnNode, @NotNull Collection<CapturedParamInfo> captured) {
|
||||
for (CapturedParamInfo valueDescriptor : captured) {
|
||||
if (valueDescriptor.getFieldName().equals(fieldInsnNode.name)) {
|
||||
if (valueDescriptor.getFieldName().equals(fieldInsnNode.name) && fieldInsnNode.owner.equals(valueDescriptor.getContainingLambdaName())) {
|
||||
return valueDescriptor;
|
||||
}
|
||||
}
|
||||
@@ -88,6 +102,7 @@ public class LambdaFieldRemapper {
|
||||
public LambdaFieldRemapper getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public String getLambdaInternalName() {
|
||||
return lambdaInternalName;
|
||||
}
|
||||
@@ -96,16 +111,9 @@ public class LambdaFieldRemapper {
|
||||
return parent == null;
|
||||
}
|
||||
|
||||
public boolean shouldPatch(@NotNull FieldInsnNode node) {
|
||||
return !isRoot() && parent.shouldPatch(node);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public AbstractInsnNode patch(@NotNull FieldInsnNode field, @NotNull MethodNode node) {
|
||||
//parent is inlined so we need patch instruction chain
|
||||
if (!isRoot()){
|
||||
return parent.patch(field, node);
|
||||
}
|
||||
throw new IllegalStateException("Should be invoked");
|
||||
@Nullable
|
||||
public StackValue getFieldForInline(@NotNull FieldInsnNode node, @Nullable StackValue prefix) {
|
||||
CapturedParamInfo field = MethodInliner.findCapturedField(node, this);
|
||||
return field.getRemapValue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ import static org.jetbrains.jet.codegen.binding.CodegenBinding.CLOSURE;
|
||||
import static org.jetbrains.jet.codegen.binding.CodegenBinding.anonymousClassForFunction;
|
||||
import static org.jetbrains.jet.codegen.binding.CodegenBinding.asmTypeForAnonymousClass;
|
||||
|
||||
public class LambdaInfo {
|
||||
public class LambdaInfo implements CapturedParamOwner {
|
||||
|
||||
public final JetFunctionLiteralExpression expression;
|
||||
|
||||
@@ -123,8 +123,8 @@ public class LambdaInfo {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static CapturedParamInfo getCapturedParamInfo(@NotNull EnclosedValueDescriptor descriptor, int index) {
|
||||
return new CapturedParamInfo(descriptor.getFieldName(), descriptor.getType(), false, index, -1);
|
||||
public CapturedParamInfo getCapturedParamInfo(@NotNull EnclosedValueDescriptor descriptor, int index) {
|
||||
return new CapturedParamInfo(CapturedParamDesc.createDesc(this, descriptor.getFieldName(), descriptor.getType()), false, index, -1);
|
||||
}
|
||||
|
||||
public void setParamOffset(int paramOffset) {
|
||||
@@ -149,11 +149,8 @@ public class LambdaInfo {
|
||||
builder.addNextParameter(type, false, null);
|
||||
}
|
||||
|
||||
remapper.addCapturedFields(this, builder);
|
||||
|
||||
List<CapturedParamInfo> infos = remapper.markRecaptured(getCapturedVars(), this);
|
||||
for (CapturedParamInfo info : infos) {
|
||||
builder.addCapturedParam(info.getFieldName(), info.getType(), info.isSkipped, info);
|
||||
}
|
||||
return builder.buildParameters();
|
||||
}
|
||||
|
||||
@@ -164,4 +161,9 @@ public class LambdaInfo {
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return closureClassType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,10 +27,7 @@ import org.jetbrains.asm4.tree.FieldInsnNode;
|
||||
import org.jetbrains.asm4.tree.MethodNode;
|
||||
import org.jetbrains.asm4.tree.VarInsnNode;
|
||||
import org.jetbrains.jet.OutputFile;
|
||||
import org.jetbrains.jet.codegen.AsmUtil;
|
||||
import org.jetbrains.jet.codegen.ClassBuilder;
|
||||
import org.jetbrains.jet.codegen.ClosureCodegen;
|
||||
import org.jetbrains.jet.codegen.FieldInfo;
|
||||
import org.jetbrains.jet.codegen.*;
|
||||
import org.jetbrains.jet.codegen.state.GenerationState;
|
||||
import org.jetbrains.jet.codegen.state.JetTypeMapper;
|
||||
|
||||
@@ -128,11 +125,14 @@ public class LambdaTransformer {
|
||||
Parameters parameters = getLambdaParameters(builder, invocation);
|
||||
|
||||
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")), oldLambdaType,
|
||||
remapper, isSameModule);
|
||||
|
||||
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 VarRemapper.ParamRemapper(parameters, 0), remapper, false);
|
||||
invokeVisitor.visitMaxs(-1, -1);
|
||||
|
||||
@@ -198,16 +198,23 @@ public class LambdaTransformer {
|
||||
);
|
||||
}
|
||||
|
||||
private static void extractParametersMapping(MethodNode constructor, ParametersBuilder builder, ConstructorInvocation invocation) {
|
||||
private void extractParametersMapping(MethodNode constructor, ParametersBuilder builder, final ConstructorInvocation invocation) {
|
||||
Map<Integer, LambdaInfo> indexToLambda = invocation.getLambdasToInline();
|
||||
|
||||
AbstractInsnNode cur = constructor.instructions.getFirst();
|
||||
cur = cur.getNext(); //skip super call
|
||||
List<LambdaInfo> capturedLambdas = new ArrayList<LambdaInfo>(); //captured var of inlined parameter
|
||||
CapturedParamOwner owner = new CapturedParamOwner() {
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.getObjectType(invocation.getOwnerInternalName());
|
||||
}
|
||||
};
|
||||
|
||||
while (cur != null) {
|
||||
if (cur.getType() == AbstractInsnNode.FIELD_INSN) {
|
||||
FieldInsnNode fieldNode = (FieldInsnNode) cur;
|
||||
CapturedParamInfo info = builder.addCapturedParam(fieldNode.name, Type.getType(fieldNode.desc), false, null);
|
||||
CapturedParamInfo info = builder.addCapturedParam(fieldNode.name, Type.getType(fieldNode.desc), false, null, owner);
|
||||
|
||||
assert fieldNode.getPrevious() instanceof VarInsnNode : "Previous instruction should be VarInsnNode but was " + fieldNode.getPrevious();
|
||||
VarInsnNode previous = (VarInsnNode) fieldNode.getPrevious();
|
||||
@@ -227,8 +234,13 @@ public class LambdaTransformer {
|
||||
List<CapturedParamInfo> allRecapturedParameters = new ArrayList<CapturedParamInfo>();
|
||||
for (LambdaInfo info : capturedLambdas) {
|
||||
for (CapturedParamInfo var : info.getCapturedVars()) {
|
||||
CapturedParamInfo recapturedParamInfo = builder.addCapturedParam(getNewFieldName(var.getFieldName()), var.getType(), true, var);
|
||||
recapturedParamInfo.setRecapturedFrom(info);
|
||||
CapturedParamInfo recapturedParamInfo = builder.addCapturedParam(getNewFieldName(var.getFieldName()), var.getType(), var.isSkipped, var, info);
|
||||
StackValue composed = StackValue.composed(StackValue.local(0, oldLambdaType),
|
||||
StackValue.field(var.getType(),
|
||||
oldLambdaType, /*TODO owner type*/
|
||||
getNewFieldName(var.getFieldName()), false)
|
||||
);
|
||||
recapturedParamInfo.setRemapValue(composed);
|
||||
allRecapturedParameters.add(var);
|
||||
}
|
||||
capturedLambdasToInline.put(info.getLambdaClassType().getInternalName(), info);
|
||||
|
||||
@@ -3,7 +3,6 @@ package org.jetbrains.jet.codegen.inline;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.asm4.Label;
|
||||
import org.jetbrains.asm4.MethodVisitor;
|
||||
import org.jetbrains.asm4.Opcodes;
|
||||
@@ -13,7 +12,6 @@ import org.jetbrains.asm4.commons.Method;
|
||||
import org.jetbrains.asm4.commons.RemappingMethodAdapter;
|
||||
import org.jetbrains.asm4.tree.*;
|
||||
import org.jetbrains.asm4.tree.analysis.*;
|
||||
import org.jetbrains.jet.codegen.AsmUtil;
|
||||
import org.jetbrains.jet.codegen.ClosureCodegen;
|
||||
import org.jetbrains.jet.codegen.StackValue;
|
||||
import org.jetbrains.jet.codegen.state.JetTypeMapper;
|
||||
@@ -21,7 +19,8 @@ import org.jetbrains.jet.utils.UtilsPackage;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.jetbrains.jet.codegen.inline.InlineCodegenUtil.*;
|
||||
import static org.jetbrains.jet.codegen.inline.InlineCodegenUtil.isInvokeOnLambda;
|
||||
import static org.jetbrains.jet.codegen.inline.InlineCodegenUtil.isLambdaConstructorCall;
|
||||
|
||||
public class MethodInliner {
|
||||
|
||||
@@ -31,13 +30,12 @@ public class MethodInliner {
|
||||
|
||||
private final InliningContext inliningContext;
|
||||
|
||||
@Nullable
|
||||
private final Type lambdaType;
|
||||
|
||||
private final LambdaFieldRemapper lambdaFieldRemapper;
|
||||
private final LambdaFieldRemapper nodeRemapper;
|
||||
|
||||
private final boolean isSameModule;
|
||||
|
||||
private final String errorPrefix;
|
||||
|
||||
private final JetTypeMapper typeMapper;
|
||||
|
||||
private final List<InvokeCall> invokeCalls = new ArrayList<InvokeCall>();
|
||||
@@ -60,23 +58,23 @@ public class MethodInliner {
|
||||
@NotNull MethodNode node,
|
||||
@NotNull Parameters parameters,
|
||||
@NotNull InliningContext parent,
|
||||
@Nullable Type lambdaType,
|
||||
LambdaFieldRemapper lambdaFieldRemapper,
|
||||
boolean isSameModule
|
||||
@NotNull LambdaFieldRemapper nodeRemapper,
|
||||
boolean isSameModule,
|
||||
@NotNull String errorPrefix
|
||||
) {
|
||||
this.node = node;
|
||||
this.parameters = parameters;
|
||||
this.inliningContext = parent;
|
||||
this.lambdaType = lambdaType;
|
||||
this.lambdaFieldRemapper = lambdaFieldRemapper;
|
||||
this.nodeRemapper = nodeRemapper;
|
||||
this.isSameModule = isSameModule;
|
||||
this.errorPrefix = errorPrefix;
|
||||
this.typeMapper = parent.state.getTypeMapper();
|
||||
this.result = InlineResult.create();
|
||||
}
|
||||
|
||||
|
||||
public InlineResult doInline(MethodVisitor adapter, VarRemapper.ParamRemapper remapper) {
|
||||
return doInline(adapter, remapper, lambdaFieldRemapper, true);
|
||||
public InlineResult doInline(MethodVisitor adapter, VarRemapper.ParamRemapper remapper, LambdaFieldRemapper fieldRemapper) {
|
||||
return doInline(adapter, remapper, fieldRemapper, true);
|
||||
}
|
||||
|
||||
public InlineResult doInline(
|
||||
@@ -85,27 +83,30 @@ public class MethodInliner {
|
||||
LambdaFieldRemapper capturedRemapper, boolean remapReturn
|
||||
) {
|
||||
//analyze body
|
||||
MethodNode transformedNode = node;
|
||||
try {
|
||||
transformedNode = markPlacesForInlineAndRemoveInlinable(transformedNode);
|
||||
}
|
||||
catch (AnalyzerException e) {
|
||||
throw UtilsPackage.rethrow(e);
|
||||
}
|
||||
MethodNode transformedNode = markPlacesForInlineAndRemoveInlinable(node);
|
||||
|
||||
transformedNode = doInline(transformedNode, capturedRemapper);
|
||||
removeClosureAssertions(transformedNode);
|
||||
transformedNode.instructions.resetLabels();
|
||||
|
||||
Label end = new Label();
|
||||
RemapVisitor visitor = new RemapVisitor(adapter, end, remapper, remapReturn);
|
||||
transformedNode.accept(visitor);
|
||||
RemapVisitor visitor = new RemapVisitor(adapter, end, remapper, remapReturn, nodeRemapper);
|
||||
try {
|
||||
transformedNode.accept(visitor);
|
||||
} catch (Exception e) {
|
||||
String text = InlineCodegen.getNodeText(transformedNode);
|
||||
|
||||
throw new RuntimeException(errorPrefix + ": couldn't inline method call '" +
|
||||
transformedNode +
|
||||
"\ncause: " +
|
||||
text, e);
|
||||
}
|
||||
visitor.visitLabel(end);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private MethodNode doInline(MethodNode node, final LambdaFieldRemapper capturedRemapper) {
|
||||
private MethodNode doInline(MethodNode node, final LambdaFieldRemapper capturedRemapper) {
|
||||
|
||||
final Deque<InvokeCall> currentInvokes = new LinkedList<InvokeCall>(invokeCalls);
|
||||
|
||||
@@ -164,15 +165,20 @@ public class MethodInliner {
|
||||
|
||||
Parameters lambdaParameters = info.addAllParameters(capturedRemapper);
|
||||
|
||||
InlinedLambdaRemapper newCapturedRemapper =
|
||||
new InlinedLambdaRemapper(info.getLambdaClassType().getInternalName(), capturedRemapper, lambdaParameters);
|
||||
|
||||
LambdaFieldRemapper lambdaFieldRemapper =
|
||||
new LambdaFieldRemapper(info.getLambdaClassType().getInternalName(), capturedRemapper, lambdaParameters);
|
||||
|
||||
setInlining(true);
|
||||
MethodInliner inliner = new MethodInliner(info.getNode(), lambdaParameters,
|
||||
inliningContext.subInlineLambda(info),
|
||||
info.getLambdaClassType(),
|
||||
capturedRemapper, true /*cause all calls in same module as lambda*/
|
||||
);
|
||||
lambdaFieldRemapper, true /*cause all calls in same module as lambda*/,
|
||||
"Lambda inlining " + info.getLambdaClassType().getInternalName());
|
||||
|
||||
VarRemapper.ParamRemapper remapper = new VarRemapper.ParamRemapper(lambdaParameters, valueParamShift);
|
||||
InlineResult lambdaResult = inliner.doInline(this.mv, remapper);//TODO add skipped this and receiver
|
||||
InlineResult lambdaResult = inliner.doInline(this.mv, remapper, newCapturedRemapper);//TODO add skipped this and receiver
|
||||
result.addAllClassesToRemove(lambdaResult);
|
||||
|
||||
//return value boxing/unboxing
|
||||
@@ -185,22 +191,8 @@ public class MethodInliner {
|
||||
assert invocation != null : "<init> call not corresponds to new call" + owner + " " + name;
|
||||
if (invocation.shouldRegenerate()) {
|
||||
//put additional captured parameters on stack
|
||||
List<CapturedParamInfo> recaptured = invocation.getAllRecapturedParameters();
|
||||
List<CapturedParamInfo> contextCaptured = MethodInliner.this.parameters.getCaptured();
|
||||
for (CapturedParamInfo capturedParamInfo : recaptured) {
|
||||
CapturedParamInfo result = null;
|
||||
for (CapturedParamInfo info : contextCaptured) {
|
||||
//TODO more sophisticated check
|
||||
if (info.getFieldName().equals(capturedParamInfo.getFieldName())) {
|
||||
result = info;
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Unsupported operation: could not transform non-inline lambda inside inlined one: " +
|
||||
owner + "." + name);
|
||||
}
|
||||
super.visitVarInsn(capturedParamInfo.getType().getOpcode(Opcodes.ILOAD), result.getIndex());
|
||||
for (CapturedParamInfo capturedParamInfo : invocation.getAllRecapturedParameters()) {
|
||||
visitFieldInsn(Opcodes.GETSTATIC, capturedParamInfo.getContainingLambdaName(), "$$$" + capturedParamInfo.getFieldName(), capturedParamInfo.getType().getDescriptor());
|
||||
}
|
||||
super.visitMethodInsn(opcode, invocation.getNewLambdaType().getInternalName(), name, invocation.getNewConstructorDescriptor());
|
||||
invocation = null;
|
||||
@@ -212,6 +204,7 @@ public class MethodInliner {
|
||||
super.visitMethodInsn(opcode, changeOwnerForExternalPackage(owner, opcode), name, desc);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
node.accept(inliner);
|
||||
@@ -219,8 +212,18 @@ public class MethodInliner {
|
||||
return resultNode;
|
||||
}
|
||||
|
||||
public void merge() {
|
||||
public CapturedParamInfo findCapturedField(FieldInsnNode node) {
|
||||
return findCapturedField(node, nodeRemapper);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static CapturedParamInfo findCapturedField(FieldInsnNode node, LambdaFieldRemapper fieldRemapper) {
|
||||
FieldInsnNode fin = new FieldInsnNode(node.getOpcode(), node.owner, node.name.substring(3), node.desc);
|
||||
CapturedParamInfo field = fieldRemapper.findField(fin);
|
||||
if (field == null) {
|
||||
throw new IllegalStateException("Couldn't find captured field " + node.owner + "." + node.name + " in " + fieldRemapper.getLambdaInternalName());
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -272,11 +275,21 @@ public class MethodInliner {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected MethodNode markPlacesForInlineAndRemoveInlinable(@NotNull MethodNode node) throws AnalyzerException {
|
||||
protected MethodNode markPlacesForInlineAndRemoveInlinable(@NotNull MethodNode node) {
|
||||
node = prepareNode(node);
|
||||
|
||||
Analyzer<SourceValue> analyzer = new Analyzer<SourceValue>(new SourceInterpreter());
|
||||
Frame<SourceValue>[] sources = analyzer.analyze("fake", node);
|
||||
Frame<SourceValue>[] sources;
|
||||
try {
|
||||
sources = analyzer.analyze("fake", node);
|
||||
} catch (AnalyzerException e) {
|
||||
String text = InlineCodegen.getNodeText(node);
|
||||
|
||||
throw new RuntimeException("Couldn't inline method call '" +
|
||||
node +
|
||||
"\ncause: " +
|
||||
text, e);
|
||||
}
|
||||
|
||||
AbstractInsnNode cur = node.instructions.getFirst();
|
||||
int index = 0;
|
||||
@@ -301,15 +314,11 @@ public class MethodInliner {
|
||||
|
||||
if (sourceValue.insns.size() == 1) {
|
||||
AbstractInsnNode insnNode = sourceValue.insns.iterator().next();
|
||||
if (insnNode.getType() == AbstractInsnNode.VAR_INSN) {
|
||||
assert insnNode.getOpcode() == Opcodes.ALOAD : insnNode.toString();
|
||||
varIndex = ((VarInsnNode) insnNode).var;
|
||||
lambdaInfo = getLambda(varIndex);
|
||||
|
||||
if (lambdaInfo != null) {
|
||||
//remove inlinable access
|
||||
node.instructions.remove(insnNode);
|
||||
}
|
||||
lambdaInfo = getLambdaIfExists(insnNode);
|
||||
if (lambdaInfo != null) {
|
||||
//remove inlinable access
|
||||
node.instructions.remove(insnNode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,13 +332,10 @@ public class MethodInliner {
|
||||
SourceValue sourceValue = frame.getStack(paramStart + i);
|
||||
if (sourceValue.insns.size() == 1) {
|
||||
AbstractInsnNode insnNode = sourceValue.insns.iterator().next();
|
||||
if (insnNode.getOpcode() == Opcodes.ALOAD) {
|
||||
int varIndex = ((VarInsnNode) insnNode).var;
|
||||
LambdaInfo lambdaInfo = getLambda(varIndex);
|
||||
if (lambdaInfo != null) {
|
||||
lambdaMapping.put(i, lambdaInfo);
|
||||
node.instructions.remove(insnNode);
|
||||
}
|
||||
LambdaInfo lambdaInfo = getLambdaIfExists(insnNode);
|
||||
if (lambdaInfo != null) {
|
||||
lambdaMapping.put(i, lambdaInfo);
|
||||
node.instructions.remove(insnNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -366,11 +372,21 @@ public class MethodInliner {
|
||||
return node;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public LambdaInfo getLambda(int index) {
|
||||
if (index < parameters.totalSize()) {
|
||||
return parameters.get(index).getLambda();
|
||||
public LambdaInfo getLambdaIfExists(AbstractInsnNode insnNode) {
|
||||
if (insnNode.getOpcode() == Opcodes.ALOAD) {
|
||||
int varIndex = ((VarInsnNode) insnNode).var;
|
||||
if (varIndex < parameters.totalSize()) {
|
||||
return parameters.get(varIndex).getLambda();
|
||||
}
|
||||
} else if (insnNode instanceof FieldInsnNode) {
|
||||
FieldInsnNode fieldInsnNode = (FieldInsnNode) insnNode;
|
||||
if (fieldInsnNode.name.startsWith("$$$")) {
|
||||
CapturedParamInfo field = findCapturedField(fieldInsnNode);
|
||||
return field.getLambda();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -398,43 +414,43 @@ public class MethodInliner {
|
||||
}
|
||||
|
||||
private void transformCaptured(@NotNull MethodNode node) {
|
||||
if (lambdaType == null) {
|
||||
if (nodeRemapper.isRoot()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//remove all this and shift all variables to captured ones size
|
||||
//encode all captured variable chains - ALOAD 0 ALOAD this$0 GETFIELD $captured - to GETFIELD $$$$captured
|
||||
AbstractInsnNode cur = node.instructions.getFirst();
|
||||
while (cur != null) {
|
||||
if (cur.getType() == AbstractInsnNode.FIELD_INSN) {
|
||||
FieldInsnNode fieldInsnNode = (FieldInsnNode) cur;
|
||||
//TODO check closure
|
||||
if (lambdaFieldRemapper.canProcess(fieldInsnNode.owner, lambdaType.getInternalName())) {
|
||||
CapturedParamInfo result = this.lambdaFieldRemapper.findField(fieldInsnNode, parameters.getCaptured());
|
||||
|
||||
if (result == null) {
|
||||
throw new UnsupportedOperationException("Coudn't find field " +
|
||||
fieldInsnNode.owner +
|
||||
"." +
|
||||
fieldInsnNode.name +
|
||||
" (" +
|
||||
fieldInsnNode.desc +
|
||||
") in captured vars of " + lambdaType);
|
||||
if (cur instanceof VarInsnNode && cur.getOpcode() == Opcodes.ALOAD) {
|
||||
if (((VarInsnNode) cur).var == 0) {
|
||||
List<AbstractInsnNode> accessChain = getCapturedFieldAccessChain((VarInsnNode) cur);
|
||||
AbstractInsnNode insnNode = nodeRemapper.transformIfNeeded(accessChain, 1, node);
|
||||
if (insnNode != null) {
|
||||
cur = insnNode;
|
||||
}
|
||||
|
||||
if (result.isSkipped()) {
|
||||
//lambda class transformation: skip captured this
|
||||
} else {
|
||||
cur = this.lambdaFieldRemapper.doTransform(node, fieldInsnNode, result);
|
||||
}
|
||||
}
|
||||
else if (lambdaFieldRemapper.shouldPatch(fieldInsnNode)) {
|
||||
cur = lambdaFieldRemapper.patch(fieldInsnNode, node);
|
||||
}
|
||||
}
|
||||
cur = cur.getNext();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<AbstractInsnNode> getCapturedFieldAccessChain(@NotNull VarInsnNode node) {
|
||||
List<AbstractInsnNode> fieldAccessChain = new ArrayList<AbstractInsnNode>();
|
||||
fieldAccessChain.add(node);
|
||||
AbstractInsnNode next = node.getNext();
|
||||
while (next != null && next instanceof FieldInsnNode) {
|
||||
fieldAccessChain.add(next);
|
||||
if("this$0".equals(((FieldInsnNode) next).name)) {
|
||||
next = next.getNext();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return fieldAccessChain;
|
||||
}
|
||||
|
||||
public static AbstractInsnNode getPreviousNoLabelNoLine(AbstractInsnNode cur) {
|
||||
AbstractInsnNode prev = cur.getPrevious();
|
||||
while (prev.getType() == AbstractInsnNode.LABEL || prev.getType() == AbstractInsnNode.LINE) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.jet.codegen.inline;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.asm4.Type;
|
||||
|
||||
@@ -46,9 +47,29 @@ public class ParametersBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public CapturedParamInfo addCapturedParam(String fieldName, Type type, boolean skipped, @Nullable ParameterInfo original) {
|
||||
return addCapturedParameter(new CapturedParamInfo(fieldName, type, skipped, nextCaptured,
|
||||
original != null ? original.getIndex() : -1));
|
||||
public CapturedParamInfo addCapturedParam(
|
||||
@NotNull CapturedParamInfo param,
|
||||
@Nullable CapturedParamInfo originalField
|
||||
) {
|
||||
CapturedParamInfo info = new CapturedParamInfo(param.desc, param.isSkipped, nextCaptured, originalField != null ? originalField.getIndex() : -1);
|
||||
info.setLambda(param.getLambda());
|
||||
return addCapturedParameter(info);
|
||||
}
|
||||
|
||||
public CapturedParamInfo addCapturedParam(
|
||||
String fieldName,
|
||||
Type type,
|
||||
boolean skipped,
|
||||
@Nullable ParameterInfo original,
|
||||
CapturedParamOwner containingLambda
|
||||
) {
|
||||
CapturedParamInfo info =
|
||||
new CapturedParamInfo(CapturedParamDesc.createDesc(containingLambda, fieldName, type), skipped, nextCaptured,
|
||||
original != null ? original.getIndex() : -1);
|
||||
if (original != null) {
|
||||
info.setLambda(original.getLambda());
|
||||
}
|
||||
return addCapturedParameter(info);
|
||||
}
|
||||
|
||||
private void addParameter(ParameterInfo info) {
|
||||
|
||||
+61
-73
@@ -20,9 +20,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.asm4.Opcodes;
|
||||
import org.jetbrains.asm4.Type;
|
||||
import org.jetbrains.asm4.tree.AbstractInsnNode;
|
||||
import org.jetbrains.asm4.tree.FieldInsnNode;
|
||||
import org.jetbrains.asm4.tree.MethodNode;
|
||||
import org.jetbrains.jet.codegen.StackValue;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -53,52 +52,35 @@ public class RegeneratedLambdaFieldRemapper extends LambdaFieldRemapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractInsnNode doTransform(
|
||||
MethodNode node, FieldInsnNode fieldInsnNode, CapturedParamInfo capturedField
|
||||
) {
|
||||
boolean isRecaptured = isRecapturedLambdaType(fieldInsnNode.owner);
|
||||
|
||||
if (!isRecaptured && capturedField.getLambda() != null) {
|
||||
//strict inlining
|
||||
return super.doTransform(node, fieldInsnNode, capturedField);
|
||||
}
|
||||
|
||||
AbstractInsnNode loadThis = getPreviousThis(fieldInsnNode);
|
||||
|
||||
int opcode = Opcodes.GETSTATIC;
|
||||
|
||||
String descriptor = Type.getObjectType(newOwnerType).getDescriptor();
|
||||
|
||||
//HACK: it would be reverted again to ALOAD 0 later
|
||||
FieldInsnNode thisStub = new FieldInsnNode(opcode, newOwnerType, "$$$this", descriptor);
|
||||
|
||||
node.instructions.insertBefore(loadThis, thisStub);
|
||||
node.instructions.remove(loadThis);
|
||||
|
||||
fieldInsnNode.owner = newOwnerType;
|
||||
fieldInsnNode.name = isRecaptured || capturedField.getRecapturedFrom() != null ? LambdaTransformer.getNewFieldName(capturedField.getFieldName()) : capturedField.getFieldName();
|
||||
|
||||
return fieldInsnNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CapturedParamInfo> markRecaptured(List<CapturedParamInfo> originalCaptured, LambdaInfo lambda) {
|
||||
List<CapturedParamInfo> captured = parameters.getCaptured();
|
||||
for (CapturedParamInfo originalField : originalCaptured) {
|
||||
for (CapturedParamInfo capturedParamInfo : captured) {
|
||||
if (capturedParamInfo.getRecapturedFrom() == lambda) {
|
||||
if (capturedParamInfo.getFieldName().equals(LambdaTransformer.getNewFieldName(originalField.getFieldName()))) {
|
||||
originalField.setRecapturedFrom(lambda);//just mark recaptured
|
||||
public void addCapturedFields(LambdaInfo lambdaInfo, ParametersBuilder builder) {
|
||||
if (canProcess(lambdaInfo.getLambdaClassType().getInternalName())) {
|
||||
List<CapturedParamInfo> captured = parameters.getCaptured();
|
||||
for (CapturedParamInfo originalField : lambdaInfo.getCapturedVars()) {
|
||||
CapturedParamInfo foundField = null;
|
||||
for (CapturedParamInfo capturedParamInfo : captured) {
|
||||
if (capturedParamInfo.getContainingLambdaName().equals(originalField.getContainingLambdaName())) {
|
||||
if (capturedParamInfo.getFieldName().equals(LambdaTransformer.getNewFieldName(originalField.getFieldName()))) {
|
||||
foundField = originalField;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (foundField == null) {
|
||||
throw new IllegalStateException("Captured parameter should exists in outer context: " + originalField.getFieldName());
|
||||
}
|
||||
|
||||
CapturedParamInfo info = builder.addCapturedParam(foundField, foundField);
|
||||
}
|
||||
} else {
|
||||
//in case when inlining lambda into another one inside inline function
|
||||
parent.addCapturedFields(lambdaInfo, builder);
|
||||
}
|
||||
return originalCaptured;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProcess(String owner, String currentLambdaType) {
|
||||
return super.canProcess(owner, currentLambdaType) || isRecapturedLambdaType(owner);
|
||||
public boolean canProcess(@NotNull String fieldOwner) {
|
||||
return super.canProcess(fieldOwner) || isRecapturedLambdaType(fieldOwner);
|
||||
}
|
||||
|
||||
private boolean isRecapturedLambdaType(String owner) {
|
||||
@@ -107,8 +89,11 @@ public class RegeneratedLambdaFieldRemapper extends LambdaFieldRemapper {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public CapturedParamInfo findField(FieldInsnNode fieldInsnNode, Collection<CapturedParamInfo> captured) {
|
||||
if (isRecapturedLambdaType(fieldInsnNode.owner)) {
|
||||
public CapturedParamInfo findField(@NotNull FieldInsnNode fieldInsnNode, @NotNull Collection<CapturedParamInfo> captured) {
|
||||
boolean searchInParent = !canProcess(fieldInsnNode.owner);
|
||||
if (searchInParent) {
|
||||
return parent.findField(fieldInsnNode);
|
||||
} else if (isRecapturedLambdaType(fieldInsnNode.owner)) {
|
||||
LambdaInfo info = recapturedLambdas.get(fieldInsnNode.owner);
|
||||
return super.findField(fieldInsnNode, info.getCapturedVars());
|
||||
}
|
||||
@@ -117,39 +102,42 @@ public class RegeneratedLambdaFieldRemapper extends LambdaFieldRemapper {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldPatch(@NotNull FieldInsnNode node) {
|
||||
//parent is inlined so we need patch instruction chain
|
||||
return shouldPatchByMe(node) || parent.shouldPatch(node);
|
||||
@Nullable
|
||||
public CapturedParamInfo findFieldInMyCaptured(@NotNull FieldInsnNode fieldInsnNode) {
|
||||
if (isRecapturedLambdaType(fieldInsnNode.owner)) {
|
||||
LambdaInfo info = recapturedLambdas.get(fieldInsnNode.owner);
|
||||
return super.findField(fieldInsnNode, info.getCapturedVars());
|
||||
}
|
||||
else {
|
||||
return super.findField(fieldInsnNode, parameters.getCaptured());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldPatchByMe(@NotNull FieldInsnNode node) {
|
||||
//parent is inlined so we need patch instruction chain
|
||||
//aloading inlined this
|
||||
return parent.isRoot() && node.owner.equals(getLambdaInternalName()) && node.name.equals("this$0");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Nullable
|
||||
@Override
|
||||
public AbstractInsnNode patch(@NotNull FieldInsnNode fieldInsnNode, @NotNull MethodNode node) {
|
||||
if (!shouldPatchByMe(fieldInsnNode)) {
|
||||
return parent.patch(fieldInsnNode, node);
|
||||
}
|
||||
//parent is inlined so we need patch instruction chain
|
||||
AbstractInsnNode previous = fieldInsnNode.getPrevious();
|
||||
AbstractInsnNode nextInstruction = fieldInsnNode.getNext();
|
||||
if (!(nextInstruction instanceof FieldInsnNode)) {
|
||||
throw new IllegalStateException(
|
||||
"Instruction after inlined one should be field access: " + nextInstruction);
|
||||
}
|
||||
if (!(previous instanceof FieldInsnNode)) {
|
||||
throw new IllegalStateException("Instruction before inlined one should be field access: " + previous);
|
||||
}
|
||||
FieldInsnNode next = (FieldInsnNode) nextInstruction;
|
||||
node.instructions.remove(next.getPrevious());
|
||||
next.owner = Type.getType(((FieldInsnNode) previous).desc).getInternalName();
|
||||
next.name = LambdaTransformer.getNewFieldName(next.name);
|
||||
public StackValue getFieldForInline(@NotNull FieldInsnNode node, @Nullable StackValue prefix) {
|
||||
FieldInsnNode fin = new FieldInsnNode(node.getOpcode(), node.owner, node.name.substring(3), node.desc);
|
||||
CapturedParamInfo field = findFieldInMyCaptured(fin);
|
||||
|
||||
return next;
|
||||
boolean searchInParent = false;
|
||||
if (field == null) {
|
||||
field = findFieldInMyCaptured(new FieldInsnNode(Opcodes.GETSTATIC, oldOwnerType, "this$0", Type.getObjectType(parent.getLambdaInternalName()).getDescriptor()));
|
||||
searchInParent = true;
|
||||
if (field == null) {
|
||||
throw new IllegalStateException("Could find captured this " + getLambdaInternalName());
|
||||
}
|
||||
}
|
||||
|
||||
String newName = field.getContainingLambdaName().equals(getLambdaInternalName())
|
||||
? field.getFieldName()
|
||||
: LambdaTransformer.getNewFieldName(field.getFieldName());
|
||||
StackValue result =
|
||||
StackValue.composed(prefix == null ? StackValue.local(0, Type.getObjectType(getLambdaInternalName())) : prefix,
|
||||
StackValue.field(field.getType(),
|
||||
Type.getObjectType(newOwnerType), /*TODO owner type*/
|
||||
newName, false)
|
||||
);
|
||||
|
||||
return searchInParent ? parent.getFieldForInline(node, result) : result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import org.jetbrains.asm4.Label;
|
||||
import org.jetbrains.asm4.MethodVisitor;
|
||||
import org.jetbrains.asm4.Opcodes;
|
||||
import org.jetbrains.asm4.commons.InstructionAdapter;
|
||||
import org.jetbrains.asm4.tree.FieldInsnNode;
|
||||
import org.jetbrains.jet.codegen.StackValue;
|
||||
|
||||
public class RemapVisitor extends InstructionAdapter {
|
||||
|
||||
@@ -29,12 +31,14 @@ public class RemapVisitor extends InstructionAdapter {
|
||||
private final VarRemapper remapper;
|
||||
|
||||
private final boolean remapReturn;
|
||||
private LambdaFieldRemapper nodeRemapper;
|
||||
|
||||
protected RemapVisitor(MethodVisitor mv, Label end, VarRemapper.ParamRemapper remapper, boolean remapReturn) {
|
||||
protected RemapVisitor(MethodVisitor mv, Label end, VarRemapper.ParamRemapper remapper, boolean remapReturn, LambdaFieldRemapper nodeRemapper) {
|
||||
super(InlineCodegenUtil.API, mv);
|
||||
this.end = end;
|
||||
this.remapper = remapper;
|
||||
this.remapReturn = remapReturn;
|
||||
this.nodeRemapper = nodeRemapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -57,6 +61,22 @@ public class RemapVisitor extends InstructionAdapter {
|
||||
remapper.visitVarInsn(opcode, var, new InstructionAdapter(mv));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
|
||||
if (name.startsWith("$$$")) {
|
||||
if (nodeRemapper instanceof RegeneratedLambdaFieldRemapper || nodeRemapper.isRoot()) {
|
||||
FieldInsnNode fin = new FieldInsnNode(opcode, owner, name, desc);
|
||||
StackValue inline = nodeRemapper.getFieldForInline(fin, null);
|
||||
inline.put(inline.type, this);
|
||||
} else {
|
||||
super.visitFieldInsn(opcode, owner, name, desc);
|
||||
}
|
||||
}
|
||||
else {
|
||||
super.visitFieldInsn(opcode, owner, name, desc);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLocalVariable(
|
||||
String name, String desc, String signature, Label start, Label end, int index
|
||||
@@ -84,15 +104,6 @@ public class RemapVisitor extends InstructionAdapter {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
|
||||
if (name.equals("$$$this")) {
|
||||
super.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
} else {
|
||||
super.visitFieldInsn(opcode, owner, name, desc);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO not skip for lambdas
|
||||
@Override
|
||||
public void visitLineNumber(int line, Label start) {
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
import builders.*
|
||||
|
||||
inline fun testAllInline(f: () -> String) : String {
|
||||
val args = array("1", "2", "3")
|
||||
val result =
|
||||
html {
|
||||
val htmlVal = 0
|
||||
head {
|
||||
title { +"XML encoding with Kotlin" }
|
||||
}
|
||||
body {
|
||||
var bodyVar = 1
|
||||
h1 { +"XML encoding with Kotlin" }
|
||||
p { +"this format can be used as an alternative markup to XML" }
|
||||
|
||||
// an element with attributes and text content
|
||||
a(href = "http://jetbrains.com/kotlin") { +"Kotlin" }
|
||||
|
||||
// mixed content
|
||||
p {
|
||||
+"This is some"
|
||||
b { +"mixed" }
|
||||
+"text. For more see the"
|
||||
a(href = "http://jetbrains.com/kotlin") { +"Kotlin" }
|
||||
+"project"
|
||||
}
|
||||
p { +"some text" }
|
||||
|
||||
// content generated from command-line arguments
|
||||
p {
|
||||
+"Command line arguments were:"
|
||||
ul {
|
||||
for (arg in args)
|
||||
li { +arg; +"$htmlVal"; +"$bodyVar"; +"${f()}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString()!!
|
||||
}
|
||||
|
||||
inline fun testHtmlNoInline(f: () -> String) : String {
|
||||
val args = array("1", "2", "3")
|
||||
val result =
|
||||
htmlNoInline() {
|
||||
val htmlVal = 0
|
||||
head {
|
||||
title { +"XML encoding with Kotlin" }
|
||||
}
|
||||
body {
|
||||
var bodyVar = 1
|
||||
h1 { +"XML encoding with Kotlin" }
|
||||
p { +"this format can be used as an alternative markup to XML" }
|
||||
|
||||
// an element with attributes and text content
|
||||
a(href = "http://jetbrains.com/kotlin") { +"Kotlin" }
|
||||
|
||||
// mixed content
|
||||
p {
|
||||
+"This is some"
|
||||
b { +"mixed" }
|
||||
+"text. For more see the"
|
||||
a(href = "http://jetbrains.com/kotlin") { +"Kotlin" }
|
||||
+"project"
|
||||
}
|
||||
p { +"some text" }
|
||||
|
||||
// content generated from command-line arguments
|
||||
p {
|
||||
+"Command line arguments were:"
|
||||
ul {
|
||||
for (arg in args)
|
||||
li { +arg; +"$htmlVal"; +"$bodyVar"; +"${f()}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString()!!
|
||||
}
|
||||
|
||||
inline fun testBodyNoInline(f: () -> String) : String {
|
||||
val args = array("1", "2", "3")
|
||||
val result =
|
||||
html {
|
||||
val htmlVal = 0
|
||||
head {
|
||||
title { +"XML encoding with Kotlin" }
|
||||
}
|
||||
bodyNoInline {
|
||||
var bodyVar = 1
|
||||
h1 { +"XML encoding with Kotlin" }
|
||||
p { +"this format can be used as an alternative markup to XML" }
|
||||
|
||||
// an element with attributes and text content
|
||||
a(href = "http://jetbrains.com/kotlin") { +"Kotlin" }
|
||||
|
||||
// mixed content
|
||||
p {
|
||||
+"This is some"
|
||||
b { +"mixed" }
|
||||
+"text. For more see the"
|
||||
a(href = "http://jetbrains.com/kotlin") { +"Kotlin" }
|
||||
+"project"
|
||||
}
|
||||
p { +"some text" }
|
||||
|
||||
// content generated from command-line arguments
|
||||
p {
|
||||
+"Command line arguments were:"
|
||||
ul {
|
||||
for (arg in args)
|
||||
li { +arg; +"$htmlVal"; +"$bodyVar"; +"${f()}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString()!!
|
||||
}
|
||||
|
||||
inline fun testBodyHtmlNoInline(f: () -> String) : String {
|
||||
val args = array("1", "2", "3")
|
||||
val result =
|
||||
htmlNoInline {
|
||||
val htmlVal = 0
|
||||
head {
|
||||
title { +"XML encoding with Kotlin" }
|
||||
}
|
||||
bodyNoInline {
|
||||
var bodyVar = 1
|
||||
h1 { +"XML encoding with Kotlin" }
|
||||
p { +"this format can be used as an alternative markup to XML" }
|
||||
|
||||
// an element with attributes and text content
|
||||
a(href = "http://jetbrains.com/kotlin") { +"Kotlin" }
|
||||
|
||||
// mixed content
|
||||
p {
|
||||
+"This is some"
|
||||
b { +"mixed" }
|
||||
+"text. For more see the"
|
||||
a(href = "http://jetbrains.com/kotlin") { +"Kotlin" }
|
||||
+"project"
|
||||
}
|
||||
p { +"some text" }
|
||||
|
||||
// content generated from command-line arguments
|
||||
p {
|
||||
+"Command line arguments were:"
|
||||
ul {
|
||||
for (arg in args)
|
||||
li { +arg; +"$htmlVal"; +"$bodyVar"; +"${f()}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString()!!
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
var expected = testAllInline({"x"});
|
||||
print(expected + " " + testHtmlNoInline({"x"}))
|
||||
|
||||
if (expected != testHtmlNoInline({"x"})) return "fail 1: ${testHtmlNoInline({"x"})}\nbut expected\n${expected} "
|
||||
if (expected != testBodyNoInline({"x"})) return "fail 2: ${testBodyNoInline({"x"})}\nbut expected\n${expected} "
|
||||
if (expected != testBodyHtmlNoInline({"x"})) return "fail 3: ${testBodyHtmlNoInline({"x"})}\nbut expected\n${expected} "
|
||||
|
||||
var captured = "x"
|
||||
if (expected != testHtmlNoInline({captured})) return "fail 4: ${testHtmlNoInline({captured})}\nbut expected\n${expected} "
|
||||
if (expected != testBodyNoInline({captured})) return "fail 5: ${testBodyNoInline({captured})}\nbut expected\n${expected} "
|
||||
if (expected != testBodyHtmlNoInline({captured})) return "fail 6: ${testBodyHtmlNoInline({captured})}\nbut expected\n${expected} "
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package builders
|
||||
|
||||
import java.util.ArrayList
|
||||
import java.util.HashMap
|
||||
|
||||
trait Element {
|
||||
fun render(builder: StringBuilder, indent: String)
|
||||
|
||||
override fun toString(): String {
|
||||
val builder = StringBuilder()
|
||||
render(builder, "")
|
||||
return builder.toString()
|
||||
}
|
||||
}
|
||||
|
||||
class TextElement(val text: String) : Element {
|
||||
override fun render(builder: StringBuilder, indent: String) {
|
||||
builder.append("$indent$text\n")
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Tag(val name: String) : Element {
|
||||
val children = ArrayList<Element>()
|
||||
val attributes = HashMap<String, String>()
|
||||
|
||||
inline protected fun initTag<T : Element>(tag: T, init: T.() -> Unit): T {
|
||||
tag.init()
|
||||
children.add(tag)
|
||||
return tag
|
||||
}
|
||||
|
||||
override fun render(builder: StringBuilder, indent: String) {
|
||||
builder.append("$indent<$name${renderAttributes()}>\n")
|
||||
for (c in children) {
|
||||
c.render(builder, indent + " ")
|
||||
}
|
||||
builder.append("$indent</$name>\n")
|
||||
}
|
||||
|
||||
private fun renderAttributes(): String? {
|
||||
val builder = StringBuilder()
|
||||
for (a in attributes.keySet()) {
|
||||
builder.append(" $a=\"${attributes[a]}\"")
|
||||
}
|
||||
return builder.toString()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TagWithText(name: String) : Tag(name) {
|
||||
fun String.plus() {
|
||||
children.add(TextElement(this))
|
||||
}
|
||||
}
|
||||
|
||||
class HTML() : TagWithText("html") {
|
||||
inline fun head(init: Head.() -> Unit) = initTag(Head(), init)
|
||||
|
||||
inline fun body(init: Body.() -> Unit) = initTag(Body(), init)
|
||||
|
||||
fun bodyNoInline(init: Body.() -> Unit) = initTag(Body(), init)
|
||||
}
|
||||
|
||||
class Head() : TagWithText("head") {
|
||||
inline fun title(init: Title.() -> Unit) = initTag(Title(), init)
|
||||
}
|
||||
|
||||
class Title() : TagWithText("title")
|
||||
|
||||
abstract class BodyTag(name: String) : TagWithText(name) {
|
||||
inline fun b(init: B.() -> Unit) = initTag(B(), init)
|
||||
inline fun p(init: P.() -> Unit) = initTag(P(), init)
|
||||
inline fun pNoInline(init: P.() -> Unit) = initTag(P(), init)
|
||||
inline fun h1(init: H1.() -> Unit) = initTag(H1(), init)
|
||||
inline fun ul(init: UL.() -> Unit) = initTag(UL(), init)
|
||||
inline fun a(href: String, init: A.() -> Unit) {
|
||||
val a = initTag(A(), init)
|
||||
a.href = href
|
||||
}
|
||||
}
|
||||
|
||||
class Body() : BodyTag("body")
|
||||
class UL() : BodyTag("ul") {
|
||||
inline fun li(init: LI.() -> Unit) = initTag(LI(), init)
|
||||
}
|
||||
|
||||
class B() : BodyTag("b")
|
||||
class LI() : BodyTag("li")
|
||||
class P() : BodyTag("p")
|
||||
class H1() : BodyTag("h1")
|
||||
class A() : BodyTag("a") {
|
||||
public var href: String
|
||||
get() = attributes["href"]!!
|
||||
set(value) {
|
||||
attributes["href"] = value
|
||||
}
|
||||
}
|
||||
|
||||
inline fun html(init: HTML.() -> Unit): HTML {
|
||||
val html = HTML()
|
||||
html.init()
|
||||
return html
|
||||
}
|
||||
|
||||
fun htmlNoInline(init: HTML.() -> Unit): HTML {
|
||||
val html = HTML()
|
||||
html.init()
|
||||
return html
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import test.*
|
||||
|
||||
fun test1(param: String): String {
|
||||
var result = "fail1"
|
||||
mfun(param) { a ->
|
||||
noInlineFun(param) { a ->
|
||||
concat("start") {
|
||||
result = doSmth(a).toString()
|
||||
}
|
||||
@@ -11,9 +11,43 @@ fun test1(param: String): String {
|
||||
return result
|
||||
}
|
||||
|
||||
fun test11(param: String): String {
|
||||
var result = "fail1"
|
||||
noInlineFun("stub") { a ->
|
||||
concat("start") {
|
||||
result = doSmth(param).toString()
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
inline fun test2(param: () -> String): String {
|
||||
var result = "fail1"
|
||||
noInlineFun("stub") { a ->
|
||||
concat(param()) {
|
||||
result = doSmth(param()).toString()
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
inline fun test22(param: () -> String): String {
|
||||
var result = "fail1"
|
||||
{{result = param()}()}()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
fun box(): String {
|
||||
if (test1("start") != "start") return "fail1"
|
||||
if (test1("nostart") != "nostart") return "fail2"
|
||||
if (test1("start") != "start") return "fail1: ${test1("start")}"
|
||||
if (test1("nostart") != "nostart") return "fail2: ${test1("nostart")}"
|
||||
if (test11("start") != "start") return "fail3: ${test11("start")}"
|
||||
|
||||
if (test2({"start"}) != "start") return "fail4: ${test2({"start"})}"
|
||||
if (test22({"start"}) != "start") return "fail5: ${test22({"start"})}"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -4,7 +4,7 @@ fun concat(suffix: String, l: (s: String) -> Unit) {
|
||||
l(suffix)
|
||||
}
|
||||
|
||||
fun <T> mfun(arg: T, f: (T) -> Unit) {
|
||||
fun <T> noInlineFun(arg: T, f: (T) -> Unit) {
|
||||
f(arg)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import test.*
|
||||
|
||||
inline fun test1(param: () -> String): String {
|
||||
var result = "fail"
|
||||
inlineFun("1") { c ->
|
||||
{
|
||||
inlineFun("2") { a ->
|
||||
{
|
||||
{
|
||||
result = param() + c + a
|
||||
}()
|
||||
}()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
inline fun test2(param: () -> String): String {
|
||||
var result = "fail"
|
||||
inlineFun("2") { a ->
|
||||
{
|
||||
{
|
||||
result = param() + a
|
||||
}()
|
||||
}()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
inline fun test3(param: () -> String): String {
|
||||
var result = "fail"
|
||||
inlineFun("2") { d ->
|
||||
inlineFun("1") { c ->
|
||||
{
|
||||
inlineFun("2") { a ->
|
||||
{
|
||||
{
|
||||
result = param() + c + a
|
||||
}()
|
||||
}()
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
fun box(): String {
|
||||
if (test1({"start"}) != "start12") return "fail1: ${test1({"start"})}"
|
||||
if (test2({"start"}) != "start2") return "fail2: ${test2({"start"})}"
|
||||
if (test3({"start"}) != "start12") return "fail3: ${test3({"start"})}"
|
||||
|
||||
var captured1 = "sta";
|
||||
val captured2 = "rt";
|
||||
if (test1({captured1 + captured2}) != "start12") return "fail4: ${test1({captured1 + captured2})}"
|
||||
if (test2({captured1 + captured2}) != "start2") return "fail5: ${test2({captured1 + captured2})}"
|
||||
if (test3({captured1 + captured2}) != "start12") return "fail6: ${test3({captured1 + captured2})}"
|
||||
|
||||
return {
|
||||
if (test1 { captured1 + captured2 } != "start12") "fail7: ${test1 { captured1 + captured2 }}"
|
||||
else if (test2 { captured1 + captured2 } != "start2") "fail8: ${test2 { captured1 + captured2 }}"
|
||||
else if (test3 { captured1 + captured2 } != "start12") "fail9: ${test3 { captured1 + captured2 }}"
|
||||
else "OK"
|
||||
} ()
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
inline fun <T> inlineFun(arg: T, f: (T) -> Unit) {
|
||||
f(arg)
|
||||
}
|
||||
@@ -6,7 +6,22 @@ fun Data.test1(d: Data) : Long {
|
||||
with(input) {
|
||||
result = use<Long>{
|
||||
val output = Output(d)
|
||||
useNoInline<Long>{
|
||||
use<Long>{
|
||||
data()
|
||||
copyTo(output, 10)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun Data.test2(d: Data) : Long {
|
||||
val input = Input(this)
|
||||
var result = 10.toLong()
|
||||
with2(input) {
|
||||
result = use<Long>{
|
||||
val output = Output(d)
|
||||
useNoInline<Long>{
|
||||
data()
|
||||
copyTo(output, 10)
|
||||
}
|
||||
@@ -21,5 +36,8 @@ fun box(): String {
|
||||
val result = Data().test1(Data())
|
||||
if (result != 100.toLong()) return "test1: ${result}"
|
||||
|
||||
val result2 = Data().test2(Data())
|
||||
if (result2 != 100.toLong()) return "test2: ${result2}"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
@@ -25,3 +25,6 @@ public fun <R> useNoInline(block: ()-> R) : R {
|
||||
public fun Input.copyTo(output: Output, size: Int): Long {
|
||||
return output.doOutput(this.data()).toLong()
|
||||
}
|
||||
|
||||
|
||||
public inline fun with2<T>(receiver : T, body : T.() -> Unit) : Unit = {receiver.body()}()
|
||||
|
||||
+10
@@ -41,6 +41,11 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxCodegenT
|
||||
doTestMultiFile("compiler/testData/codegen/boxInline/builders");
|
||||
}
|
||||
|
||||
@TestMetadata("buildersAndLambdaCapturing")
|
||||
public void testBuildersAndLambdaCapturing() throws Exception {
|
||||
doTestMultiFile("compiler/testData/codegen/boxInline/buildersAndLambdaCapturing");
|
||||
}
|
||||
|
||||
@TestMetadata("captureInlinable")
|
||||
public void testCaptureInlinable() throws Exception {
|
||||
doTestMultiFile("compiler/testData/codegen/boxInline/captureInlinable");
|
||||
@@ -141,6 +146,11 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxCodegenT
|
||||
doTestMultiFile("compiler/testData/codegen/boxInline/noInlineLambdaChain");
|
||||
}
|
||||
|
||||
@TestMetadata("noInlineLambdaChainWithCapturedInline")
|
||||
public void testNoInlineLambdaChainWithCapturedInline() throws Exception {
|
||||
doTestMultiFile("compiler/testData/codegen/boxInline/noInlineLambdaChainWithCapturedInline");
|
||||
}
|
||||
|
||||
@TestMetadata("noInlineLambdaX2")
|
||||
public void testNoInlineLambdaX2() throws Exception {
|
||||
doTestMultiFile("compiler/testData/codegen/boxInline/noInlineLambdaX2");
|
||||
|
||||
+10
@@ -41,6 +41,11 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
|
||||
doBoxTest("compiler/testData/codegen/boxInline/builders");
|
||||
}
|
||||
|
||||
@TestMetadata("buildersAndLambdaCapturing")
|
||||
public void testBuildersAndLambdaCapturing() throws Exception {
|
||||
doBoxTest("compiler/testData/codegen/boxInline/buildersAndLambdaCapturing");
|
||||
}
|
||||
|
||||
@TestMetadata("captureInlinable")
|
||||
public void testCaptureInlinable() throws Exception {
|
||||
doBoxTest("compiler/testData/codegen/boxInline/captureInlinable");
|
||||
@@ -141,6 +146,11 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
|
||||
doBoxTest("compiler/testData/codegen/boxInline/noInlineLambdaChain");
|
||||
}
|
||||
|
||||
@TestMetadata("noInlineLambdaChainWithCapturedInline")
|
||||
public void testNoInlineLambdaChainWithCapturedInline() throws Exception {
|
||||
doBoxTest("compiler/testData/codegen/boxInline/noInlineLambdaChainWithCapturedInline");
|
||||
}
|
||||
|
||||
@TestMetadata("noInlineLambdaX2")
|
||||
public void testNoInlineLambdaX2() throws Exception {
|
||||
doBoxTest("compiler/testData/codegen/boxInline/noInlineLambdaX2");
|
||||
|
||||
Reference in New Issue
Block a user