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