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.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.lexer.JetTokens; import org.jetbrains.jet.lexer.JetTokens;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -302,25 +303,38 @@ public class AsmUtil {
} }
public static void genClosureFields(CalculatedClosure closure, ClassBuilder v, JetTypeMapper typeMapper) { public static void genClosureFields(CalculatedClosure closure, ClassBuilder v, JetTypeMapper typeMapper) {
List<Pair<String, Type>> allFields = new ArrayList<Pair<String, Type>>();
ClassifierDescriptor captureThis = closure.getCaptureThis(); ClassifierDescriptor captureThis = closure.getCaptureThis();
int access = NO_FLAG_PACKAGE_PRIVATE | ACC_SYNTHETIC | ACC_FINAL;
if (captureThis != null) { if (captureThis != null) {
v.newField(null, access, CAPTURED_THIS_FIELD, typeMapper.mapType(captureThis).getDescriptor(), null, allFields.add(Pair.create(CAPTURED_THIS_FIELD, typeMapper.mapType(captureThis)));
null);
} }
JetType captureReceiverType = closure.getCaptureReceiverType(); JetType captureReceiverType = closure.getCaptureReceiverType();
if (captureReceiverType != null) { if (captureReceiverType != null) {
v.newField(null, access, CAPTURED_RECEIVER_FIELD, typeMapper.mapType(captureReceiverType).getDescriptor(), allFields.add(Pair.create(CAPTURED_RECEIVER_FIELD, typeMapper.mapType(captureReceiverType)));
null, null);
} }
List<Pair<String, Type>> fields = closure.getRecordedFields(); allFields.addAll(closure.getRecordedFields());
for (Pair<String, Type> field : fields) { genClosureFields(allFields, v);
v.newField(null, access, field.first, field.second.getDescriptor(), null, null); }
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) { public static int genAssignInstanceFieldFromParam(FieldInfo info, int index, InstructionAdapter iv) {
assert !info.isStatic(); assert !info.isStatic();
Type fieldType = info.getFieldType(); Type fieldType = info.getFieldType();
@@ -136,6 +136,13 @@ public final class ClassFileFactory extends GenerationStateAware implements Outp
return newVisitor(type, sourceFile); 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 @NotNull
public ClassBuilder forPackagePart(@NotNull Type asmType, @NotNull PsiFile sourceFile) { public ClassBuilder forPackagePart(@NotNull Type asmType, @NotNull PsiFile sourceFile) {
return newVisitor(asmType, 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.binding.CalculatedClosure;
import org.jetbrains.jet.codegen.context.CodegenContext; import org.jetbrains.jet.codegen.context.CodegenContext;
import org.jetbrains.jet.codegen.context.LocalLookup; 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.BothSignatureWriter;
import org.jetbrains.jet.codegen.signature.JvmMethodSignature; import org.jetbrains.jet.codegen.signature.JvmMethodSignature;
import org.jetbrains.jet.codegen.state.GenerationState; import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.codegen.state.JetTypeMapper; import org.jetbrains.jet.codegen.state.JetTypeMapper;
import org.jetbrains.jet.lang.descriptors.*; 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.BindingContext;
import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
import org.jetbrains.jet.lang.resolve.java.JvmAbi; import org.jetbrains.jet.lang.resolve.java.JvmAbi;
import org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils; import org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils;
import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.resolve.name.Name;
@@ -210,6 +207,16 @@ public class ClosureCodegen extends ParentCodegenAwareImpl {
private Method generateConstructor(@NotNull ClassBuilder cv) { private Method generateConstructor(@NotNull ClassBuilder cv) {
List<FieldInfo> args = calculateConstructorParameters(typeMapper, closure, asmType); 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); Type[] argTypes = fieldListToTypeArray(args);
Method constructor = new Method("<init>", Type.VOID_TYPE, argTypes); 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.InstructionAdapter;
import org.jetbrains.asm4.commons.Method; import org.jetbrains.asm4.commons.Method;
import org.jetbrains.jet.codegen.asm.InlineCodegen; 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.Inliner;
import org.jetbrains.jet.codegen.asm.NameGenerator;
import org.jetbrains.jet.codegen.binding.CalculatedClosure; import org.jetbrains.jet.codegen.binding.CalculatedClosure;
import org.jetbrains.jet.codegen.binding.CodegenBinding; import org.jetbrains.jet.codegen.binding.CodegenBinding;
import org.jetbrains.jet.codegen.binding.MutableClosure; import org.jetbrains.jet.codegen.binding.MutableClosure;
@@ -100,6 +102,8 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
@Nullable @Nullable
private final MemberCodegen parentCodegen; private final MemberCodegen parentCodegen;
private NameGenerator inlineNameGenerator;
/* /*
* When we create a temporary variable to hold some value not to compute it many times * 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 * 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) { public void pushClosureOnStack(CalculatedClosure closure, boolean ignoreThisAndReceiver, Inliner inliner) {
if (closure != null) { if (closure != null) {
int paramIndex = 0;
if (!ignoreThisAndReceiver) { if (!ignoreThisAndReceiver) {
ClassDescriptor captureThis = closure.getCaptureThis(); ClassDescriptor captureThis = closure.getCaptureThis();
if (captureThis != null) { if (captureThis != null) {
StackValue thisOrOuter = generateThisOrOuter(captureThis, false); StackValue thisOrOuter = generateThisOrOuter(captureThis, false);
thisOrOuter.put(OBJECT_TYPE, v); if (inliner.shouldPutValue(OBJECT_TYPE, thisOrOuter, context, null)) {
inliner.putInLocal(OBJECT_TYPE, thisOrOuter); thisOrOuter.put(OBJECT_TYPE, v);
}
inliner.putCapturedInLocal(OBJECT_TYPE, thisOrOuter, null, paramIndex++);
} }
JetType captureReceiver = closure.getCaptureReceiverType(); JetType captureReceiver = closure.getCaptureReceiverType();
if (captureReceiver != null) { if (captureReceiver != null) {
Type asmType = typeMapper.mapType(captureReceiver); Type asmType = typeMapper.mapType(captureReceiver);
StackValue.Local capturedReceiver = StackValue.local(context.isStatic() ? 0 : 1, asmType); StackValue.Local capturedReceiver = StackValue.local(context.isStatic() ? 0 : 1, asmType);
capturedReceiver.put(asmType, v); if (inliner.shouldPutValue(asmType, capturedReceiver, context, null)) {
inliner.putInLocal(asmType, capturedReceiver); 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()); sharedVarType = typeMapper.mapType((VariableDescriptor) entry.getKey());
} }
StackValue capturedVar = entry.getValue().getOuterValue(this); StackValue capturedVar = entry.getValue().getOuterValue(this);
if (inliner.shouldPutValue(sharedVarType, capturedVar, context)) { if (inliner.shouldPutValue(sharedVarType, capturedVar, context, null)) {
capturedVar.put(sharedVarType, v); 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) { if (callable instanceof CallableMethod) {
CallableMethod callableMethod = (CallableMethod) callable; CallableMethod callableMethod = (CallableMethod) callable;
invokeMethodWithArguments(callableMethod, resolvedCall, receiver); invokeMethodWithArguments(call, callableMethod, resolvedCall, receiver);
//noinspection ConstantConditions
Type returnType = typeMapper.mapReturnType(resolvedCall.getResultingDescriptor()); Type returnType = typeMapper.mapReturnType(resolvedCall.getResultingDescriptor());
StackValue.coerce(callableMethod.getReturnType(), returnType, v); StackValue.coerce(callableMethod.getReturnType(), returnType, v);
return StackValue.onStack(returnType); return StackValue.onStack(returnType);
@@ -2104,6 +2114,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
public void invokeMethodWithArguments( public void invokeMethodWithArguments(
@Nullable Call call,
@NotNull CallableMethod callableMethod, @NotNull CallableMethod callableMethod,
@NotNull ResolvedCall<? extends CallableDescriptor> resolvedCall, @NotNull ResolvedCall<? extends CallableDescriptor> resolvedCall,
@NotNull StackValue receiver @NotNull StackValue receiver
@@ -2354,12 +2365,12 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
JetExpression argumentExpression = valueArgument.getArgumentExpression(); JetExpression argumentExpression = valueArgument.getArgumentExpression();
assert argumentExpression != null : valueArgument.asElement().getText(); assert argumentExpression != null : valueArgument.asElement().getText();
if (inliner.isInliningClosure(argumentExpression)) { if (inliner.isInliningClosure(argumentExpression, valueParameter)) {
inliner.rememberClosure((JetFunctionLiteralExpression) argumentExpression, parameterType); inliner.rememberClosure((JetFunctionLiteralExpression) argumentExpression, parameterType);
putInLocal = false; putInLocal = false;
} else { } else {
StackValue value = gen(argumentExpression); StackValue value = gen(argumentExpression);
if (inliner.shouldPutValue(parameterType, value, context)) { if (inliner.shouldPutValue(parameterType, value, context, valueParameter)) {
value.put(parameterType, v); value.put(parameterType, v);
} }
valueIfPresent = value; valueIfPresent = value;
@@ -2377,7 +2388,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
} }
if (putInLocal) { if (putInLocal) {
inliner.putInLocal(parameterType, valueIfPresent); inliner.putInLocal(parameterType, valueIfPresent, valueParameter);
} }
} }
return mask; return mask;
@@ -3342,7 +3353,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
ConstructorDescriptor originalOfSamAdapter = (ConstructorDescriptor) SamCodegenUtil.getOriginalIfSamAdapter(constructorDescriptor); ConstructorDescriptor originalOfSamAdapter = (ConstructorDescriptor) SamCodegenUtil.getOriginalIfSamAdapter(constructorDescriptor);
CallableMethod method = typeMapper.mapToCallableMethod(originalOfSamAdapter == null ? constructorDescriptor : originalOfSamAdapter); CallableMethod method = typeMapper.mapToCallableMethod(originalOfSamAdapter == null ? constructorDescriptor : originalOfSamAdapter);
invokeMethodWithArguments(method, resolvedCall, StackValue.none()); invokeMethodWithArguments(null, method, resolvedCall, StackValue.none());
return StackValue.onStack(type); 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() { public MethodContext getContext() {
return context; 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); superCallable.invokeWithNotNullAssertion(codegen.v, state, resolvedCall);
} }
else { 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()); CallableMethod method = typeMapper.mapToCallableMethod((ConstructorDescriptor) resolvedCall.getResultingDescriptor());
codegen.invokeMethodWithArguments(method, resolvedCall, StackValue.none()); codegen.invokeMethodWithArguments(null, method, resolvedCall, StackValue.none());
} }
else { else {
iv.invokespecial(implClass.getInternalName(), "<init>", "(Ljava/lang/String;I)V"); 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 @NotNull
public ClassBuilder getBuilder() { public ClassBuilder getBuilder() {
return builder; 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; 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 int index;
public final boolean inlinable; 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; this.index = index;
inlinable = isInlinable; inlinable = isInlinable;
this.parameters = parameterInfos;
} }
public boolean isInlinable() { public boolean isInlinable() {
return inlinable; 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; package org.jetbrains.jet.codegen.asm;
import com.google.common.collect.Lists;
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.*; import org.jetbrains.asm4.ClassVisitor;
import org.jetbrains.asm4.commons.InstructionAdapter; import org.jetbrains.asm4.Opcodes;
import org.jetbrains.asm4.Type;
import org.jetbrains.asm4.commons.Method; import org.jetbrains.asm4.commons.Method;
import org.jetbrains.asm4.tree.*; import org.jetbrains.asm4.tree.MethodNode;
import org.jetbrains.asm4.tree.analysis.*;
import org.jetbrains.asm4.tree.analysis.Frame;
import org.jetbrains.asm4.util.Textifier; import org.jetbrains.asm4.util.Textifier;
import org.jetbrains.asm4.util.TraceMethodVisitor; import org.jetbrains.asm4.util.TraceMethodVisitor;
import org.jetbrains.jet.codegen.*; import org.jetbrains.jet.codegen.*;
import org.jetbrains.jet.codegen.context.CodegenContext; 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.MethodContext;
import org.jetbrains.jet.codegen.context.PackageContext;
import org.jetbrains.jet.codegen.signature.JvmMethodParameterKind; import org.jetbrains.jet.codegen.signature.JvmMethodParameterKind;
import org.jetbrains.jet.codegen.signature.JvmMethodParameterSignature; import org.jetbrains.jet.codegen.signature.JvmMethodParameterSignature;
import org.jetbrains.jet.codegen.signature.JvmMethodSignature; 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.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingContextUtils; import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants; 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 org.jetbrains.jet.renderer.DescriptorRenderer;
import java.io.IOException; import java.io.IOException;
@@ -56,7 +56,7 @@ import static org.jetbrains.jet.codegen.AsmUtil.*;
public class InlineCodegen implements ParentCodegenAware, Inliner { public class InlineCodegen implements ParentCodegenAware, Inliner {
public final static String INVOKE = "invoke"; private final JetTypeMapper typeMapper;
private final ExpressionCodegen codegen; private final ExpressionCodegen codegen;
@@ -66,16 +66,10 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
private final boolean disabled; private final boolean disabled;
private final Call call;
private final SimpleFunctionDescriptor functionDescriptor; 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 BindingContext bindingContext;
private final MethodContext context; private final MethodContext context;
@@ -86,19 +80,28 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
private final JvmMethodSignature jvmSignature; 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( public InlineCodegen(
@NotNull ExpressionCodegen codegen, @NotNull ExpressionCodegen codegen,
boolean notSeparateInline, boolean notSeparateInline,
@NotNull GenerationState state, @NotNull GenerationState state,
boolean disabled, boolean disabled,
@NotNull SimpleFunctionDescriptor functionDescriptor @NotNull SimpleFunctionDescriptor functionDescriptor,
@NotNull Call call
) { ) {
this.state = state;
this.typeMapper = state.getTypeMapper();
this.codegen = codegen; this.codegen = codegen;
this.notSeparateInline = notSeparateInline; this.notSeparateInline = notSeparateInline;
this.state = state;
this.disabled = disabled; this.disabled = disabled;
this.call = call;
this.functionDescriptor = functionDescriptor.getOriginal(); this.functionDescriptor = functionDescriptor.getOriginal();
typeMapper = codegen.getTypeMapper();
bindingContext = codegen.getBindingContext(); bindingContext = codegen.getBindingContext();
initialFrameSize = codegen.getFrameMap().getCurrentSize(); initialFrameSize = codegen.getFrameMap().getCurrentSize();
@@ -114,7 +117,7 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
try { try {
node = createMethodNode(callableMethod); node = createMethodNode(callableMethod);
inlineCall(node, true); inlineCall(node);
} }
catch (CompilationException e) { catch (CompilationException e) {
throw e; throw e;
@@ -126,14 +129,14 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
functionDescriptor.getName() + functionDescriptor.getName() +
"' into \n" + (element != null ? element.getText() : "null psi element " + codegen.getContext().getContextDescriptor()) + "' into \n" + (element != null ? element.getText() : "null psi element " + codegen.getContext().getContextDescriptor()) +
"\ncause: " + "\ncause: " +
text, e, null); text, e, call.getCallElement());
} }
} }
@NotNull @NotNull
private MethodNode createMethodNode(CallableMethod callableMethod) private MethodNode createMethodNode(CallableMethod callableMethod)
throws ClassNotFoundException, IOException { throws ClassNotFoundException, IOException {
MethodNode node = null; MethodNode node;
if (functionDescriptor instanceof DeserializedSimpleFunctionDescriptor) { if (functionDescriptor instanceof DeserializedSimpleFunctionDescriptor) {
VirtualFile file = InlineCodegenUtil.getVirtualFileForCallable((DeserializedSimpleFunctionDescriptor) functionDescriptor, state); VirtualFile file = InlineCodegenUtil.getVirtualFileForCallable((DeserializedSimpleFunctionDescriptor) functionDescriptor, state);
node = InlineCodegenUtil.getMethodNode(file.getInputStream(), functionDescriptor.getName().asString(), node = InlineCodegenUtil.getMethodNode(file.getInputStream(), functionDescriptor.getName().asString(),
@@ -166,160 +169,43 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
(JetDeclarationWithBody) element), (JetDeclarationWithBody) element),
getParentCodegen()); getParentCodegen());
//TODO //TODO
node.visitMaxs(20, 20); node.visitMaxs(30, 30);
node.visitEnd(); node.visitEnd();
} }
return node; return node;
} }
private void inlineCall(MethodNode node, boolean inlineClosures) { private void inlineCall(MethodNode node) {
if (inlineClosures) {
removeClosureAssertions(node);
try {
markPlacesForInlineAndRemoveInlinable(node);
}
catch (AnalyzerException e) {
throw new RuntimeException(e);
}
}
int valueParamSize = originalFunctionFrame.getCurrentSize();
int originalSize = codegen.getFrameMap().getCurrentSize();
generateClosuresBodies(); generateClosuresBodies();
List<ParameterInfo> realParams = new ArrayList<ParameterInfo>(tempTypes);
putClosureParametersOnStack(); 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) { Parameters parameters = new Parameters(realParams, Parameters.addStubs(captured, realParams.size()));
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;
}
}
private void markPlacesForInlineAndRemoveInlinable(@NotNull MethodNode node) throws AnalyzerException { InliningInfo info =
Analyzer<SourceValue> analyzer = new Analyzer<SourceValue>(new SourceInterpreter()); new InliningInfo(expressionMap, null, null, null, state,
Frame<SourceValue>[] sources = analyzer.analyze("fake", node); 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(); VarRemapper.ParamRemapper remapper = new VarRemapper.ParamRemapper(parameters, new VarRemapper.ShiftRemapper(initialFrameSize, null));
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;
AbstractInsnNode insnNode = sourceValue.insns.iterator().next(); inliner.doTransformAndMerge(codegen.getInstructionAdapter(), remapper);
assert insnNode.getType() == AbstractInsnNode.VAR_INSN && insnNode.getOpcode() == Opcodes.ALOAD; generateClosuresBodies();
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");
} }
private void generateClosuresBodies() { private void generateClosuresBodies() {
for (ClosureInfo info : expressionMap.values()) { for (LambdaInfo info : expressionMap.values()) {
info.setNode(generateClosureBody(info)); info.setNode(generateClosureBody(info));
} }
} }
private MethodNode generateClosureBody(ClosureInfo info) { private MethodNode generateClosureBody(LambdaInfo info) {
JetFunctionLiteral declaration = info.getFunctionLiteral(); JetFunctionLiteral declaration = info.getFunctionLiteral();
FunctionDescriptor descriptor = info.getFunctionDescriptor(); FunctionDescriptor descriptor = info.getFunctionDescriptor();
@@ -331,113 +217,58 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
Method asmMethod = jvmMethodSignature.getAsmMethod(); Method asmMethod = jvmMethodSignature.getAsmMethod();
MethodNode methodNode = new MethodNode(Opcodes.ASM4, getMethodAsmFlags(descriptor, context.getContextKind()), asmMethod.getName(), asmMethod.getDescriptor(), jvmMethodSignature.getGenericsSignature(), null); 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 @Override
public boolean generateLocalVarTable() { public boolean generateLocalVarTable() {
return false; return false;
} }
}, codegen.getParentCodegen()); }, 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 @Override
AbstractInsnNode cur = transformedNode.instructions.getFirst(); public void putInLocal(Type type, StackValue stackValue, ValueParameterDescriptor valueParameterDescriptor) {
while (cur != null) { putCapturedInLocal(type, stackValue, valueParameterDescriptor, -1);
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 @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) { if (!disabled && notSeparateInline && Type.VOID_TYPE != type) {
//TODO remap only inlinable closure => otherwise we could get a lot of problem //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; int remappedIndex = couldBeRemapped ? ((StackValue.Local) stackValue).getIndex() : -1;
ParameterInfo info = new ParameterInfo(type, false, remappedIndex, couldBeRemapped ? -1 : codegen.getFrameMap().enterTemp(type)); 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); doWithParameter(info);
} }
} }
@Override @Override
public boolean shouldPutValue(Type type, StackValue stackValue, MethodContext context) { public boolean shouldPutValue(Type type, StackValue stackValue, MethodContext context, ValueParameterDescriptor descriptor) {
if (stackValue != null && context.isInlineFunction() && stackValue instanceof StackValue.Local) { //boolean isInline = true/*context.isInlineFunction() || context.getParentContext() instanceof ClosureContext*/;
if (isInvokeOnInlinable(type.getClassName(), "invoke")) { //if (stackValue != null && isInline && stackValue instanceof StackValue.Local) {
//TODO remap only inlinable closure => otherwise we could get a lot of problem // if (isInvokeOnInlinable(type.getClassName(), "invoke") && (descriptor == null || !InlineUtil.hasNoinlineAnnotation(descriptor))) {
return false; //TODO check annotations // //TODO remap only inlinable closure => otherwise we could get a lot of problem
} // return false;
} // }
return true; //}
return false == (stackValue != null && stackValue instanceof StackValue.Local);
} }
private void doWithParameter(ParameterInfo info) { private void doWithParameter(ParameterInfo info) {
@@ -459,7 +290,7 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
private void putParameterOnStack(ParameterInfo info) { private void putParameterOnStack(ParameterInfo info) {
if (!info.isSkippedOrRemapped()) { if (!info.isSkippedOrRemapped()) {
int index = info.index; int index = info.getIndex();
Type type = info.type; Type type = info.type;
StackValue.local(index, type).store(type, codegen.getInstructionAdapter()); StackValue.local(index, type).store(type, codegen.getInstructionAdapter());
} }
@@ -484,7 +315,7 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
recordParamInfo(info, false); 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(); ParameterInfo param = iterator.previous();
putParameterOnStack(param); putParameterOnStack(param);
} }
@@ -493,7 +324,7 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
@Override @Override
public void leaveTemps() { public void leaveTemps() {
FrameMap frameMap = codegen.getFrameMap(); 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(); ParameterInfo param = iterator.previous();
if (!param.isSkippedOrRemapped()) { if (!param.isSkippedOrRemapped()) {
frameMap.leaveTemp(param.type); frameMap.leaveTemp(param.type);
@@ -505,31 +336,11 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
return disabled; 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 @Override
public boolean isInliningClosure(JetExpression expression) { public boolean isInliningClosure(JetExpression expression, ValueParameterDescriptor valueParameterDescriptora) {
return !disabled && expression instanceof JetFunctionLiteralExpression; return !disabled &&
expression instanceof JetFunctionLiteralExpression &&
!InlineUtil.hasNoinlineAnnotation(valueParameterDescriptora);
} }
@Override @Override
@@ -537,19 +348,35 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
ParameterInfo closureInfo = new ParameterInfo(type, true, -1, -1); ParameterInfo closureInfo = new ParameterInfo(type, true, -1, -1);
int index = recordParamInfo(closureInfo, true); int index = recordParamInfo(closureInfo, true);
ClosureInfo info = new ClosureInfo(expression, typeMapper); LambdaInfo info = new LambdaInfo(expression, typeMapper);
expressionMap.put(index, info); expressionMap.put(index, info);
closureInfo.setLambda(info);
} }
private void putClosureParametersOnStack() { private void putClosureParametersOnStack() {
//TODO: SORT //TODO: SORT
for (ClosureInfo next : expressionMap.values()) { int currentSize = tempTypes.size();
for (LambdaInfo next : expressionMap.values()) {
if (next.closure != null) { if (next.closure != null) {
int size = tempTypes.size(); activeLambda = next;
next.setParamOffset(size); next.setParamOffset(currentSize);
codegen.pushClosureOnStack(next.closure, false, this); 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 @Nullable
@@ -559,8 +386,8 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
} }
public static CodegenContext getContext(DeclarationDescriptor descriptor, GenerationState state) { public static CodegenContext getContext(DeclarationDescriptor descriptor, GenerationState state) {
if (descriptor instanceof NamespaceDescriptor) { if (descriptor instanceof PackageFragmentDescriptor) {
return new NamespaceContext((NamespaceDescriptor) descriptor, null); return new PackageContext((PackageFragmentDescriptor) descriptor, null);
} }
CodegenContext parent = getContext(descriptor.getContainingDeclaration(), state); CodegenContext parent = getContext(descriptor.getContainingDeclaration(), state);
@@ -580,7 +407,7 @@ public class InlineCodegen implements ParentCodegenAware, Inliner {
} }
@NotNull @NotNull
private static String getNodeText(@Nullable MethodNode node) { public static String getNodeText(@Nullable MethodNode node) {
if (node == null) { if (node == null) {
return "Not generated"; return "Not generated";
} }
@@ -19,18 +19,20 @@ package org.jetbrains.jet.codegen.asm;
import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.ClassReader; import org.jetbrains.asm4.*;
import org.jetbrains.asm4.ClassVisitor;
import org.jetbrains.asm4.MethodVisitor;
import org.jetbrains.asm4.Opcodes;
import org.jetbrains.asm4.tree.MethodNode; 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.GenerationState;
import org.jetbrains.jet.codegen.state.JetTypeMapper;
import org.jetbrains.jet.descriptors.serialization.JavaProtoBuf; import org.jetbrains.jet.descriptors.serialization.JavaProtoBuf;
import org.jetbrains.jet.descriptors.serialization.ProtoBuf; import org.jetbrains.jet.descriptors.serialization.ProtoBuf;
import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedSimpleFunctionDescriptor; import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedSimpleFunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.*; 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.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.java.PackageClassUtils; import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
import org.jetbrains.jet.lang.resolve.kotlin.VirtualFileFinder; 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.IOException;
import java.io.InputStream; 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 { public class InlineCodegenUtil {
private final static int API = Opcodes.ASM4; public final static int API = Opcodes.ASM4;
public final static String INVOKE = "invoke";
@Nullable @Nullable
public static MethodNode getMethodNode( public static MethodNode getMethodNode(
@@ -73,14 +77,14 @@ public class InlineCodegenUtil {
public static VirtualFile getVirtualFileForCallable(DeserializedSimpleFunctionDescriptor deserializedDescriptor, GenerationState state) { public static VirtualFile getVirtualFileForCallable(DeserializedSimpleFunctionDescriptor deserializedDescriptor, GenerationState state) {
VirtualFile file = null; VirtualFile file = null;
DeclarationDescriptor parentDeclatation = deserializedDescriptor.getContainingDeclaration(); DeclarationDescriptor parentDeclatation = deserializedDescriptor.getContainingDeclaration();
if (parentDeclatation instanceof NamespaceDescriptor) { if (parentDeclatation instanceof PackageFragmentDescriptor) {
ProtoBuf.Callable proto = deserializedDescriptor.getFunctionProto(); ProtoBuf.Callable proto = deserializedDescriptor.getFunctionProto();
if (proto.hasExtension(JavaProtoBuf.implClassName)) { if (proto.hasExtension(JavaProtoBuf.implClassName)) {
Name name = deserializedDescriptor.getNameResolver().getName(proto.getExtension(JavaProtoBuf.implClassName)); Name name = deserializedDescriptor.getNameResolver().getName(proto.getExtension(JavaProtoBuf.implClassName));
FqName namespaceFqName = FqName namespaceFqName =
PackageClassUtils.getPackageClassFqName(((NamespaceDescriptor) parentDeclatation).getFqName()).parent().child( PackageClassUtils.getPackageClassFqName(((PackageFragmentDescriptor) parentDeclatation).getFqName()).parent().child(
name); name);
file = findVirtualFile(state.getProject(), namespaceFqName); file = findVirtualFile(state.getProject(), namespaceFqName, true);
} else { } else {
assert false : "Function in namespace should have implClassName property in proto: " + deserializedDescriptor; assert false : "Function in namespace should have implClassName property in proto: " + deserializedDescriptor;
} }
@@ -96,33 +100,72 @@ public class InlineCodegenUtil {
} }
@Nullable @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); VirtualFileFinder fileFinder = ServiceManager.getService(project, VirtualFileFinder.class);
VirtualFile virtualFile = fileFinder.findVirtualFile(containerFqName); if (onlyKotlin) {
if (virtualFile == null) { return fileFinder.findVirtualFile(containerFqName);
return null; } else {
return fileFinder.findVirtualFile(containerFqName.asString().replace('.', '/'));
} }
return virtualFile;
} }
//TODO: navigate to inner classes //TODO: navigate to inner classes
@Nullable @Nullable
private static FqName getContainerFqName(@NotNull DeclarationDescriptor referencedDescriptor) { public static FqName getContainerFqName(@NotNull DeclarationDescriptor referencedDescriptor) {
ClassOrNamespaceDescriptor ClassOrPackageFragmentDescriptor
containerDescriptor = DescriptorUtils.getParentOfType(referencedDescriptor, ClassOrNamespaceDescriptor.class, false); containerDescriptor = DescriptorUtils.getParentOfType(referencedDescriptor, ClassOrPackageFragmentDescriptor.class, false);
if (containerDescriptor instanceof NamespaceDescriptor) { if (containerDescriptor instanceof PackageFragmentDescriptor) {
return PackageClassUtils.getPackageClassFqName(getFQName(containerDescriptor).toSafe()); return PackageClassUtils.getPackageClassFqName(getFqName(containerDescriptor).toSafe());
} }
if (containerDescriptor instanceof ClassDescriptor) { if (containerDescriptor instanceof ClassDescriptor) {
ClassKind classKind = ((ClassDescriptor) containerDescriptor).getKind(); ClassKind classKind = ((ClassDescriptor) containerDescriptor).getKind();
if (classKind == ClassKind.CLASS_OBJECT || classKind == ClassKind.ENUM_ENTRY) { if (classKind == ClassKind.CLASS_OBJECT || classKind == ClassKind.ENUM_ENTRY) {
return getContainerFqName(containerDescriptor.getContainingDeclaration()); return getContainerFqName(containerDescriptor.getContainingDeclaration());
} }
return getFQName(containerDescriptor).toSafe(); return getFqName(containerDescriptor).toSafe();
} }
return null; 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 @Nullable
private static VirtualFile findVirtualFileContainingDescriptor( private static VirtualFile findVirtualFileContainingDescriptor(
@NotNull Project project, @NotNull Project project,
@@ -132,7 +175,44 @@ public class InlineCodegenUtil {
if (containerFqName == null) { if (containerFqName == null) {
return 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.CallableMethod;
import org.jetbrains.jet.codegen.StackValue; import org.jetbrains.jet.codegen.StackValue;
import org.jetbrains.jet.codegen.context.MethodContext; 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.JetExpression;
import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression; import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression;
@@ -35,7 +36,14 @@ public interface Inliner {
} }
@Override @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 @Override
public boolean isInliningClosure(JetExpression expression) { public boolean isInliningClosure(JetExpression expression, ValueParameterDescriptor valueParameterDescriptora) {
return false; return false;
} }
@@ -61,7 +69,8 @@ public interface Inliner {
@Override @Override
public boolean shouldPutValue( public boolean shouldPutValue(
Type type, StackValue stackValue, MethodContext context Type type, StackValue stackValue, MethodContext context,
ValueParameterDescriptor descriptor
) { ) {
return true; return true;
} }
@@ -69,15 +78,17 @@ public interface Inliner {
void inlineCall(CallableMethod callableMethod, ClassVisitor visitor); 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 putHiddenParams();
void leaveTemps(); void leaveTemps();
boolean isInliningClosure(JetExpression expression); boolean isInliningClosure(JetExpression expression, ValueParameterDescriptor valueParameterDescriptora);
void rememberClosure(JetFunctionLiteralExpression expression, Type type); 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.anonymousClassForFunction;
import static org.jetbrains.jet.codegen.binding.CodegenBinding.asmTypeForAnonymousClass; import static org.jetbrains.jet.codegen.binding.CodegenBinding.asmTypeForAnonymousClass;
public class ClosureInfo { public class LambdaInfo {
public final JetFunctionLiteralExpression expression; public final JetFunctionLiteralExpression expression;
@@ -47,7 +47,7 @@ public class ClosureInfo {
private MethodNode node; private MethodNode node;
private Collection<EnclosedValueDescriptor> capturedVars; private List<CapturedParamInfo> capturedVars;
private final FunctionDescriptor functionDescriptor; private final FunctionDescriptor functionDescriptor;
@@ -57,7 +57,7 @@ public class ClosureInfo {
private int paramOffset; private int paramOffset;
ClosureInfo(@NotNull JetFunctionLiteralExpression expression, @NotNull JetTypeMapper typeMapper) { LambdaInfo(@NotNull JetFunctionLiteralExpression expression, @NotNull JetTypeMapper typeMapper) {
this.expression = expression; this.expression = expression;
this.typeMapper = typeMapper; this.typeMapper = typeMapper;
BindingContext bindingContext = typeMapper.getBindingContext(); BindingContext bindingContext = typeMapper.getBindingContext();
@@ -91,38 +91,56 @@ public class ClosureInfo {
return classDescriptor; return classDescriptor;
} }
public Type getClosureClassType() { public Type getLambdaClassType() {
return closureClassType; return closureClassType;
} }
public Collection<EnclosedValueDescriptor> getCapturedVars() { public List<CapturedParamInfo> getCapturedVars() {
//lazy initialization cause it would be calculated after object creation //lazy initialization cause it would be calculated after object creation
int index = 0;
if (capturedVars == null) { if (capturedVars == null) {
capturedVars = new ArrayList<EnclosedValueDescriptor>(); capturedVars = new ArrayList<CapturedParamInfo>();
if (closure.getCaptureThis() != null) { if (closure.getCaptureThis() != null) {
EnclosedValueDescriptor descriptor = new EnclosedValueDescriptor(AsmUtil.CAPTURED_THIS_FIELD, null, null, typeMapper.mapType(closure.getCaptureThis())); 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) { if (closure.getCaptureReceiverType() != null) {
EnclosedValueDescriptor descriptor = new EnclosedValueDescriptor(AsmUtil.CAPTURED_RECEIVER_FIELD, null, null, typeMapper.mapType(closure.getCaptureReceiverType())); 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) { 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; 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() { public int getParamOffset() {
return paramOffset; return paramOffset;
} }
public void setParamOffset(int paramOffset) { public void setParamOffset(int paramOffset) {
this.paramOffset = paramOffset; this.paramOffset = paramOffset;
shiftParams(paramOffset);
} }
public List<Type> getParamsWithoutCapturedValOrVar() { public List<Type> getParamsWithoutCapturedValOrVar() {
@@ -132,8 +150,7 @@ public class ClosureInfo {
public int getCapturedVarsSize() { public int getCapturedVarsSize() {
int size = 0; int size = 0;
for (Iterator<EnclosedValueDescriptor> iterator = getCapturedVars().iterator(); iterator.hasNext(); ) { for (CapturedParamInfo next : getCapturedVars()) {
EnclosedValueDescriptor next = iterator.next();
size += next.getType().getSize(); size += next.getType().getSize();
} }
return size; 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 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 Type type;
public final boolean isSkipped; public final boolean isSkipped;
public final int remapIndex; private int remapIndex;
public LambdaInfo lambda;
ParameterInfo(Type type, boolean skipped, int remapIndex, int index) { ParameterInfo(Type type, boolean skipped, int remapIndex, int index) {
this.type = type; this.type = type;
@@ -42,8 +44,20 @@ class ParameterInfo {
return isSkipped || remapIndex != -1; return isSkipped || remapIndex != -1;
} }
public boolean isRemapped() {
return remapIndex != -1;
}
public int getRemapIndex() {
return remapIndex;
}
public int getInlinedIndex() { public int getInlinedIndex() {
return remapIndex != -1 ? remapIndex : index; return remapIndex != -1 ? getRemapIndex() : getIndex();
}
public int getIndex() {
return index;
} }
public boolean isSkipped() { public boolean isSkipped() {
@@ -53,4 +67,21 @@ class ParameterInfo {
public Type getType() { public Type getType() {
return type; 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; 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 org.jetbrains.asm4.commons.InstructionAdapter;
import java.util.ArrayList; public class RemapVisitor extends InstructionAdapter {
import java.util.List;
//http://asm.ow2.org/current/asm-transformations.pdf
public class InliningAdapter extends InstructionAdapter {
private final Label end; 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) { private final VarRemapper remapper;
super(api, mv);
private final boolean remapReturn;
protected RemapVisitor(MethodVisitor mv, Label end, VarRemapper remapper, boolean remapReturn) {
super(InlineCodegenUtil.API, mv);
this.end = end; this.end = end;
this.remapper = remapper; this.remapper = remapper;
nextLocalIndex = localsStart; this.remapReturn = remapReturn;
} }
@Override
public void visitInsn(int opcode) { 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); super.visitJumpInsn(Opcodes.GOTO, end);
} }
else { else {
@@ -54,21 +56,13 @@ public class InliningAdapter extends InstructionAdapter {
public void visitVarInsn(int opcode, int var) { public void visitVarInsn(int opcode, int var) {
int newVar = remapper.remap(var); int newVar = remapper.remap(var);
super.visitVarInsn(opcode, newVar); 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 @Override
public void visitLocalVariable( public void visitLocalVariable(
String name, String desc, String signature, Label start, Label end, int index String name, String desc, String signature, Label start, Label end, int index
) { ) {
//super.visitLocalVariable(name, desc, signature, start, end, index);
} }
@Override @Override
@@ -90,5 +84,4 @@ public class InliningAdapter extends InstructionAdapter {
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
return null; 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 abstract class VarRemapper {
public static class ShiftRemapper extends VarRemapper { public static class ShiftRemapper extends VarRemapper {
private final int shift; private final int shift;
public ShiftRemapper(int shift) { public ShiftRemapper(int shift, VarRemapper remapper) {
super(remapper);
this.shift = shift; this.shift = shift;
} }
@@ -36,12 +39,12 @@ public abstract class VarRemapper {
public static class ClosureRemapper extends ShiftRemapper { public static class ClosureRemapper extends ShiftRemapper {
private final ClosureInfo info; private final LambdaInfo info;
private final List<ParameterInfo> originalParams; private final List<ParameterInfo> originalParams;
private final int capturedSize; private final int capturedSize;
public ClosureRemapper(ClosureInfo info, int valueParametersShift, List<ParameterInfo> originalParams) { public ClosureRemapper(LambdaInfo info, int valueParametersShift, List<ParameterInfo> originalParams) {
super(valueParametersShift); super(valueParametersShift, null);
this.info = info; this.info = info;
this.originalParams = originalParams; this.originalParams = originalParams;
capturedSize = info.getCapturedVarsSize(); capturedSize = info.getCapturedVarsSize();
@@ -58,46 +61,67 @@ public abstract class VarRemapper {
public static class ParamRemapper extends VarRemapper { public static class ParamRemapper extends VarRemapper {
private final int paramShift; private final int allParamsSize;
private final int paramSize; private final Parameters params;
private final int additionalParamsSize; private final int actualParamsSize;
private final List<ParameterInfo> params;
private final int actualParams;
public ParamRemapper(int paramShift, int paramSize, int additionalParamsSize, List<ParameterInfo> params) { private final int [] remapIndex;
this.paramShift = paramShift;
this.paramSize = paramSize; public ParamRemapper(Parameters params, VarRemapper parent) {
this.additionalParamsSize = additionalParamsSize; super(parent);
this.allParamsSize = params.totalSize();
this.params = params; this.params = params;
int paramCount = 0; int realSize = 0;
for (int i = 0; i < params.size(); i++) { remapIndex = new int [params.totalSize()];
ParameterInfo info = params.get(i);
int index = 0;
for (ParameterInfo info : params) {
if (!info.isSkippedOrRemapped()) { 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 @Override
public int doRemap(int index) { public int doRemap(int index) {
if (index < paramSize) { int remappedIndex;
if (index < allParamsSize) {
ParameterInfo info = params.get(index); 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); throw new RuntimeException("Trying to access skipped parameter: " + info.type + " at " +index);
} }
return info.getInlinedIndex(); if (info.isRemapped()) {
return remappedIndex;
}
} else { } 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 boolean nestedRemmapper;
protected VarRemapper parent;
public VarRemapper(VarRemapper parent) {
this.parent = parent;
}
public int remap(int index) { public int remap(int index) {
if (nestedRemmapper) { if (nestedRemmapper) {
return index; return index;
@@ -111,4 +135,14 @@ public abstract class VarRemapper {
nestedRemmapper = nestedRemap; 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 { private OutputFileCollection compileA(@NotNull File ktAFile) throws IOException {
JetCoreEnvironment jetCoreEnvironment = JetTestUtils.createEnvironmentWithMockJdkAndIdeaAnnotations(getTestRootDisposable(), JetCoreEnvironment jetCoreEnvironment = JetTestUtils.createEnvironmentWithMockJdkAndIdeaAnnotations(getTestRootDisposable(),
ConfigurationKind.JDK_ONLY); ConfigurationKind.ALL);
return compileKotlin(ktAFile, aDir, jetCoreEnvironment, getTestRootDisposable()); return compileKotlin(ktAFile, aDir, jetCoreEnvironment, getTestRootDisposable());
} }
private OutputFileCollection compileB(@NotNull File ktBFile) throws IOException { private OutputFileCollection compileB(@NotNull File ktBFile) throws IOException {
CompilerConfiguration configurationWithADirInClasspath = JetTestUtils 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), return compileKotlin(ktBFile, bDir, JetCoreEnvironment.createForTests(getTestRootDisposable(), configurationWithADirInClasspath),
getTestRootDisposable()); getTestRootDisposable());