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