Support regeneration of non inlinable lambdas/local funs/sams

This commit is contained in:
Mikhael Bogdanov
2014-02-03 15:04:06 +04:00
parent b7e12ba524
commit 7995ffb798
28 changed files with 1853 additions and 389 deletions
@@ -42,6 +42,7 @@ import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -302,25 +303,38 @@ public class AsmUtil {
}
public static void genClosureFields(CalculatedClosure closure, ClassBuilder v, JetTypeMapper typeMapper) {
List<Pair<String, Type>> allFields = new ArrayList<Pair<String, Type>>();
ClassifierDescriptor captureThis = closure.getCaptureThis();
int access = NO_FLAG_PACKAGE_PRIVATE | ACC_SYNTHETIC | ACC_FINAL;
if (captureThis != null) {
v.newField(null, access, CAPTURED_THIS_FIELD, typeMapper.mapType(captureThis).getDescriptor(), null,
null);
allFields.add(Pair.create(CAPTURED_THIS_FIELD, typeMapper.mapType(captureThis)));
}
JetType captureReceiverType = closure.getCaptureReceiverType();
if (captureReceiverType != null) {
v.newField(null, access, CAPTURED_RECEIVER_FIELD, typeMapper.mapType(captureReceiverType).getDescriptor(),
null, null);
allFields.add(Pair.create(CAPTURED_RECEIVER_FIELD, typeMapper.mapType(captureReceiverType)));
}
List<Pair<String, Type>> fields = closure.getRecordedFields();
for (Pair<String, Type> field : fields) {
v.newField(null, access, field.first, field.second.getDescriptor(), null, null);
allFields.addAll(closure.getRecordedFields());
genClosureFields(allFields, v);
}
public static void genClosureFields(List<Pair<String, Type>> allFields, ClassBuilder builder) {
//noinspection PointlessBitwiseExpression
int access = NO_FLAG_PACKAGE_PRIVATE | ACC_SYNTHETIC | ACC_FINAL;
for (Pair<String, Type> field : allFields) {
builder.newField(null, access, field.first, field.second.getDescriptor(), null, null);
}
}
public static List<FieldInfo> transformCapturedParams(List<Pair<String, Type>> allFields, Type owner) {
List<FieldInfo> result = new ArrayList<FieldInfo>();
for (Pair<String, Type> field : allFields) {
result.add(FieldInfo.createForHiddenField(owner, field.second, field.first));
}
return result;
}
public static int genAssignInstanceFieldFromParam(FieldInfo info, int index, InstructionAdapter iv) {
assert !info.isStatic();
Type fieldType = info.getFieldType();
@@ -136,6 +136,13 @@ public final class ClassFileFactory extends GenerationStateAware implements Outp
return newVisitor(type, sourceFile);
}
public ClassBuilder forLambdaInlining(Type lambaType, PsiFile sourceFile) {
if (isPrimitive(lambaType)) {
throw new IllegalStateException("Codegen for primitive type is not possible: " + lambaType);
}
return newVisitor(lambaType, sourceFile);
}
@NotNull
public ClassBuilder forPackagePart(@NotNull Type asmType, @NotNull PsiFile sourceFile) {
return newVisitor(asmType, sourceFile);
@@ -28,15 +28,12 @@ import org.jetbrains.asm4.commons.Method;
import org.jetbrains.jet.codegen.binding.CalculatedClosure;
import org.jetbrains.jet.codegen.context.CodegenContext;
import org.jetbrains.jet.codegen.context.LocalLookup;
import org.jetbrains.jet.codegen.context.MethodContext;
import org.jetbrains.jet.codegen.signature.BothSignatureWriter;
import org.jetbrains.jet.codegen.signature.JvmMethodSignature;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.codegen.state.JetTypeMapper;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.JetDeclarationWithBody;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
import org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils;
import org.jetbrains.jet.lang.resolve.name.Name;
@@ -210,6 +207,16 @@ public class ClosureCodegen extends ParentCodegenAwareImpl {
private Method generateConstructor(@NotNull ClassBuilder cv) {
List<FieldInfo> args = calculateConstructorParameters(typeMapper, closure, asmType);
return generateConstructor(cv, args, fun, superClass, state);
}
public static Method generateConstructor(
@NotNull ClassBuilder cv,
@NotNull List<FieldInfo> args,
@Nullable PsiElement fun,
@NotNull Type superClass,
@NotNull GenerationState state
) {
Type[] argTypes = fieldListToTypeArray(args);
Method constructor = new Method("<init>", Type.VOID_TYPE, argTypes);
@@ -33,7 +33,9 @@ import org.jetbrains.asm4.Type;
import org.jetbrains.asm4.commons.InstructionAdapter;
import org.jetbrains.asm4.commons.Method;
import org.jetbrains.jet.codegen.asm.InlineCodegen;
import org.jetbrains.jet.codegen.asm.InlineCodegenUtil;
import org.jetbrains.jet.codegen.asm.Inliner;
import org.jetbrains.jet.codegen.asm.NameGenerator;
import org.jetbrains.jet.codegen.binding.CalculatedClosure;
import org.jetbrains.jet.codegen.binding.CodegenBinding;
import org.jetbrains.jet.codegen.binding.MutableClosure;
@@ -100,6 +102,8 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
@Nullable
private final MemberCodegen parentCodegen;
private NameGenerator inlineNameGenerator;
/*
* When we create a temporary variable to hold some value not to compute it many times
* we put it into this map to emit access to that variable instead of evaluating the whole expression
@@ -1360,20 +1364,25 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
public void pushClosureOnStack(CalculatedClosure closure, boolean ignoreThisAndReceiver, Inliner inliner) {
if (closure != null) {
int paramIndex = 0;
if (!ignoreThisAndReceiver) {
ClassDescriptor captureThis = closure.getCaptureThis();
if (captureThis != null) {
StackValue thisOrOuter = generateThisOrOuter(captureThis, false);
thisOrOuter.put(OBJECT_TYPE, v);
inliner.putInLocal(OBJECT_TYPE, thisOrOuter);
if (inliner.shouldPutValue(OBJECT_TYPE, thisOrOuter, context, null)) {
thisOrOuter.put(OBJECT_TYPE, v);
}
inliner.putCapturedInLocal(OBJECT_TYPE, thisOrOuter, null, paramIndex++);
}
JetType captureReceiver = closure.getCaptureReceiverType();
if (captureReceiver != null) {
Type asmType = typeMapper.mapType(captureReceiver);
StackValue.Local capturedReceiver = StackValue.local(context.isStatic() ? 0 : 1, asmType);
capturedReceiver.put(asmType, v);
inliner.putInLocal(asmType, capturedReceiver);
if (inliner.shouldPutValue(asmType, capturedReceiver, context, null)) {
capturedReceiver.put(asmType, v);
}
inliner.putCapturedInLocal(asmType, capturedReceiver, null, paramIndex++);
}
}
@@ -1383,10 +1392,10 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
sharedVarType = typeMapper.mapType((VariableDescriptor) entry.getKey());
}
StackValue capturedVar = entry.getValue().getOuterValue(this);
if (inliner.shouldPutValue(sharedVarType, capturedVar, context)) {
if (inliner.shouldPutValue(sharedVarType, capturedVar, context, null)) {
capturedVar.put(sharedVarType, v);
}
inliner.putInLocal(sharedVarType, capturedVar);
inliner.putCapturedInLocal(sharedVarType, capturedVar, null, paramIndex++);
}
}
}
@@ -2028,7 +2037,8 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
) {
if (callable instanceof CallableMethod) {
CallableMethod callableMethod = (CallableMethod) callable;
invokeMethodWithArguments(callableMethod, resolvedCall, receiver);
invokeMethodWithArguments(call, callableMethod, resolvedCall, receiver);
//noinspection ConstantConditions
Type returnType = typeMapper.mapReturnType(resolvedCall.getResultingDescriptor());
StackValue.coerce(callableMethod.getReturnType(), returnType, v);
return StackValue.onStack(returnType);
@@ -2104,6 +2114,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
public void invokeMethodWithArguments(
@Nullable Call call,
@NotNull CallableMethod callableMethod,
@NotNull ResolvedCall<? extends CallableDescriptor> resolvedCall,
@NotNull StackValue receiver
@@ -2354,12 +2365,12 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
JetExpression argumentExpression = valueArgument.getArgumentExpression();
assert argumentExpression != null : valueArgument.asElement().getText();
if (inliner.isInliningClosure(argumentExpression)) {
if (inliner.isInliningClosure(argumentExpression, valueParameter)) {
inliner.rememberClosure((JetFunctionLiteralExpression) argumentExpression, parameterType);
putInLocal = false;
} else {
StackValue value = gen(argumentExpression);
if (inliner.shouldPutValue(parameterType, value, context)) {
if (inliner.shouldPutValue(parameterType, value, context, valueParameter)) {
value.put(parameterType, v);
}
valueIfPresent = value;
@@ -2377,7 +2388,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
}
if (putInLocal) {
inliner.putInLocal(parameterType, valueIfPresent);
inliner.putInLocal(parameterType, valueIfPresent, valueParameter);
}
}
return mask;
@@ -3342,7 +3353,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
ConstructorDescriptor originalOfSamAdapter = (ConstructorDescriptor) SamCodegenUtil.getOriginalIfSamAdapter(constructorDescriptor);
CallableMethod method = typeMapper.mapToCallableMethod(originalOfSamAdapter == null ? constructorDescriptor : originalOfSamAdapter);
invokeMethodWithArguments(method, resolvedCall, StackValue.none());
invokeMethodWithArguments(null, method, resolvedCall, StackValue.none());
return StackValue.onStack(type);
}
@@ -3913,4 +3924,14 @@ The "returned" value of try expression with no finally is either the last expres
public MethodContext getContext() {
return context;
}
public NameGenerator getInlineNameGenerator() {
if (inlineNameGenerator == null) {
CodegenContext context = getContext();
String prefix = InlineCodegenUtil.getInlineName(context.getContextDescriptor(), typeMapper);
inlineNameGenerator = new NameGenerator(prefix + "$$inline");
}
return inlineNameGenerator;
}
}
@@ -1557,7 +1557,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
superCallable.invokeWithNotNullAssertion(codegen.v, state, resolvedCall);
}
else {
codegen.invokeMethodWithArguments(superCallable, resolvedCall, StackValue.none());
codegen.invokeMethodWithArguments(null, superCallable, resolvedCall, StackValue.none());
}
}
@@ -1643,7 +1643,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
CallableMethod method = typeMapper.mapToCallableMethod((ConstructorDescriptor) resolvedCall.getResultingDescriptor());
codegen.invokeMethodWithArguments(method, resolvedCall, StackValue.none());
codegen.invokeMethodWithArguments(null, method, resolvedCall, StackValue.none());
}
else {
iv.invokespecial(implClass.getInternalName(), "<init>", "(Ljava/lang/String;I)V");
@@ -130,6 +130,10 @@ public class MemberCodegen extends ParentCodegenAwareImpl {
}
}
public void genClassOrObject(CodegenContext parentContext, JetClassOrObject aClass) {
genClassOrObject(parentContext, aClass, state, this);
}
@NotNull
public ClassBuilder getBuilder() {
return builder;
@@ -0,0 +1,48 @@
/*
* Copyright 2010-2013 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.asm;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.asm4.Type;
import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
public class CapturedParamInfo extends ParameterInfo {
private final String fieldName;
private int shift = 0;
public static final CapturedParamInfo STUB = new CapturedParamInfo("", AsmTypeConstants.OBJECT_TYPE, true, -1, -1);
public CapturedParamInfo(@NotNull String fieldName, @NotNull Type type, boolean skipped, int remapIndex, int index) {
super(type, skipped, remapIndex, index);
this.fieldName = fieldName;
}
public String getFieldName() {
return fieldName;
}
@Override
public int getIndex() {
return shift + super.getIndex();
}
public void setShift(int shift) {
this.shift = shift;
}
}
@@ -0,0 +1,77 @@
/*
* Copyright 2010-2013 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.asm;
import org.jetbrains.asm4.Type;
import java.util.List;
import java.util.Map;
public class ConstructorInvocation {
private final String ownerInternalName;
private final Map<Integer, InlinableAccess> access;
private Type newLambdaType;
private String newConstructorDescriptor;
private List<CapturedParamInfo> recaptured;
ConstructorInvocation(String ownerInternalName, Map<Integer, InlinableAccess> access) {
this.ownerInternalName = ownerInternalName;
this.access = access;
}
public String getOwnerInternalName() {
return ownerInternalName;
}
public boolean isInlinable() {
return !access.isEmpty();
}
public Map<Integer, InlinableAccess> getAccess() {
return access;
}
public Type getNewLambdaType() {
return newLambdaType;
}
public void setNewLambdaType(Type newLambdaType) {
this.newLambdaType = newLambdaType;
}
public String getNewConstructorDescriptor() {
return newConstructorDescriptor;
}
public void setNewConstructorDescriptor(String newConstructorDescriptor) {
this.newConstructorDescriptor = newConstructorDescriptor;
}
public List<CapturedParamInfo> getRecaptured() {
return recaptured;
}
public void setRecaptured(List<CapturedParamInfo> recaptured) {
this.recaptured = recaptured;
}
}
@@ -0,0 +1,47 @@
/*
* Copyright 2010-2013 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.asm;
public class FieldAccess {
private final String name;
private final String type;
private final FieldAccess owner;
private final boolean isThisAccess;
public FieldAccess(String name, String type, FieldAccess owner) {
this.name = name;
this.type = type;
this.owner = owner;
isThisAccess = false;
}
public FieldAccess(String name, String type) {
this.name = "!this!" + name;
this.type = type;
isThisAccess = true;
owner = null;
}
public boolean isThisAccess() {
return isThisAccess;
}
}
@@ -16,18 +16,40 @@
package org.jetbrains.jet.codegen.asm;
class ClosureUsage {
import org.jetbrains.annotations.Nullable;
import java.util.List;
class InlinableAccess {
public final int index;
public final boolean inlinable;
ClosureUsage(int index, boolean isInlinable) {
private final List<ParameterInfo> parameters;
private LambdaInfo info;
InlinableAccess(int index, boolean isInlinable, List<ParameterInfo> parameterInfos) {
this.index = index;
inlinable = isInlinable;
this.parameters = parameterInfos;
}
public boolean isInlinable() {
return inlinable;
}
@Nullable
public LambdaInfo getInfo() {
return info;
}
public void setInfo(LambdaInfo info) {
this.info = info;
}
public List<ParameterInfo> getParameters() {
return parameters;
}
}
@@ -0,0 +1,54 @@
/*
* Copyright 2010-2013 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.asm;
import org.jetbrains.asm4.MethodVisitor;
import org.jetbrains.asm4.Opcodes;
import org.jetbrains.asm4.commons.InstructionAdapter;
public class InlineAdapter extends InstructionAdapter {
private int nextLocalIndex = 0;
public InlineAdapter(MethodVisitor mv, int localsSize) {
super(mv);
nextLocalIndex = localsSize;
}
@Override
public void visitIincInsn(int var, int increment) {
super.visitIincInsn(var, increment);
updateIndex(var, 1);
}
@Override
public void visitVarInsn(int opcode, int var) {
super.visitVarInsn(opcode, var);
updateIndex(var, (opcode == Opcodes.DSTORE || opcode == Opcodes.LSTORE || opcode == Opcodes.DLOAD || opcode == Opcodes.LLOAD ? 2 : 1));
}
private void updateIndex(int var, int varSize) {
int newIndex = var + varSize;
if (newIndex > nextLocalIndex) {
nextLocalIndex = newIndex;
}
}
public int getNextLocalIndex() {
return nextLocalIndex;
}
}
@@ -16,23 +16,21 @@
package org.jetbrains.jet.codegen.asm;
import com.google.common.collect.Lists;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.*;
import org.jetbrains.asm4.commons.InstructionAdapter;
import org.jetbrains.asm4.ClassVisitor;
import org.jetbrains.asm4.Opcodes;
import org.jetbrains.asm4.Type;
import org.jetbrains.asm4.commons.Method;
import org.jetbrains.asm4.tree.*;
import org.jetbrains.asm4.tree.analysis.*;
import org.jetbrains.asm4.tree.analysis.Frame;
import org.jetbrains.asm4.tree.MethodNode;
import org.jetbrains.asm4.util.Textifier;
import org.jetbrains.asm4.util.TraceMethodVisitor;
import org.jetbrains.jet.codegen.*;
import org.jetbrains.jet.codegen.context.CodegenContext;
import org.jetbrains.jet.codegen.context.EnclosedValueDescriptor;
import org.jetbrains.jet.codegen.context.MethodContext;
import org.jetbrains.jet.codegen.context.PackageContext;
import org.jetbrains.jet.codegen.signature.JvmMethodParameterKind;
import org.jetbrains.jet.codegen.signature.JvmMethodParameterSignature;
import org.jetbrains.jet.codegen.signature.JvmMethodSignature;
@@ -44,6 +42,8 @@ import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.types.lang.InlineUtil;
import org.jetbrains.jet.renderer.DescriptorRenderer;
import java.io.IOException;
@@ -56,7 +56,7 @@ import static org.jetbrains.jet.codegen.AsmUtil.*;
public class InlineCodegen implements ParentCodegenAware, Inliner {
public final static String INVOKE = "invoke";
private final JetTypeMapper typeMapper;
private final ExpressionCodegen codegen;
@@ -66,16 +66,10 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
private final boolean disabled;
private final Call call;
private final SimpleFunctionDescriptor functionDescriptor;
private final List<ParameterInfo> tempTypes = new ArrayList<ParameterInfo>();
private final List<ClosureUsage> closures = new ArrayList<ClosureUsage>();
private final Map<Integer, ClosureInfo> expressionMap = new HashMap<Integer, ClosureInfo>();
private final JetTypeMapper typeMapper;
private final BindingContext bindingContext;
private final MethodContext context;
@@ -86,19 +80,28 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
private final JvmMethodSignature jvmSignature;
private LambdaInfo activeLambda;
protected final List<ParameterInfo> tempTypes = new ArrayList<ParameterInfo>();
protected final Map<Integer, LambdaInfo> expressionMap = new HashMap<Integer, LambdaInfo>();
public InlineCodegen(
@NotNull ExpressionCodegen codegen,
boolean notSeparateInline,
@NotNull GenerationState state,
boolean disabled,
@NotNull SimpleFunctionDescriptor functionDescriptor
@NotNull SimpleFunctionDescriptor functionDescriptor,
@NotNull Call call
) {
this.state = state;
this.typeMapper = state.getTypeMapper();
this.codegen = codegen;
this.notSeparateInline = notSeparateInline;
this.state = state;
this.disabled = disabled;
this.call = call;
this.functionDescriptor = functionDescriptor.getOriginal();
typeMapper = codegen.getTypeMapper();
bindingContext = codegen.getBindingContext();
initialFrameSize = codegen.getFrameMap().getCurrentSize();
@@ -114,7 +117,7 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
try {
node = createMethodNode(callableMethod);
inlineCall(node, true);
inlineCall(node);
}
catch (CompilationException e) {
throw e;
@@ -126,14 +129,14 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
functionDescriptor.getName() +
"' into \n" + (element != null ? element.getText() : "null psi element " + codegen.getContext().getContextDescriptor()) +
"\ncause: " +
text, e, null);
text, e, call.getCallElement());
}
}
@NotNull
private MethodNode createMethodNode(CallableMethod callableMethod)
throws ClassNotFoundException, IOException {
MethodNode node = null;
MethodNode node;
if (functionDescriptor instanceof DeserializedSimpleFunctionDescriptor) {
VirtualFile file = InlineCodegenUtil.getVirtualFileForCallable((DeserializedSimpleFunctionDescriptor) functionDescriptor, state);
node = InlineCodegenUtil.getMethodNode(file.getInputStream(), functionDescriptor.getName().asString(),
@@ -166,160 +169,43 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
(JetDeclarationWithBody) element),
getParentCodegen());
//TODO
node.visitMaxs(20, 20);
node.visitMaxs(30, 30);
node.visitEnd();
}
return node;
}
private void inlineCall(MethodNode node, boolean inlineClosures) {
if (inlineClosures) {
removeClosureAssertions(node);
try {
markPlacesForInlineAndRemoveInlinable(node);
}
catch (AnalyzerException e) {
throw new RuntimeException(e);
}
}
int valueParamSize = originalFunctionFrame.getCurrentSize();
int originalSize = codegen.getFrameMap().getCurrentSize();
private void inlineCall(MethodNode node) {
generateClosuresBodies();
List<ParameterInfo> realParams = new ArrayList<ParameterInfo>(tempTypes);
putClosureParametersOnStack();
int additionalParams = codegen.getFrameMap().getCurrentSize() - originalSize;
VarRemapper remapper = new VarRemapper.ParamRemapper(initialFrameSize, valueParamSize, additionalParams, tempTypes);
doInline(node.access, node.desc, codegen.getMethodVisitor(), node, remapper.doRemap(valueParamSize + additionalParams), inlineClosures, remapper);
}
List<CapturedParamInfo> captured = getAllCaptured();
private void removeClosureAssertions(MethodNode node) {
AbstractInsnNode cur = node.instructions.getFirst();
while (cur != null && cur.getNext() != null) {
AbstractInsnNode next = cur.getNext();
if (next.getType() == AbstractInsnNode.METHOD_INSN) {
MethodInsnNode methodInsnNode = (MethodInsnNode) next;
if (methodInsnNode.name.equals("checkParameterIsNotNull") && methodInsnNode.owner.equals("jet/runtime/Intrinsics")) {
AbstractInsnNode prev = cur.getPrevious();
assert prev.getType() == AbstractInsnNode.VAR_INSN && prev.getOpcode() == Opcodes.ALOAD;
int varIndex = ((VarInsnNode) prev).var;
ClosureInfo closure = expressionMap.get(varIndex);
if (closure != null) {
node.instructions.remove(prev);
node.instructions.remove(cur);
cur = next.getNext();
node.instructions.remove(next);
next = cur;
}
}
}
cur = next;
}
}
Parameters parameters = new Parameters(realParams, Parameters.addStubs(captured, realParams.size()));
private void markPlacesForInlineAndRemoveInlinable(@NotNull MethodNode node) throws AnalyzerException {
Analyzer<SourceValue> analyzer = new Analyzer<SourceValue>(new SourceInterpreter());
Frame<SourceValue>[] sources = analyzer.analyze("fake", node);
InliningInfo info =
new InliningInfo(expressionMap, null, null, null, state,
codegen.getInlineNameGenerator().subGenerator(functionDescriptor.getName().asString()),
codegen.getContext().getContextDescriptor());
MethodInliner inliner = new MethodInliner(node, parameters, info, null, new LambdaFieldRemapper()); //with captured
AbstractInsnNode cur = node.instructions.getFirst();
int index = 0;
while (cur != null) {
if (cur.getType() == AbstractInsnNode.METHOD_INSN) {
MethodInsnNode methodInsnNode = (MethodInsnNode) cur;
//TODO check closure
if (isInvokeOnInlinable(methodInsnNode.owner, methodInsnNode.name) /*&& methodInsnNode.owner.equals(INLINE_RUNTIME)*/) {
Frame<SourceValue> frame = sources[index];
SourceValue sourceValue = frame.getStack(frame.getStackSize() - Type.getArgumentTypes(methodInsnNode.desc).length - 1);
assert sourceValue.insns.size() == 1;
VarRemapper.ParamRemapper remapper = new VarRemapper.ParamRemapper(parameters, new VarRemapper.ShiftRemapper(initialFrameSize, null));
AbstractInsnNode insnNode = sourceValue.insns.iterator().next();
assert insnNode.getType() == AbstractInsnNode.VAR_INSN && insnNode.getOpcode() == Opcodes.ALOAD;
int varIndex = ((VarInsnNode) insnNode).var;
ClosureInfo closureInfo = expressionMap.get(varIndex);
if (closureInfo != null) { //TODO: maybe add separate map for noninlinable closures
closures.add(new ClosureUsage(varIndex, true));
node.instructions.remove(insnNode);
} else {
closures.add(new ClosureUsage(varIndex, false));
}
}
}
cur = cur.getNext();
index++;
}
}
private void doInline(
int access,
String desc,
MethodVisitor mv,
MethodNode methodNode,
int frameSize,
final boolean inlineClosures,
@NotNull VarRemapper remapper
) {
Label end = new Label();
final LinkedList<ClosureUsage> infos = new LinkedList<ClosureUsage>(closures);
methodNode.instructions.resetLabels();
MethodVisitor methodVisitor = codegen.getMethodVisitor();
InliningAdapter inliner = new InliningAdapter(methodVisitor, Opcodes.ASM4, desc, end, frameSize, remapper) {
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
if (inlineClosures && /*INLINE_RUNTIME.equals(owner) &&*/ isInvokeOnInlinable(owner, name)) { //TODO add method
assert !infos.isEmpty();
ClosureUsage closureUsage = infos.remove();
ClosureInfo info = expressionMap.get(closureUsage.index);
if (!closureUsage.isInlinable()) {
//noninlinable closure
super.visitMethodInsn(opcode, owner, name, desc);
return;
}
//TODO replace with codegen
int valueParamShift = getNextLocalIndex();
remapper.setNestedRemap(true);
putStackValuesIntoLocals(info.getParamsWithoutCapturedValOrVar(), valueParamShift, this, desc);
Label closureEnd = new Label();
InliningAdapter closureInliner = new InliningAdapter(mv, Opcodes.ASM4, desc, closureEnd, getNextLocalIndex(),
new VarRemapper.ClosureRemapper(info, valueParamShift, tempTypes));
info.getNode().instructions.resetLabels();
info.getNode().accept(closureInliner); //TODO
remapper.setNestedRemap(false);
mv.visitLabel(closureEnd);
Method bridge = typeMapper.mapSignature(ClosureCodegen.getInvokeFunction(info.getFunctionDescriptor())).getAsmMethod();
Method delegate = typeMapper.mapSignature(info.getFunctionDescriptor()).getAsmMethod();
StackValue.onStack(delegate.getReturnType()).put(bridge.getReturnType(), closureInliner);
}
else {
super.visitMethodInsn(opcode, owner, name, desc);
}
}
};
methodNode.accept(inliner);
methodVisitor.visitLabel(end);
}
private boolean isInvokeOnInlinable(String owner, String name) {
return INVOKE.equals(name) && /*TODO: check type*/owner.contains("Function");
inliner.doTransformAndMerge(codegen.getInstructionAdapter(), remapper);
generateClosuresBodies();
}
private void generateClosuresBodies() {
for (ClosureInfo info : expressionMap.values()) {
for (LambdaInfo info : expressionMap.values()) {
info.setNode(generateClosureBody(info));
}
}
private MethodNode generateClosureBody(ClosureInfo info) {
private MethodNode generateClosureBody(LambdaInfo info) {
JetFunctionLiteral declaration = info.getFunctionLiteral();
FunctionDescriptor descriptor = info.getFunctionDescriptor();
@@ -331,113 +217,58 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
Method asmMethod = jvmMethodSignature.getAsmMethod();
MethodNode methodNode = new MethodNode(Opcodes.ASM4, getMethodAsmFlags(descriptor, context.getContextKind()), asmMethod.getName(), asmMethod.getDescriptor(), jvmMethodSignature.getGenericsSignature(), null);
FunctionCodegen.generateMethodBody(methodNode, descriptor, context, jvmMethodSignature, new FunctionGenerationStrategy.FunctionDefault(state, descriptor, declaration) {
//AnalyzerAdapter adapter = new AnalyzerAdapter("fake", methodNode.access, methodNode.name, methodNode.desc, methodNode);
MethodNode adapter = methodNode;
FunctionCodegen.generateMethodBody(adapter, descriptor, context, jvmMethodSignature, new FunctionGenerationStrategy.FunctionDefault(state, descriptor, declaration) {
@Override
public boolean generateLocalVarTable() {
return false;
}
}, codegen.getParentCodegen());
adapter.visitMaxs(30, 30);
return transformClosure(methodNode, info);
return methodNode;
}
@NotNull
private static MethodNode transformClosure(@NotNull MethodNode node, @NotNull ClosureInfo info) {
//remove all this and shift all variables to captured ones size
final int localVarSHift = info.getCapturedVarsSize();
MethodNode transformedNode = new MethodNode(node.access, node.name, node.desc, node.signature, null) {
private boolean remappingCaptured = false;
@Override
public void visitVarInsn(int opcode, int var) {
super.visitVarInsn(opcode, var + (remappingCaptured ? 0 : localVarSHift - 1/*remove this*/));
}
};
node.accept(transformedNode);
//remove all field access to local var
AbstractInsnNode cur = transformedNode.instructions.getFirst();
while (cur != null) {
if (cur.getType() == AbstractInsnNode.FIELD_INSN) {
FieldInsnNode fieldInsnNode = (FieldInsnNode) cur;
//TODO check closure
String owner = fieldInsnNode.owner;
if (info.getClosureClassType().getInternalName().equals(fieldInsnNode.owner)) {
int opcode = fieldInsnNode.getOpcode();
String name = fieldInsnNode.name;
String desc = fieldInsnNode.desc;
Collection<EnclosedValueDescriptor> vars = info.getCapturedVars();
int index = 0;//skip this
boolean found = false;
for (EnclosedValueDescriptor valueDescriptor : vars) {
Type type = valueDescriptor.getType();
if (valueDescriptor.getFieldName().equals(name)) {
opcode = opcode == Opcodes.GETFIELD ? type.getOpcode(Opcodes.ILOAD) : type.getOpcode(Opcodes.ISTORE);
found = true;
break;
}
index += type.getSize();
}
if (!found) {
throw new UnsupportedOperationException("Coudn't find field " +
owner +
"." +
name +
" (" +
desc +
") in captured vars of " +
info.getFunctionLiteral().getText());
}
VarInsnNode varInsNode = new VarInsnNode(opcode, index);
AbstractInsnNode prev = cur.getPrevious();
while (prev.getType() == AbstractInsnNode.LABEL || prev.getType() == AbstractInsnNode.LINE) {
prev = prev.getPrevious();
}
assert prev.getType() == AbstractInsnNode.VAR_INSN;
VarInsnNode loadThis = (VarInsnNode) prev;
assert /*loadThis.var == info.getCapturedVarsSize() - 1 && */loadThis.getOpcode() == Opcodes.ALOAD;
transformedNode.instructions.remove(prev);
transformedNode.instructions.insertBefore(cur, varInsNode);
transformedNode.instructions.remove(cur);
cur = varInsNode;
}
}
cur = cur.getNext();
}
return transformedNode;
@Override
public void putInLocal(Type type, StackValue stackValue, ValueParameterDescriptor valueParameterDescriptor) {
putCapturedInLocal(type, stackValue, valueParameterDescriptor, -1);
}
@Override
public void putInLocal(Type type, StackValue stackValue) {
public void putCapturedInLocal(
Type type, StackValue stackValue, ValueParameterDescriptor valueParameterDescriptor, int index
) {
if (!disabled && notSeparateInline && Type.VOID_TYPE != type) {
//TODO remap only inlinable closure => otherwise we could get a lot of problem
boolean couldBeRemapped = !shouldPutValue(type, stackValue, codegen.getContext());
boolean couldBeRemapped = !shouldPutValue(type, stackValue, codegen.getContext(), valueParameterDescriptor);
int remappedIndex = couldBeRemapped ? ((StackValue.Local) stackValue).getIndex() : -1;
ParameterInfo info = new ParameterInfo(type, false, remappedIndex, couldBeRemapped ? -1 : codegen.getFrameMap().enterTemp(type));
if (index >= 0 && couldBeRemapped) {
CapturedParamInfo capturedParamInfo = activeLambda.getCapturedVars().get(index);
capturedParamInfo.setRemapIndex(info.getInlinedIndex());
}
doWithParameter(info);
}
}
@Override
public boolean shouldPutValue(Type type, StackValue stackValue, MethodContext context) {
if (stackValue != null && context.isInlineFunction() && stackValue instanceof StackValue.Local) {
if (isInvokeOnInlinable(type.getClassName(), "invoke")) {
//TODO remap only inlinable closure => otherwise we could get a lot of problem
return false; //TODO check annotations
}
}
return true;
public boolean shouldPutValue(Type type, StackValue stackValue, MethodContext context, ValueParameterDescriptor descriptor) {
//boolean isInline = true/*context.isInlineFunction() || context.getParentContext() instanceof ClosureContext*/;
//if (stackValue != null && isInline && stackValue instanceof StackValue.Local) {
// if (isInvokeOnInlinable(type.getClassName(), "invoke") && (descriptor == null || !InlineUtil.hasNoinlineAnnotation(descriptor))) {
// //TODO remap only inlinable closure => otherwise we could get a lot of problem
// return false;
// }
//}
return false == (stackValue != null && stackValue instanceof StackValue.Local);
}
private void doWithParameter(ParameterInfo info) {
@@ -459,7 +290,7 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
private void putParameterOnStack(ParameterInfo info) {
if (!info.isSkippedOrRemapped()) {
int index = info.index;
int index = info.getIndex();
Type type = info.type;
StackValue.local(index, type).store(type, codegen.getInstructionAdapter());
}
@@ -484,7 +315,7 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
recordParamInfo(info, false);
}
for (ListIterator<ParameterInfo> iterator = tempTypes.listIterator(tempTypes.size()); iterator.hasPrevious(); ) {
for (ListIterator<? extends ParameterInfo> iterator = tempTypes.listIterator(tempTypes.size()); iterator.hasPrevious(); ) {
ParameterInfo param = iterator.previous();
putParameterOnStack(param);
}
@@ -493,7 +324,7 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
@Override
public void leaveTemps() {
FrameMap frameMap = codegen.getFrameMap();
for (ListIterator<ParameterInfo> iterator = tempTypes.listIterator(tempTypes.size()); iterator.hasPrevious(); ) {
for (ListIterator<? extends ParameterInfo> iterator = tempTypes.listIterator(tempTypes.size()); iterator.hasPrevious(); ) {
ParameterInfo param = iterator.previous();
if (!param.isSkippedOrRemapped()) {
frameMap.leaveTemp(param.type);
@@ -505,31 +336,11 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
return disabled;
}
private static void putStackValuesIntoLocals(List<Type> directOrder, int shift, InstructionAdapter mv, String descriptor) {
Type [] actualParams = Type.getArgumentTypes(descriptor); //last param is closure itself
assert actualParams.length == directOrder.size() : "Number of expected and actual params should be equals!";
int size = 0;
for (Type next : directOrder) {
size += next.getSize();
}
shift += size;
int index = directOrder.size();
for (Type next : Lists.reverse(directOrder)) {
shift -= next.getSize();
Type typeOnStack = actualParams[--index];
if (!typeOnStack.equals(next)) {
StackValue.onStack(typeOnStack).put(next, mv);
}
mv.visitVarInsn(next.getOpcode(Opcodes.ISTORE), shift);
}
}
@Override
public boolean isInliningClosure(JetExpression expression) {
return !disabled && expression instanceof JetFunctionLiteralExpression;
public boolean isInliningClosure(JetExpression expression, ValueParameterDescriptor valueParameterDescriptora) {
return !disabled &&
expression instanceof JetFunctionLiteralExpression &&
!InlineUtil.hasNoinlineAnnotation(valueParameterDescriptora);
}
@Override
@@ -537,19 +348,35 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
ParameterInfo closureInfo = new ParameterInfo(type, true, -1, -1);
int index = recordParamInfo(closureInfo, true);
ClosureInfo info = new ClosureInfo(expression, typeMapper);
LambdaInfo info = new LambdaInfo(expression, typeMapper);
expressionMap.put(index, info);
closureInfo.setLambda(info);
}
private void putClosureParametersOnStack() {
//TODO: SORT
for (ClosureInfo next : expressionMap.values()) {
int currentSize = tempTypes.size();
for (LambdaInfo next : expressionMap.values()) {
if (next.closure != null) {
int size = tempTypes.size();
next.setParamOffset(size);
activeLambda = next;
next.setParamOffset(currentSize);
codegen.pushClosureOnStack(next.closure, false, this);
currentSize += next.getCapturedVarsSize();
}
}
activeLambda = null;
}
private List<CapturedParamInfo> getAllCaptured() {
//TODO: SORT
List<CapturedParamInfo> result = new ArrayList<CapturedParamInfo>();
for (LambdaInfo next : expressionMap.values()) {
if (next.closure != null) {
result.addAll(next.getCapturedVars());
}
}
return result;
}
@Nullable
@@ -559,8 +386,8 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
}
public static CodegenContext getContext(DeclarationDescriptor descriptor, GenerationState state) {
if (descriptor instanceof NamespaceDescriptor) {
return new NamespaceContext((NamespaceDescriptor) descriptor, null);
if (descriptor instanceof PackageFragmentDescriptor) {
return new PackageContext((PackageFragmentDescriptor) descriptor, null);
}
CodegenContext parent = getContext(descriptor.getContainingDeclaration(), state);
@@ -580,7 +407,7 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
}
@NotNull
private static String getNodeText(@Nullable MethodNode node) {
public static String getNodeText(@Nullable MethodNode node) {
if (node == null) {
return "Not generated";
}
@@ -19,18 +19,20 @@ package org.jetbrains.jet.codegen.asm;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.ClassReader;
import org.jetbrains.asm4.ClassVisitor;
import org.jetbrains.asm4.MethodVisitor;
import org.jetbrains.asm4.Opcodes;
import org.jetbrains.asm4.*;
import org.jetbrains.asm4.tree.MethodNode;
import org.jetbrains.jet.codegen.PackageCodegen;
import org.jetbrains.jet.codegen.binding.CodegenBinding;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.codegen.state.JetTypeMapper;
import org.jetbrains.jet.descriptors.serialization.JavaProtoBuf;
import org.jetbrains.jet.descriptors.serialization.ProtoBuf;
import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedSimpleFunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
import org.jetbrains.jet.lang.resolve.kotlin.VirtualFileFinder;
@@ -40,11 +42,13 @@ import org.jetbrains.jet.lang.resolve.name.Name;
import java.io.IOException;
import java.io.InputStream;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getFQName;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getFqName;
public class InlineCodegenUtil {
private final static int API = Opcodes.ASM4;
public final static int API = Opcodes.ASM4;
public final static String INVOKE = "invoke";
@Nullable
public static MethodNode getMethodNode(
@@ -73,14 +77,14 @@ public class InlineCodegenUtil {
public static VirtualFile getVirtualFileForCallable(DeserializedSimpleFunctionDescriptor deserializedDescriptor, GenerationState state) {
VirtualFile file = null;
DeclarationDescriptor parentDeclatation = deserializedDescriptor.getContainingDeclaration();
if (parentDeclatation instanceof NamespaceDescriptor) {
if (parentDeclatation instanceof PackageFragmentDescriptor) {
ProtoBuf.Callable proto = deserializedDescriptor.getFunctionProto();
if (proto.hasExtension(JavaProtoBuf.implClassName)) {
Name name = deserializedDescriptor.getNameResolver().getName(proto.getExtension(JavaProtoBuf.implClassName));
FqName namespaceFqName =
PackageClassUtils.getPackageClassFqName(((NamespaceDescriptor) parentDeclatation).getFqName()).parent().child(
PackageClassUtils.getPackageClassFqName(((PackageFragmentDescriptor) parentDeclatation).getFqName()).parent().child(
name);
file = findVirtualFile(state.getProject(), namespaceFqName);
file = findVirtualFile(state.getProject(), namespaceFqName, true);
} else {
assert false : "Function in namespace should have implClassName property in proto: " + deserializedDescriptor;
}
@@ -96,33 +100,72 @@ public class InlineCodegenUtil {
}
@Nullable
private static VirtualFile findVirtualFile(@NotNull Project project, @NotNull FqName containerFqName) {
public static VirtualFile findVirtualFile(@NotNull Project project, @NotNull FqName containerFqName, boolean onlyKotlin) {
VirtualFileFinder fileFinder = ServiceManager.getService(project, VirtualFileFinder.class);
VirtualFile virtualFile = fileFinder.findVirtualFile(containerFqName);
if (virtualFile == null) {
return null;
if (onlyKotlin) {
return fileFinder.findVirtualFile(containerFqName);
} else {
return fileFinder.findVirtualFile(containerFqName.asString().replace('.', '/'));
}
return virtualFile;
}
//TODO: navigate to inner classes
@Nullable
private static FqName getContainerFqName(@NotNull DeclarationDescriptor referencedDescriptor) {
ClassOrNamespaceDescriptor
containerDescriptor = DescriptorUtils.getParentOfType(referencedDescriptor, ClassOrNamespaceDescriptor.class, false);
if (containerDescriptor instanceof NamespaceDescriptor) {
return PackageClassUtils.getPackageClassFqName(getFQName(containerDescriptor).toSafe());
public static FqName getContainerFqName(@NotNull DeclarationDescriptor referencedDescriptor) {
ClassOrPackageFragmentDescriptor
containerDescriptor = DescriptorUtils.getParentOfType(referencedDescriptor, ClassOrPackageFragmentDescriptor.class, false);
if (containerDescriptor instanceof PackageFragmentDescriptor) {
return PackageClassUtils.getPackageClassFqName(getFqName(containerDescriptor).toSafe());
}
if (containerDescriptor instanceof ClassDescriptor) {
ClassKind classKind = ((ClassDescriptor) containerDescriptor).getKind();
if (classKind == ClassKind.CLASS_OBJECT || classKind == ClassKind.ENUM_ENTRY) {
return getContainerFqName(containerDescriptor.getContainingDeclaration());
}
return getFQName(containerDescriptor).toSafe();
return getFqName(containerDescriptor).toSafe();
}
return null;
}
public static String getInlineName(@NotNull DeclarationDescriptor referencedDescriptor, @NotNull JetTypeMapper typeMapper) {
return getInlineName(referencedDescriptor, referencedDescriptor, typeMapper);
}
private static String getInlineName(@NotNull DeclarationDescriptor referencedDescriptor, @NotNull DeclarationDescriptor currentDescriptor, @NotNull JetTypeMapper typeMapper) {
if (currentDescriptor instanceof PackageFragmentDescriptor) {
PsiElement psiElement = BindingContextUtils.descriptorToDeclaration(typeMapper.getBindingContext(), referencedDescriptor);
if (psiElement == null) {
psiElement = BindingContextUtils.descriptorToDeclaration(typeMapper.getBindingContext(), referencedDescriptor.getContainingDeclaration());
if (psiElement == null) {
throw new RuntimeException("Couldn't find declaration for " + referencedDescriptor.getContainingDeclaration().getName() + "." + referencedDescriptor.getName() );
}
}
Type packageFragmentType =
PackageCodegen.getPackagePartType(PackageClassUtils.getPackageClassFqName(getFqName(currentDescriptor).toSafe()),
psiElement.getContainingFile().getVirtualFile());
return packageFragmentType.getInternalName().replace('.', '/');
}
else if (currentDescriptor instanceof ClassifierDescriptor) {
Type type = typeMapper.mapType((ClassifierDescriptor) currentDescriptor);
return type.getInternalName();
} else if (currentDescriptor instanceof FunctionDescriptor) {
ClassDescriptor descriptor =
typeMapper.getBindingContext().get(CodegenBinding.CLASS_FOR_FUNCTION, (FunctionDescriptor) currentDescriptor);
if (descriptor != null) {
Type type = typeMapper.mapType(descriptor);
return type.getInternalName();
}
}
assert currentDescriptor != null : "Wrong descriptor hierarchy " + currentDescriptor;
String suffix = currentDescriptor.getName().isSpecial() ? "" : currentDescriptor.getName().asString();
return getInlineName(referencedDescriptor, currentDescriptor.getContainingDeclaration(), typeMapper) + "$" + suffix;
}
@Nullable
private static VirtualFile findVirtualFileContainingDescriptor(
@NotNull Project project,
@@ -132,7 +175,44 @@ public class InlineCodegenUtil {
if (containerFqName == null) {
return null;
}
return findVirtualFile(project, containerFqName);
return findVirtualFile(project, containerFqName, true);
}
public static boolean isInvokeOnInlinable(String owner, String name) {
return INVOKE.equals(name) && /*TODO: check type*/owner.contains("Function");
}
public static boolean isLambdaConstructorCall(@NotNull String internalName, @NotNull String name) {
if (!"<init>".equals(name)) {
return false;
}
return isLambdaClass(internalName);
}
public static boolean isLambdaClass(String internalName) {
String shortName = getLastNamePart(internalName);
int index = shortName.lastIndexOf("$");
if (index < 0) {
return false;
}
String suffix = shortName.substring(index + 1);
for (char c : suffix.toCharArray()) {
if (!Character.isDigit(c)) return false;
}
return true;
}
@NotNull
private static String getLastNamePart(@NotNull String internalName) {
int index = internalName.lastIndexOf("/");
return index < 0 ? internalName : internalName.substring(index + 1);
}
public static boolean isInitCallOfFunction(String owner, String name) {
return "<init>".equals(name);
}
}
@@ -0,0 +1,42 @@
/*
* Copyright 2010-2013 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.asm;
import org.jetbrains.asm4.tree.AbstractInsnNode;
import org.jetbrains.asm4.tree.FieldInsnNode;
import org.jetbrains.asm4.tree.MethodNode;
public class InlineFieldRemapper extends LambdaFieldRemapper {
private String oldOwnerType;
private String newOwnerType;
public InlineFieldRemapper(String oldOwnerType, String newOwnerType) {
this.oldOwnerType = oldOwnerType;
this.newOwnerType = newOwnerType;
}
@Override
public AbstractInsnNode doTransform(
MethodNode node, FieldInsnNode fieldInsnNode, CapturedParamInfo capturedField
) {
fieldInsnNode.owner = newOwnerType;
fieldInsnNode.name = LambdaTransformer.getNewFieldName(fieldInsnNode.name);
return fieldInsnNode;
}
}
@@ -21,6 +21,7 @@ import org.jetbrains.asm4.Type;
import org.jetbrains.jet.codegen.CallableMethod;
import org.jetbrains.jet.codegen.StackValue;
import org.jetbrains.jet.codegen.context.MethodContext;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression;
@@ -35,7 +36,14 @@ public interface Inliner {
}
@Override
public void putInLocal(Type type, StackValue stackValue) {
public void putInLocal(Type type, StackValue stackValue, ValueParameterDescriptor valueParameterDescriptor) {
}
@Override
public void putCapturedInLocal(
Type type, StackValue stackValue, ValueParameterDescriptor valueParameterDescriptor, int index
) {
}
@@ -45,7 +53,7 @@ public interface Inliner {
}
@Override
public boolean isInliningClosure(JetExpression expression) {
public boolean isInliningClosure(JetExpression expression, ValueParameterDescriptor valueParameterDescriptora) {
return false;
}
@@ -61,7 +69,8 @@ public interface Inliner {
@Override
public boolean shouldPutValue(
Type type, StackValue stackValue, MethodContext context
Type type, StackValue stackValue, MethodContext context,
ValueParameterDescriptor descriptor
) {
return true;
}
@@ -69,15 +78,17 @@ public interface Inliner {
void inlineCall(CallableMethod callableMethod, ClassVisitor visitor);
void putInLocal(Type type, StackValue stackValue);
void putInLocal(Type type, StackValue stackValue, ValueParameterDescriptor valueParameterDescriptor);
boolean shouldPutValue(Type type, StackValue stackValue, MethodContext context);
void putCapturedInLocal(Type type, StackValue stackValue, ValueParameterDescriptor valueParameterDescriptor, int index);
boolean shouldPutValue(Type type, StackValue stackValue, MethodContext context, ValueParameterDescriptor descriptor);
void putHiddenParams();
void leaveTemps();
boolean isInliningClosure(JetExpression expression);
boolean isInliningClosure(JetExpression expression, ValueParameterDescriptor valueParameterDescriptora);
void rememberClosure(JetFunctionLiteralExpression expression, Type type);
}
@@ -0,0 +1,66 @@
/*
* Copyright 2010-2013 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.asm;
import org.jetbrains.jet.codegen.context.CodegenContext;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.lang.psi.Call;
import java.util.List;
import java.util.Map;
public class InliningInfo {
public final Map<Integer, LambdaInfo> expresssionMap;
public final List<InlinableAccess> inlinableAccesses;
public final List<ConstructorInvocation> constructorInvocation;
public final VarRemapper remapper;
public final GenerationState state;
public final NameGenerator nameGenerator;
public final CodegenContext startContext;
public final Call call;
public InliningInfo(
Map<Integer, LambdaInfo> map,
List<InlinableAccess> accesses,
List<ConstructorInvocation> invocation,
VarRemapper remapper,
GenerationState state,
NameGenerator nameGenerator,
CodegenContext startContext,
Call call
) {
expresssionMap = map;
inlinableAccesses = accesses;
constructorInvocation = invocation;
this.remapper = remapper;
this.state = state;
this.nameGenerator = nameGenerator;
this.startContext = startContext;
this.call = call;
}
public InliningInfo subInline(NameGenerator generator) {
return new InliningInfo(expresssionMap, inlinableAccesses, constructorInvocation, remapper, state, generator, startContext, call);
}
}
@@ -0,0 +1,46 @@
/*
* Copyright 2010-2013 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.asm;
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 static org.jetbrains.jet.codegen.asm.MethodInliner.getPreviousNoLabelNoLine;
public class LambdaFieldRemapper {
public AbstractInsnNode doTransform(MethodNode node, FieldInsnNode fieldInsnNode, CapturedParamInfo capturedField) {
AbstractInsnNode prev = getPreviousNoLabelNoLine(fieldInsnNode);
assert prev.getType() == AbstractInsnNode.VAR_INSN;
VarInsnNode loadThis = (VarInsnNode) prev;
assert /*loadThis.var == info.getCapturedVarsSize() - 1 && */loadThis.getOpcode() == Opcodes.ALOAD;
int opcode = fieldInsnNode.getOpcode() == Opcodes.GETFIELD ? capturedField.getType().getOpcode(Opcodes.ILOAD) : capturedField.getType().getOpcode(Opcodes.ISTORE);
VarInsnNode insn = new VarInsnNode(opcode, capturedField.getIndex());
node.instructions.remove(prev); //remove aload this
node.instructions.insertBefore(fieldInsnNode, insn);
node.instructions.remove(fieldInsnNode); //remove aload field
return insn;
}
}
@@ -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 ClosureInfo {
public class LambdaInfo {
public final JetFunctionLiteralExpression expression;
@@ -47,7 +47,7 @@ public class ClosureInfo {
private MethodNode node;
private Collection<EnclosedValueDescriptor> capturedVars;
private List<CapturedParamInfo> capturedVars;
private final FunctionDescriptor functionDescriptor;
@@ -57,7 +57,7 @@ public class ClosureInfo {
private int paramOffset;
ClosureInfo(@NotNull JetFunctionLiteralExpression expression, @NotNull JetTypeMapper typeMapper) {
LambdaInfo(@NotNull JetFunctionLiteralExpression expression, @NotNull JetTypeMapper typeMapper) {
this.expression = expression;
this.typeMapper = typeMapper;
BindingContext bindingContext = typeMapper.getBindingContext();
@@ -91,38 +91,56 @@ public class ClosureInfo {
return classDescriptor;
}
public Type getClosureClassType() {
public Type getLambdaClassType() {
return closureClassType;
}
public Collection<EnclosedValueDescriptor> getCapturedVars() {
public List<CapturedParamInfo> getCapturedVars() {
//lazy initialization cause it would be calculated after object creation
int index = 0;
if (capturedVars == null) {
capturedVars = new ArrayList<EnclosedValueDescriptor>();
capturedVars = new ArrayList<CapturedParamInfo>();
if (closure.getCaptureThis() != null) {
EnclosedValueDescriptor descriptor = new EnclosedValueDescriptor(AsmUtil.CAPTURED_THIS_FIELD, null, null, typeMapper.mapType(closure.getCaptureThis()));
capturedVars.add(descriptor);
capturedVars.add(getCapturedParamInfo(descriptor, index));
index += descriptor.getType().getSize();
}
if (closure.getCaptureReceiverType() != null) {
EnclosedValueDescriptor descriptor = new EnclosedValueDescriptor(AsmUtil.CAPTURED_RECEIVER_FIELD, null, null, typeMapper.mapType(closure.getCaptureReceiverType()));
capturedVars.add(descriptor);
capturedVars.add(getCapturedParamInfo(descriptor, index));
index += descriptor.getType().getSize();
}
if (closure != null) {
capturedVars.addAll(closure.getCaptureVariables().values());
for (EnclosedValueDescriptor descriptor : closure.getCaptureVariables().values()) {
capturedVars.add(getCapturedParamInfo(descriptor, index));
index += descriptor.getType().getSize();
}
}
}
return capturedVars;
}
@NotNull
public static CapturedParamInfo getCapturedParamInfo(@NotNull EnclosedValueDescriptor descriptor, int index) {
return new CapturedParamInfo(descriptor.getFieldName(), descriptor.getType(), false, -1, index);
}
private void shiftParams(int shift) {
for (CapturedParamInfo var : getCapturedVars()) {
var.setShift(shift);
}
}
public int getParamOffset() {
return paramOffset;
}
public void setParamOffset(int paramOffset) {
this.paramOffset = paramOffset;
shiftParams(paramOffset);
}
public List<Type> getParamsWithoutCapturedValOrVar() {
@@ -132,8 +150,7 @@ public class ClosureInfo {
public int getCapturedVarsSize() {
int size = 0;
for (Iterator<EnclosedValueDescriptor> iterator = getCapturedVars().iterator(); iterator.hasNext(); ) {
EnclosedValueDescriptor next = iterator.next();
for (CapturedParamInfo next : getCapturedVars()) {
size += next.getType().getSize();
}
return size;
@@ -0,0 +1,266 @@
/*
* Copyright 2010-2013 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.asm;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.*;
import org.jetbrains.asm4.commons.Method;
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.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.state.GenerationState;
import org.jetbrains.jet.codegen.state.JetTypeMapper;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.name.FqName;
import java.io.IOException;
import java.util.*;
import static org.jetbrains.asm4.Opcodes.ASM4;
import static org.jetbrains.asm4.Opcodes.V1_6;
public class LambdaTransformer {
protected final GenerationState state;
protected final JetTypeMapper typeMapper;
private final MethodNode constructor;
private final MethodNode invoke;
private final MethodNode bridge;
private final InliningInfo info;
private final Map<String, Integer> paramMapping = new HashMap<String, Integer>();
private final Type oldLambdaType;
private final Type newLambdaType;
private int classAccess;
private String signature;
private String superName;
private String[] interfaces;
public LambdaTransformer(String lambdaInternalName, InliningInfo info) {
this.state = info.state;
this.typeMapper = state.getTypeMapper();
this.info = info;
this.oldLambdaType = Type.getObjectType(lambdaInternalName);
newLambdaType = Type.getObjectType(info.nameGenerator.genLambdaClassName());
//try to find just compiled classes then in dependencies
ClassReader reader;
try {
OutputFile outputFile = state.getFactory().get(lambdaInternalName + ".class");
if (outputFile != null) {
reader = new ClassReader(outputFile.asByteArray());
} else {
VirtualFile file = InlineCodegenUtil.findVirtualFile(state.getProject(), new FqName(lambdaInternalName.replace('/', '.')), false);
if (file == null) {
throw new RuntimeException("Couldn't find virtual file for " + lambdaInternalName);
}
reader = new ClassReader(file.getInputStream());
}
}
catch (IOException e) {
throw new RuntimeException(e);
}
constructor = getMethodNode(reader, true, false);
invoke = getMethodNode(reader, false, false);
bridge = getMethodNode(reader, false, true);
}
private void buildInvokeParams(ParametersBuilder builder) {
builder.addThis(oldLambdaType, false);
Type[] types = Type.getArgumentTypes(invoke.desc);
for (Type type : types) {
builder.addNextParameter(type, false, null);
}
}
public void doTransform(ConstructorInvocation invocation) {
ClassBuilder classBuilder = createClassBuilder();
classBuilder.defineClass(null,
V1_6,
classAccess,
newLambdaType.getInternalName(),
signature,
superName,
interfaces
);
ParametersBuilder builder = ParametersBuilder.newBuilder();
Parameters parameters = getLambdaParameters(builder, invocation);
MethodVisitor invokeVisitor = newMethod(classBuilder, invoke);
MethodInliner inliner = new MethodInliner(invoke, parameters, info.subInline(info.nameGenerator.subGenerator("lambda")), oldLambdaType,
new LambdaFieldRemapper());
inliner.doTransformAndMerge(invokeVisitor, new VarRemapper.ParamRemapper(parameters, null), new InlineFieldRemapper(oldLambdaType.getInternalName(), newLambdaType.getInternalName()), false);
invokeVisitor.visitMaxs(-1, -1);
generateConstructorAndFields(classBuilder, builder, invocation);
if (bridge != null) {
MethodVisitor invokeBridge = newMethod(classBuilder, bridge);
bridge.accept(new MethodVisitor(ASM4, invokeBridge) {
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
if (owner.equals(oldLambdaType.getInternalName())) {
super.visitMethodInsn(opcode, newLambdaType.getInternalName(), name, desc);
} else {
super.visitMethodInsn(opcode, owner, name, desc);
}
}
});
}
classBuilder.done();
invocation.setNewLambdaType(newLambdaType);
}
private void generateConstructorAndFields(@NotNull ClassBuilder classBuilder, @NotNull ParametersBuilder builder, @NotNull ConstructorInvocation invocation) {
List<CapturedParamInfo> infos = builder.buildCaptured();
List<Pair<String, Type>> newConstructorSignature = new ArrayList<Pair<String, Type>>();
for (CapturedParamInfo capturedParamInfo : infos) {
if (capturedParamInfo.getLambda() == null) { //not inlined
newConstructorSignature.add(new Pair<String, Type>(capturedParamInfo.getFieldName(), capturedParamInfo.getType()));
}
}
List<FieldInfo> fields = AsmUtil.transformCapturedParams(newConstructorSignature, newLambdaType);
AsmUtil.genClosureFields(newConstructorSignature, classBuilder);
Method newConstructor = ClosureCodegen.generateConstructor(classBuilder, fields, null, Type.getObjectType(superName), state);
invocation.setNewConstructorDescriptor(newConstructor.getDescriptor());
}
private Parameters getLambdaParameters(ParametersBuilder builder, ConstructorInvocation invocation) {
buildInvokeParams(builder);
extractParametersMapping(constructor, builder, invocation);
return builder.buildParameters();
}
private ClassBuilder createClassBuilder() {
return state.getFactory().forLambdaInlining(newLambdaType, info.call.getCalleeExpression().getContainingFile());
}
private static MethodVisitor newMethod(ClassBuilder builder, MethodNode original) {
return builder.newMethod(
null,
original.access,
original.name,
original.desc,
original.signature,
null //TODO: change signature to list
);
}
private void extractParametersMapping(MethodNode constructor, ParametersBuilder builder, ConstructorInvocation invocation) {
Map<Integer, InlinableAccess> indexToLambda = invocation.getAccess();
AbstractInsnNode cur = constructor.instructions.getFirst();
cur = cur.getNext(); //skip super call
List<LambdaInfo> additionalCaptured = new ArrayList<LambdaInfo>(); //captured var of inlined parameter
while (cur != null) {
if (cur.getType() == AbstractInsnNode.FIELD_INSN) {
FieldInsnNode fieldNode = (FieldInsnNode) cur;
VarInsnNode previous = (VarInsnNode) fieldNode.getPrevious();
int varIndex = previous.var;
paramMapping.put(fieldNode.name, varIndex);
CapturedParamInfo info = builder.addCapturedParam(fieldNode.name, Type.getType(fieldNode.desc), false, null);
InlinableAccess access = indexToLambda.get(varIndex);
if (access != null) {
LambdaInfo accessInfo = access.getInfo();
if (accessInfo != null) {
info.setLambda(accessInfo);
additionalCaptured.add(accessInfo);
}
}
}
cur = cur.getNext();
}
List<CapturedParamInfo> recaptured = new ArrayList<CapturedParamInfo>();
for (LambdaInfo info : additionalCaptured) {
List<CapturedParamInfo> vars = info.getCapturedVars();
for (CapturedParamInfo var : vars) {
CapturedParamInfo recapturedParamInfo = builder.addCapturedParam(getNewFieldName(var.getFieldName()), var.getType(), true, var);
recaptured.add(var);
}
}
invocation.setRecaptured(recaptured);
}
@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);
if (findConstructor && isConstructorMethod || (!findConstructor && !isConstructorMethod && ((access & Opcodes.ACC_BRIDGE) == (findBridge ? Opcodes.ACC_BRIDGE : 0)))) {
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];
}
public Type getNewLambdaType() {
return newLambdaType;
}
public static String getNewFieldName(String oldName) {
return oldName + "$inlined";
}
}
@@ -0,0 +1,468 @@
package org.jetbrains.jet.codegen.asm;
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;
import org.jetbrains.asm4.Type;
import org.jetbrains.asm4.commons.InstructionAdapter;
import org.jetbrains.asm4.commons.Method;
import org.jetbrains.asm4.tree.*;
import org.jetbrains.asm4.tree.analysis.*;
import org.jetbrains.jet.codegen.ClosureCodegen;
import org.jetbrains.jet.codegen.StackValue;
import org.jetbrains.jet.codegen.state.JetTypeMapper;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
import java.util.*;
import static org.jetbrains.jet.codegen.asm.InlineCodegenUtil.isLambdaConstructorCall;
import static org.jetbrains.jet.codegen.asm.InlineCodegenUtil.isInvokeOnInlinable;
public class MethodInliner {
private final MethodNode node;
private final Parameters parameters;
private final InliningInfo parent;
@Nullable
private final Type lambdaInfo;
private final LambdaFieldRemapper lambdaFieldRemapper;
private final JetTypeMapper typeMapper;
private final List<InlinableAccess> inlinableInvocation = new ArrayList<InlinableAccess>();
private final List<ConstructorInvocation> constructorInvocation = new ArrayList<ConstructorInvocation>();
/*
*
* @param node
* @param parameters
* @param parent
* @param lambdaInfo - in case on lambda 'invoke' inlining
*/
public MethodInliner(
@NotNull MethodNode node,
Parameters parameters,
@NotNull InliningInfo parent,
@Nullable Type lambdaInfo,
LambdaFieldRemapper lambdaFieldRemapper
) {
this.node = node;
this.parameters = parameters;
this.parent = parent;
this.lambdaInfo = lambdaInfo;
this.lambdaFieldRemapper = lambdaFieldRemapper;
this.typeMapper = parent.state.getTypeMapper();
}
public void doTransformAndMerge(MethodVisitor adapter, VarRemapper.ParamRemapper remapper) {
doTransformAndMerge(adapter, remapper, new LambdaFieldRemapper(), true);
}
public void doTransformAndMerge(
MethodVisitor adapter,
VarRemapper.ParamRemapper remapper,
LambdaFieldRemapper capturedRemapper, boolean remapReturn
) {
//analyze body
MethodNode transformedNode = node;
try {
transformedNode = markPlacesForInlineAndRemoveInlinable(transformedNode);
}
catch (AnalyzerException e) {
throw new RuntimeException(e);
}
transformedNode = doInline(transformedNode, capturedRemapper);
removeClosureAssertions(transformedNode);
transformedNode.instructions.resetLabels();
Label end = new Label();
RemapVisitor visitor = new RemapVisitor(adapter, end, remapper, remapReturn);
transformedNode.accept(visitor);
visitor.visitLabel(end);
}
private MethodNode doInline(MethodNode node, final LambdaFieldRemapper capturedRemapper) {
final Deque<InlinableAccess> infos = new LinkedList<InlinableAccess>(inlinableInvocation);
MethodNode resultNode = new MethodNode(node.access, node.name, node.desc, node.signature, null);
//TODO add reset to counter
InlineAdapter inliner = new InlineAdapter(resultNode, parameters.totalSize()) {
@Override
public void anew(Type type) {
if (isLambdaConstructorCall(type.getInternalName(), "<init>")) {
ConstructorInvocation invocation = constructorInvocation.get(0);
if (invocation.isInlinable()) {
LambdaTransformer transformer = new LambdaTransformer(invocation.getOwnerInternalName(), parent.subInline(parent.nameGenerator));
transformer.doTransform(invocation);
super.anew(transformer.getNewLambdaType());
} else {
super.anew(type);
}
} else {
super.anew(type);
}
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
if (/*INLINE_RUNTIME.equals(owner) &&*/ isInvokeOnInlinable(owner, name)) { //TODO add method
assert !infos.isEmpty();
InlinableAccess inlinableAccess = infos.remove();
if (!inlinableAccess.isInlinable()) {
//noninlinable closure
super.visitMethodInsn(opcode, owner, name, desc);
return;
}
LambdaInfo info = getLambda(inlinableAccess.index);
int valueParamShift = getNextLocalIndex();
putStackValuesIntoLocals(info.getParamsWithoutCapturedValOrVar(), valueParamShift, this, desc);
List<ParameterInfo> lambdaParameters = inlinableAccess.getParameters();
Parameters params = new Parameters(lambdaParameters, Parameters.transformList(info.getCapturedVars(), lambdaParameters.size()));
MethodInliner inliner = new MethodInliner(info.getNode(), params, parent.subInline(parent.nameGenerator.subGenerator("lambda")), info.getLambdaClassType(),
capturedRemapper);
VarRemapper.ParamRemapper remapper = new VarRemapper.ParamRemapper(params, new VarRemapper.ShiftRemapper(valueParamShift, null));
inliner.doTransformAndMerge(this.mv, remapper); //TODO add skipped this and receiver
Method bridge = typeMapper.mapSignature(ClosureCodegen.getInvokeFunction(info.getFunctionDescriptor())).getAsmMethod();
Method delegate = typeMapper.mapSignature(info.getFunctionDescriptor()).getAsmMethod();
StackValue.onStack(delegate.getReturnType()).put(bridge.getReturnType(), this);
}
else if (isLambdaConstructorCall(owner, name)) { //TODO add method
ConstructorInvocation invocation = constructorInvocation.remove(0);
if (invocation.isInlinable()) {
//put additional captured parameters on stack
List<CapturedParamInfo> recaptured = invocation.getRecaptured();
for (CapturedParamInfo capturedParamInfo : recaptured) {
Type type = capturedParamInfo.getType();
super.visitVarInsn(type.getOpcode(Opcodes.ILOAD), capturedParamInfo.getIndex());
}
super.visitMethodInsn(opcode, invocation.getNewLambdaType().getInternalName(), name, invocation.getNewConstructorDescriptor());
} else {
super.visitMethodInsn(opcode, owner, name, desc);
}
}
else {
super.visitMethodInsn(opcode, owner, name, desc);
}
}
};
node.accept(inliner);
return resultNode;
}
public void merge() {
}
@NotNull
public MethodNode prepareNode(@NotNull MethodNode node) {
if (parameters.getCaptured().size() != 0) {
final int capturedParamsSize = parameters.getCaptured().size();
final int realParametersSize = parameters.getReal().size();
Type[] types = Type.getArgumentTypes(node.desc);
Type returnType = Type.getReturnType(node.desc);
ArrayList<Type> capturedTypes = parameters.getCapturedTypes();
Type[] allTypes = ArrayUtil.mergeArrays(types, capturedTypes.toArray(new Type[capturedTypes.size()]));
MethodNode transformedNode = new MethodNode(node.access, node.name, Type.getMethodDescriptor(returnType, allTypes), node.signature, null) {
@Override
public void visitVarInsn(int opcode, int var) {
int newIndex;
if (var < realParametersSize) {
newIndex = var;
} else {
newIndex = var + capturedParamsSize;
}
super.visitVarInsn(opcode, newIndex);
}
@Override
public void visitIincInsn(int var, int increment) {
int newIndex;
if (var < realParametersSize) {
newIndex = var;
} else {
newIndex = var + capturedParamsSize;
}
super.visitIincInsn(newIndex, increment);
}
};
node.accept(transformedNode);
transformedNode.visitMaxs(30, 30);
if (lambdaInfo != null) {
transformCaptured(transformedNode, parameters, lambdaInfo, lambdaFieldRemapper);
}
return transformedNode;
}
return node;
}
protected MethodNode markPlacesForInlineAndRemoveInlinable(@NotNull MethodNode node) throws AnalyzerException {
node = prepareNode(node);
Analyzer<SourceValue> analyzer = new Analyzer<SourceValue>(new SourceInterpreter());
Frame<SourceValue>[] sources = analyzer.analyze("fake", node);
AbstractInsnNode cur = node.instructions.getFirst();
int index = 0;
while (cur != null) {
if (cur.getType() == AbstractInsnNode.METHOD_INSN) {
MethodInsnNode methodInsnNode = (MethodInsnNode) cur;
String owner = methodInsnNode.owner;
String desc = methodInsnNode.desc;
String name = methodInsnNode.name;
//TODO check closure
int paramLength = Type.getArgumentTypes(desc).length + 1;//non static
if (isInvokeOnInlinable(owner, name) /*&& methodInsnNode.owner.equals(INLINE_RUNTIME)*/) {
Frame<SourceValue> frame = sources[index];
SourceValue sourceValue = frame.getStack(frame.getStackSize() - paramLength);
boolean isInlinable = false;
LambdaInfo lambdaInfo = null;
int varIndex = -1;
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);
isInlinable = lambdaInfo != null;
if (isInlinable) {
//remove inlinable access
node.instructions.remove(insnNode);
}
}
}
inlinableInvocation.add(new InlinableAccess(varIndex, isInlinable, getParametersInfo(lambdaInfo, desc)));
}
else if (isLambdaConstructorCall(owner, name)) {
Frame<SourceValue> frame = sources[index];
Map<Integer, InlinableAccess> infos = new HashMap<Integer, InlinableAccess>();
int paramStart = frame.getStackSize() - paramLength;
for (int i = 0; i < paramLength; i++) {
SourceValue sourceValue = frame.getStack(paramStart + i);
if (sourceValue.insns.size() == 1) {
AbstractInsnNode insnNode = sourceValue.insns.iterator().next();
if (insnNode.getType() == AbstractInsnNode.VAR_INSN && insnNode.getOpcode() == Opcodes.ALOAD) {
int varIndex = ((VarInsnNode) insnNode).var;
LambdaInfo lambdaInfo = getLambda(varIndex);
if (lambdaInfo != null) {
InlinableAccess inlinableAccess = new InlinableAccess(varIndex, true, null);
inlinableAccess.setInfo(lambdaInfo);
infos.put(i, inlinableAccess);
//remove inlinable access
node.instructions.remove(insnNode);
}
}
}
}
ConstructorInvocation invocation = new ConstructorInvocation(owner, infos);
constructorInvocation.add(invocation);
}
}
cur = cur.getNext();
index++;
}
return node;
}
public List<ParameterInfo> getParametersInfo(LambdaInfo info, String desc) {
List<ParameterInfo> result = new ArrayList<ParameterInfo>();
Type[] types = Type.getArgumentTypes(desc);
//add skipped this cause closure doesn't have it
//result.add(ParameterInfo.STUB);
ParameterInfo thiz = new ParameterInfo(AsmTypeConstants.OBJECT_TYPE, true, -1, -1);
thiz.setLambda(info);
result.add(thiz);
int index = 1;
if (info != null) {
List<ValueParameterDescriptor> valueParameters = info.getFunctionDescriptor().getValueParameters();
for (ValueParameterDescriptor parameter : valueParameters) {
Type type = typeMapper.mapType(parameter.getType());
int paramIndex = index++;
result.add(new ParameterInfo(type, false, -1, paramIndex));
if (type.getSize() == 2) {
result.add(ParameterInfo.STUB);
}
}
} else {
for (Type type : types) {
int paramIndex = index++;
result.add(new ParameterInfo(type, false, -1, paramIndex));
if (type.getSize() == 2) {
result.add(ParameterInfo.STUB);
}
}
}
return result;
}
public LambdaInfo getLambda(int index) {
if (index < parameters.totalSize()) {
return parameters.get(index).getLambda();
}
return null;
}
private void removeClosureAssertions(MethodNode node) {
AbstractInsnNode cur = node.instructions.getFirst();
while (cur != null && cur.getNext() != null) {
AbstractInsnNode next = cur.getNext();
if (next.getType() == AbstractInsnNode.METHOD_INSN) {
MethodInsnNode methodInsnNode = (MethodInsnNode) next;
if (methodInsnNode.name.equals("checkParameterIsNotNull") && methodInsnNode.owner.equals("jet/runtime/Intrinsics")) {
AbstractInsnNode prev = cur.getPrevious();
assert prev.getType() == AbstractInsnNode.VAR_INSN && prev.getOpcode() == Opcodes.ALOAD;
int varIndex = ((VarInsnNode) prev).var;
LambdaInfo closure = getLambda(varIndex);
if (closure != null) {
node.instructions.remove(prev);
node.instructions.remove(cur);
cur = next.getNext();
node.instructions.remove(next);
next = cur;
}
}
}
cur = next;
}
}
static List<FieldAccess> transformCaptured(
@NotNull MethodNode node,
@NotNull Parameters paramsToSearch,
@NotNull Type lambdaClassType,
@NotNull LambdaFieldRemapper lambdaFieldRemapper
) {
List<FieldAccess> capturedFields = new ArrayList<FieldAccess>();
//remove all this and shift all variables to captured ones size
AbstractInsnNode cur = node.instructions.getFirst();
while (cur != null) {
if (cur.getType() == AbstractInsnNode.FIELD_INSN) {
FieldInsnNode fieldInsnNode = (FieldInsnNode) cur;
//TODO check closure
String owner = fieldInsnNode.owner;
if (lambdaClassType.getInternalName().equals(fieldInsnNode.owner)) {
String name = fieldInsnNode.name;
String desc = fieldInsnNode.desc;
Collection<CapturedParamInfo> vars = paramsToSearch.getCaptured();
CapturedParamInfo result = null;
for (CapturedParamInfo valueDescriptor : vars) {
if (valueDescriptor.getFieldName().equals(name)) {
result = valueDescriptor;
break;
}
}
if (result == null) {
throw new UnsupportedOperationException("Coudn't find field " +
owner +
"." +
name +
" (" +
desc +
") in captured vars of " + lambdaClassType);
}
if (result.isSkipped()) {
//lambda class transformation skip this captured
} else {
cur = lambdaFieldRemapper.doTransform(node, fieldInsnNode, result);
//AbstractInsnNode prev = getPreviousNoLableNoLine(cur);
//
//assert prev.getType() == AbstractInsnNode.VAR_INSN;
//VarInsnNode loadThis = (VarInsnNode) prev;
//assert /*loadThis.var == info.getCapturedVarsSize() - 1 && */loadThis.getOpcode() == Opcodes.ALOAD;
//
//int opcode = fieldInsnNode.getOpcode() == Opcodes.GETFIELD ? result.getType().getOpcode(Opcodes.ILOAD) : result.getType().getOpcode(Opcodes.ISTORE);
//VarInsnNode insn = new VarInsnNode(opcode, result.getIndex());
//
//node.instructions.remove(prev); //remove aload this
//node.instructions.insertBefore(cur, insn);
//node.instructions.remove(cur); //remove aload field
//
//cur = insn;
//
//FieldAccess fieldAccess = new FieldAccess(fieldInsnNode.name, fieldInsnNode.desc, new FieldAccess("" + loadThis.var, lambdaClassType.getInternalName()));
//capturedFields.add(fieldAccess);
}
}
}
cur = cur.getNext();
}
return capturedFields;
}
public static AbstractInsnNode getPreviousNoLabelNoLine(AbstractInsnNode cur) {
AbstractInsnNode prev = cur.getPrevious();
while (prev.getType() == AbstractInsnNode.LABEL || prev.getType() == AbstractInsnNode.LINE) {
prev = prev.getPrevious();
}
return prev;
}
public static void putStackValuesIntoLocals(List<Type> directOrder, int shift, InstructionAdapter mv, String descriptor) {
Type[] actualParams = Type.getArgumentTypes(descriptor); //last param is closure itself
assert actualParams.length == directOrder.size() : "Number of expected and actual params should be equals!";
int size = 0;
for (Type next : directOrder) {
size += next.getSize();
}
shift += size;
int index = directOrder.size();
for (Type next : Lists.reverse(directOrder)) {
shift -= next.getSize();
Type typeOnStack = actualParams[--index];
if (!typeOnStack.equals(next)) {
StackValue.onStack(typeOnStack).put(next, mv);
}
mv.visitVarInsn(next.getOpcode(Opcodes.ISTORE), shift);
}
}
}
@@ -0,0 +1,36 @@
/*
* Copyright 2010-2013 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.asm;
public class NameGenerator {
private final String ownerMethod;
private int nextIndex = 1;
public NameGenerator(String onwerMethod) {
this.ownerMethod = onwerMethod;
}
public String genLambdaClassName() {
return ownerMethod + "$" + nextIndex++;
}
public NameGenerator subGenerator(String inliningMethod) {
return new NameGenerator(ownerMethod+ "$" + inliningMethod + nextIndex++);
}
}
@@ -23,13 +23,15 @@ class ParameterInfo {
public static final ParameterInfo STUB = new ParameterInfo(AsmTypeConstants.OBJECT_TYPE, true, -1, -1);
public final int index;
private int index;
public final Type type;
public final boolean isSkipped;
public final int remapIndex;
private int remapIndex;
public LambdaInfo lambda;
ParameterInfo(Type type, boolean skipped, int remapIndex, int index) {
this.type = type;
@@ -42,8 +44,20 @@ class ParameterInfo {
return isSkipped || remapIndex != -1;
}
public boolean isRemapped() {
return remapIndex != -1;
}
public int getRemapIndex() {
return remapIndex;
}
public int getInlinedIndex() {
return remapIndex != -1 ? remapIndex : index;
return remapIndex != -1 ? getRemapIndex() : getIndex();
}
public int getIndex() {
return index;
}
public boolean isSkipped() {
@@ -53,4 +67,21 @@ class ParameterInfo {
public Type getType() {
return type;
}
public LambdaInfo getLambda() {
return lambda;
}
public void setLambda(LambdaInfo lambda) {
this.lambda = lambda;
}
public void setRemapIndex(int remapIndex) {
this.remapIndex = remapIndex;
}
public void setIndex(int index) {
this.index = index;
}
}
@@ -0,0 +1,116 @@
/*
* Copyright 2010-2013 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.asm;
import com.google.common.collect.Iterables;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.asm4.Type;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Parameters implements Iterable<ParameterInfo> {
private final List<ParameterInfo> real;
private final List<CapturedParamInfo> captured;
public Parameters(List<ParameterInfo> real, List<CapturedParamInfo> captured) {
this.real = real;
this.captured = captured;
}
public List<ParameterInfo> getReal() {
return real;
}
public List<CapturedParamInfo> getCaptured() {
return captured;
}
public int totalSize() {
return real.size() + captured.size();
}
public ParameterInfo get(int index) {
if (index < real.size()) {
return real.get(index);
}
return captured.get(index - real.size());
}
@NotNull
@Override
public Iterator<ParameterInfo> iterator() {
return Iterables.concat(real, captured).iterator();
}
public static List<CapturedParamInfo> transformList(List<CapturedParamInfo> capturedParams, int realSize) {
List<CapturedParamInfo> result = new ArrayList<CapturedParamInfo>();
for (CapturedParamInfo capturedParamInfo : capturedParams) {
CapturedParamInfo newInfo =
new CapturedParamInfo(capturedParamInfo.getFieldName(), capturedParamInfo.getType(), capturedParamInfo.isSkipped,
capturedParamInfo.getIndex(), result.size() + realSize);
newInfo.setLambda(capturedParamInfo.getLambda());
result.add(newInfo);
if (capturedParamInfo.getType().getSize() == 2) {
result.add(CapturedParamInfo.STUB);
}
}
return result;
}
public static List<CapturedParamInfo> addStubs(List<CapturedParamInfo> capturedParams, int realSize) {
List<CapturedParamInfo> result = new ArrayList<CapturedParamInfo>();
for (CapturedParamInfo capturedParamInfo : capturedParams) {
CapturedParamInfo newInfo = new CapturedParamInfo(capturedParamInfo.getFieldName(), capturedParamInfo.getType(), capturedParamInfo.isSkipped,
capturedParamInfo.getRemapIndex(), result.size() + realSize);
newInfo.setLambda(capturedParamInfo.getLambda());
result.add(newInfo);
if (capturedParamInfo.getType().getSize() == 2) {
result.add(CapturedParamInfo.STUB);
}
}
return result;
}
public static List<ParameterInfo> addStubs(List<ParameterInfo> params) {
List<ParameterInfo> result = new ArrayList<ParameterInfo>();
for (ParameterInfo newInfo : params) {
result.add(newInfo);
if (newInfo.getType().getSize() == 2) {
result.add(ParameterInfo.STUB);
}
}
return result;
}
public ArrayList<Type> getCapturedTypes() {
ArrayList<Type> result = new ArrayList<Type>();
for (CapturedParamInfo info : captured) {
if(info != CapturedParamInfo.STUB) {
Type type = info.getType();
result.add(type);
}
}
return result;
}
}
@@ -0,0 +1,90 @@
/*
* Copyright 2010-2013 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.asm;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ParametersBuilder {
private final List<ParameterInfo> params = new ArrayList<ParameterInfo>();
private final List<CapturedParamInfo> capturedParams = new ArrayList<CapturedParamInfo>();
private final List<CapturedParamInfo> additionalCapturedParams = new ArrayList<CapturedParamInfo>();
private int nextIndex = 0;
private int nextCaptured = 0;
public static ParametersBuilder newBuilder() {
return new ParametersBuilder();
}
public ParametersBuilder addThis(Type type, boolean skipped) {
addParameter(new ParameterInfo(type, skipped, -1, nextIndex));
return this;
}
public ParametersBuilder addNextParameter(Type type, boolean skipped, @Nullable ParameterInfo original) {
addParameter(new ParameterInfo(type, skipped, original != null ? original.getIndex() : -1 , nextIndex));
return this;
}
public CapturedParamInfo addCapturedParam(String fieldName, Type type, boolean skipped, @Nullable ParameterInfo original) {
return addCapturedParameter(new CapturedParamInfo(fieldName, type, skipped, original != null ? original.getIndex() : -1, nextCaptured));
}
public CapturedParamInfo addAdditionalCapturedParam(String fieldName, Type type, boolean skipped, @Nullable ParameterInfo original) {
CapturedParamInfo capturedParamInfo =
new CapturedParamInfo(fieldName, type, skipped, original != null ? original.getIndex() : -1, nextCaptured);
additionalCapturedParams.add(capturedParamInfo);
return capturedParamInfo;
}
private void addParameter(ParameterInfo info) {
params.add(info);
nextIndex += info.getType().getSize();
}
private CapturedParamInfo addCapturedParameter(CapturedParamInfo info) {
capturedParams.add(info);
nextCaptured += info.getType().getSize();
return info;
}
public List<ParameterInfo> build() {
return Collections.unmodifiableList(params);
}
public List<CapturedParamInfo> buildCaptured() {
return Collections.unmodifiableList(capturedParams);
}
public List<ParameterInfo> buildWithStubs() {
return Parameters.addStubs(build());
}
public List<CapturedParamInfo> buildCapturedWithStubs() {
return Parameters.addStubs(buildCaptured(), nextIndex);
}
public Parameters buildParameters() {
return new Parameters(buildWithStubs(), buildCapturedWithStubs());
}
}
@@ -16,28 +16,30 @@
package org.jetbrains.jet.codegen.asm;
import org.jetbrains.asm4.*;
import org.jetbrains.asm4.AnnotationVisitor;
import org.jetbrains.asm4.Label;
import org.jetbrains.asm4.MethodVisitor;
import org.jetbrains.asm4.Opcodes;
import org.jetbrains.asm4.commons.InstructionAdapter;
import java.util.ArrayList;
import java.util.List;
//http://asm.ow2.org/current/asm-transformations.pdf
public class InliningAdapter extends InstructionAdapter {
public class RemapVisitor extends InstructionAdapter {
private final Label end;
protected final VarRemapper remapper;
private int nextLocalIndex = 0;
public InliningAdapter(MethodVisitor mv, int api, String desc, Label end, int localsStart, VarRemapper remapper) {
super(api, mv);
private final VarRemapper remapper;
private final boolean remapReturn;
protected RemapVisitor(MethodVisitor mv, Label end, VarRemapper remapper, boolean remapReturn) {
super(InlineCodegenUtil.API, mv);
this.end = end;
this.remapper = remapper;
nextLocalIndex = localsStart;
this.remapReturn = remapReturn;
}
@Override
public void visitInsn(int opcode) {
if (opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) {
if (remapReturn && opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) {
super.visitJumpInsn(Opcodes.GOTO, end);
}
else {
@@ -54,21 +56,13 @@ public class InliningAdapter extends InstructionAdapter {
public void visitVarInsn(int opcode, int var) {
int newVar = remapper.remap(var);
super.visitVarInsn(opcode, newVar);
int size = newVar + (opcode == Opcodes.DSTORE || opcode == Opcodes.LSTORE ? 2 : 1);
if (size > nextLocalIndex) {
nextLocalIndex = size;
}
}
public int getNextLocalIndex() {
return nextLocalIndex;
}
@Override
public void visitLocalVariable(
String name, String desc, String signature, Label start, Label end, int index
) {
//super.visitLocalVariable(name, desc, signature, start, end, index);
}
@Override
@@ -90,5 +84,4 @@ public class InliningAdapter extends InstructionAdapter {
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
return null;
}
}
}
@@ -0,0 +1,40 @@
/*
* Copyright 2010-2013 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.asm;
import org.jetbrains.asm4.MethodVisitor;
import org.jetbrains.asm4.commons.InstructionAdapter;
public class ShiftAdapter extends InstructionAdapter {
private int shift;
public ShiftAdapter(MethodVisitor mv, int shift) {
super(mv);
this.shift = shift;
}
@Override
public void visitIincInsn(int var, int increment) {
super.visitIincInsn(var + shift, increment);
}
@Override
public void visitVarInsn(int opcode, int var) {
super.visitVarInsn(opcode, var + shift);
}
}
@@ -20,11 +20,14 @@ import java.util.List;
public abstract class VarRemapper {
public static class ShiftRemapper extends VarRemapper {
private final int shift;
public ShiftRemapper(int shift) {
public ShiftRemapper(int shift, VarRemapper remapper) {
super(remapper);
this.shift = shift;
}
@@ -36,12 +39,12 @@ public abstract class VarRemapper {
public static class ClosureRemapper extends ShiftRemapper {
private final ClosureInfo info;
private final LambdaInfo info;
private final List<ParameterInfo> originalParams;
private final int capturedSize;
public ClosureRemapper(ClosureInfo info, int valueParametersShift, List<ParameterInfo> originalParams) {
super(valueParametersShift);
public ClosureRemapper(LambdaInfo info, int valueParametersShift, List<ParameterInfo> originalParams) {
super(valueParametersShift, null);
this.info = info;
this.originalParams = originalParams;
capturedSize = info.getCapturedVarsSize();
@@ -58,46 +61,67 @@ public abstract class VarRemapper {
public static class ParamRemapper extends VarRemapper {
private final int paramShift;
private final int paramSize;
private final int additionalParamsSize;
private final List<ParameterInfo> params;
private final int actualParams;
private final int allParamsSize;
private final Parameters params;
private final int actualParamsSize;
public ParamRemapper(int paramShift, int paramSize, int additionalParamsSize, List<ParameterInfo> params) {
this.paramShift = paramShift;
this.paramSize = paramSize;
this.additionalParamsSize = additionalParamsSize;
private final int [] remapIndex;
public ParamRemapper(Parameters params, VarRemapper parent) {
super(parent);
this.allParamsSize = params.totalSize();
this.params = params;
int paramCount = 0;
for (int i = 0; i < params.size(); i++) {
ParameterInfo info = params.get(i);
int realSize = 0;
remapIndex = new int [params.totalSize()];
int index = 0;
for (ParameterInfo info : params) {
if (!info.isSkippedOrRemapped()) {
paramCount += info.getType().getSize();
remapIndex[index] = realSize;
realSize += info.getType().getSize();
} else {
remapIndex[index] = info.isRemapped() ? info.getRemapIndex() : -1;
}
index++;
}
actualParams = paramCount;
actualParamsSize = realSize;
}
@Override
public int doRemap(int index) {
if (index < paramSize) {
int remappedIndex;
if (index < allParamsSize) {
ParameterInfo info = params.get(index);
if (info.isSkipped) {
remappedIndex = remapIndex[index];
if (info.isSkipped || remappedIndex == -1) {
throw new RuntimeException("Trying to access skipped parameter: " + info.type + " at " +index);
}
return info.getInlinedIndex();
if (info.isRemapped()) {
return remappedIndex;
}
} else {
return paramShift + actualParams + index; //captured params not used directly in this inlined method, they used in closure
remappedIndex = actualParamsSize - params.totalSize() + index; //captured params not used directly in this inlined method, they used in closure
}
if (parent == null) {
return remappedIndex;
}
return parent.doRemap(remappedIndex);
}
}
protected boolean nestedRemmapper;
protected VarRemapper parent;
public VarRemapper(VarRemapper parent) {
this.parent = parent;
}
public int remap(int index) {
if (nestedRemmapper) {
return index;
@@ -111,4 +135,14 @@ public abstract class VarRemapper {
nestedRemmapper = nestedRemap;
}
public static int getActualSize(List<ParameterInfo> params) {
int size = 0;
for (ParameterInfo paramInfo : params) {
if (!paramInfo.isSkippedOrRemapped()) {
size += paramInfo.getType().getSize();
}
}
return size;
}
}
@@ -121,13 +121,13 @@ public abstract class AbstractCompileKotlinAgainstKotlinTest extends TestCaseWit
private OutputFileCollection compileA(@NotNull File ktAFile) throws IOException {
JetCoreEnvironment jetCoreEnvironment = JetTestUtils.createEnvironmentWithMockJdkAndIdeaAnnotations(getTestRootDisposable(),
ConfigurationKind.JDK_ONLY);
ConfigurationKind.ALL);
return compileKotlin(ktAFile, aDir, jetCoreEnvironment, getTestRootDisposable());
}
private OutputFileCollection compileB(@NotNull File ktBFile) throws IOException {
CompilerConfiguration configurationWithADirInClasspath = JetTestUtils
.compilerConfigurationForTests(ConfigurationKind.JDK_ONLY, TestJdkKind.MOCK_JDK, JetTestUtils.getAnnotationsJar(), aDir);
.compilerConfigurationForTests(ConfigurationKind.ALL, TestJdkKind.MOCK_JDK, JetTestUtils.getAnnotationsJar(), aDir);
return compileKotlin(ktBFile, bDir, JetCoreEnvironment.createForTests(getTestRootDisposable(), configurationWithADirInClasspath),
getTestRootDisposable());