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