Method && Closure Inline

This commit is contained in:
Mikhael Bogdanov
2014-02-18 12:13:41 +04:00
parent 406855467c
commit 769b07fbc8
22 changed files with 1467 additions and 58 deletions
@@ -42,7 +42,6 @@ public abstract class ClassBodyCodegen extends MemberCodegen {
protected final OwnerKind kind;
protected final ClassDescriptor descriptor;
protected final ClassBuilder v;
protected final ClassContext context;
private MethodVisitor clInitMethod;
@@ -55,10 +54,9 @@ public abstract class ClassBodyCodegen extends MemberCodegen {
@NotNull GenerationState state,
@Nullable MemberCodegen parentCodegen
) {
super(state, parentCodegen);
super(state, parentCodegen, context, v);
descriptor = state.getBindingContext().get(BindingContext.CLASS, aClass);
myClass = aClass;
this.context = context;
this.kind = context.getContextKind();
this.v = v;
}
@@ -28,12 +28,15 @@ import org.jetbrains.asm4.commons.Method;
import org.jetbrains.jet.codegen.binding.CalculatedClosure;
import org.jetbrains.jet.codegen.context.CodegenContext;
import org.jetbrains.jet.codegen.context.LocalLookup;
import org.jetbrains.jet.codegen.context.MethodContext;
import org.jetbrains.jet.codegen.signature.BothSignatureWriter;
import org.jetbrains.jet.codegen.signature.JvmMethodSignature;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.codegen.state.JetTypeMapper;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.JetDeclarationWithBody;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
import org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils;
import org.jetbrains.jet.lang.resolve.name.Name;
@@ -294,7 +297,7 @@ public class ClosureCodegen extends ParentCodegenAwareImpl {
return signature;
}
private static FunctionDescriptor getInvokeFunction(FunctionDescriptor funDescriptor) {
public static FunctionDescriptor getInvokeFunction(FunctionDescriptor funDescriptor) {
int paramCount = funDescriptor.getValueParameters().size();
KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
ClassDescriptor funClass = funDescriptor.getReceiverParameter() == null
@@ -32,6 +32,8 @@ import org.jetbrains.asm4.MethodVisitor;
import org.jetbrains.asm4.Type;
import org.jetbrains.asm4.commons.InstructionAdapter;
import org.jetbrains.asm4.commons.Method;
import org.jetbrains.jet.codegen.asm.InlineCodegen;
import org.jetbrains.jet.codegen.asm.Inliner;
import org.jetbrains.jet.codegen.binding.CalculatedClosure;
import org.jetbrains.jet.codegen.binding.CodegenBinding;
import org.jetbrains.jet.codegen.binding.MutableClosure;
@@ -1353,27 +1355,38 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
}
protected void pushClosureOnStack(CalculatedClosure closure, boolean ignoreThisAndReceiver) {
pushClosureOnStack(closure, ignoreThisAndReceiver, Inliner.NOT_INLINE);
}
public void pushClosureOnStack(CalculatedClosure closure, boolean ignoreThisAndReceiver, Inliner inliner) {
if (closure != null) {
if (!ignoreThisAndReceiver) {
ClassDescriptor captureThis = closure.getCaptureThis();
if (captureThis != null) {
generateThisOrOuter(captureThis, false).put(OBJECT_TYPE, v);
StackValue thisOrOuter = generateThisOrOuter(captureThis, false);
thisOrOuter.put(OBJECT_TYPE, v);
inliner.putInLocal(OBJECT_TYPE, thisOrOuter);
}
JetType captureReceiver = closure.getCaptureReceiverType();
if (captureReceiver != null) {
v.load(context.isStatic() ? 0 : 1, typeMapper.mapType(captureReceiver));
Type asmType = typeMapper.mapType(captureReceiver);
StackValue.Local capturedReceiver = StackValue.local(context.isStatic() ? 0 : 1, asmType);
capturedReceiver.put(asmType, v);
inliner.putInLocal(asmType, capturedReceiver);
}
}
for (Map.Entry<DeclarationDescriptor, EnclosedValueDescriptor> entry : closure.getCaptureVariables().entrySet()) {
//if (entry.getKey() instanceof VariableDescriptor && !(entry.getKey() instanceof PropertyDescriptor)) {
Type sharedVarType = typeMapper.getSharedVarType(entry.getKey());
if (sharedVarType == null) {
sharedVarType = typeMapper.mapType((VariableDescriptor) entry.getKey());
}
entry.getValue().getOuterValue(this).put(sharedVarType, v);
//}
StackValue capturedVar = entry.getValue().getOuterValue(this);
if (inliner.shouldPutValue(sharedVarType, capturedVar, context)) {
capturedVar.put(sharedVarType, v);
}
inliner.putInLocal(sharedVarType, capturedVar);
}
}
}
@@ -1971,7 +1984,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
@NotNull
public StackValue invokeFunction(
Call call,
@NotNull Call call,
StackValue receiver,
ResolvedCall<? extends CallableDescriptor> resolvedCall
) {
@@ -2095,20 +2108,31 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
@NotNull ResolvedCall<? extends CallableDescriptor> resolvedCall,
@NotNull StackValue receiver
) {
boolean disable = false;
CallableDescriptor descriptor = resolvedCall.getResultingDescriptor();
boolean isInline = descriptor instanceof SimpleFunctionDescriptor && ((SimpleFunctionDescriptor) descriptor).isInline();
Inliner inliner = !isInline ? Inliner.NOT_INLINE
: new InlineCodegen(this, true, state, disable, (SimpleFunctionDescriptor) unwrapFakeOverride((CallableMemberDescriptor)descriptor.getOriginal()));
if (resolvedCall instanceof VariableAsFunctionResolvedCall) {
resolvedCall = ((VariableAsFunctionResolvedCall) resolvedCall).getFunctionCall();
}
if (!(resolvedCall.getResultingDescriptor() instanceof ConstructorDescriptor)) { // otherwise already
if (!(descriptor instanceof ConstructorDescriptor)) { // otherwise already
receiver = StackValue.receiver(resolvedCall, receiver, this, callableMethod);
receiver.put(receiver.type, v);
}
pushArgumentsAndInvoke(resolvedCall, callableMethod);
assert inliner == Inliner.NOT_INLINE || !hasDefaults(resolvedCall) && !tailRecursionCodegen.isTailRecursion(resolvedCall) :
"Method with defaults or tail recursive couldn't be inlined " + descriptor;
pushArgumentsAndInvoke(resolvedCall, callableMethod, inliner);
}
private void pushArgumentsAndInvoke(@NotNull ResolvedCall<?> resolvedCall, @NotNull CallableMethod callable) {
int mask = pushMethodArguments(resolvedCall, callable.getValueParameterTypes());
private void pushArgumentsAndInvoke(@NotNull ResolvedCall<?> resolvedCall, @NotNull CallableMethod callable, @NotNull Inliner inliner) {
inliner.putHiddenParams();
int mask = pushMethodArguments(resolvedCall, callable.getValueParameterTypes(), inliner);
if (tailRecursionCodegen.isTailRecursion(resolvedCall)) {
tailRecursionCodegen.generateTailRecursion(resolvedCall);
@@ -2116,11 +2140,17 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
}
if (mask == 0) {
callable.invokeWithNotNullAssertion(v, state, resolvedCall);
if (inliner != Inliner.NOT_INLINE) {
inliner.inlineCall(callable, getParentCodegen().getBuilder().getVisitor());
} else {
callable.invokeWithNotNullAssertion(v, state, resolvedCall);
}
}
else {
callable.invokeDefaultWithNotNullAssertion(v, state, resolvedCall, mask);
}
inliner.leaveTemps();
}
private void genThisAndReceiverFromResolvedCall(
@@ -2283,10 +2313,18 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
}
private int pushMethodArguments(@NotNull ResolvedCall resolvedCall, List<Type> valueParameterTypes) {
return pushMethodArguments(resolvedCall, valueParameterTypes, false);
return pushMethodArguments(resolvedCall, valueParameterTypes, null);
}
private int pushMethodArguments(@NotNull ResolvedCall resolvedCall, List<Type> valueParameterTypes, boolean skipLast) {
return pushMethodArguments(resolvedCall, valueParameterTypes, skipLast, null);
}
private int pushMethodArguments(@NotNull ResolvedCall resolvedCall, List<Type> valueParameterTypes, Inliner inliner) {
return pushMethodArguments(resolvedCall, valueParameterTypes, false, inliner);
}
private int pushMethodArguments(@NotNull ResolvedCall resolvedCall, List<Type> valueParameterTypes, boolean skipLast, Inliner inliner) {
@SuppressWarnings("unchecked")
List<ResolvedValueArgument> valueArguments = resolvedCall.getValueArgumentsByIndex();
CallableDescriptor fd = resolvedCall.getResultingDescriptor();
@@ -2296,15 +2334,18 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
throw new IllegalStateException();
}
inliner = inliner != null ? inliner : Inliner.NOT_INLINE;
int mask = 0;
for (Iterator<ValueParameterDescriptor> iterator = valueParameters.iterator(); iterator.hasNext(); ) {
ValueParameterDescriptor valueParameter = iterator.next();
if (skipLast && !iterator.hasNext()) {
continue;
}
boolean putInLocal = true;
StackValue valueIfPresent = null;
ResolvedValueArgument resolvedValueArgument = valueArguments.get(valueParameter.getIndex());
Type parameterType = valueParameterTypes.get(valueParameter.getIndex());
if (resolvedValueArgument instanceof ExpressionValueArgument) {
@@ -2313,9 +2354,17 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
JetExpression argumentExpression = valueArgument.getArgumentExpression();
assert argumentExpression != null : valueArgument.asElement().getText();
gen(argumentExpression, parameterType);
}
else if (resolvedValueArgument instanceof DefaultValueArgument) {
if (inliner.isInliningClosure(argumentExpression)) {
inliner.rememberClosure((JetFunctionLiteralExpression) argumentExpression, parameterType);
putInLocal = false;
} else {
StackValue value = gen(argumentExpression);
if (inliner.shouldPutValue(parameterType, value, context)) {
value.put(parameterType, v);
}
valueIfPresent = value;
}
} else if (resolvedValueArgument instanceof DefaultValueArgument) {
pushDefaultValueOnStack(parameterType, v);
mask |= (1 << valueParameter.getIndex());
}
@@ -2326,10 +2375,29 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
else {
throw new UnsupportedOperationException();
}
if (putInLocal) {
inliner.putInLocal(parameterType, valueIfPresent);
}
}
return mask;
}
private boolean hasDefaults(@NotNull ResolvedCall resolvedCall) {
@SuppressWarnings("unchecked")
List<ResolvedValueArgument> valueArguments = resolvedCall.getValueArgumentsByIndex();
CallableDescriptor fd = resolvedCall.getResultingDescriptor();
for (ValueParameterDescriptor valueParameter : fd.getValueParameters()) {
StackValue valueIfPresent = null;
ResolvedValueArgument resolvedValueArgument = valueArguments.get(valueParameter.getIndex());
if (resolvedValueArgument instanceof DefaultValueArgument) {
return true;
}
}
return false;
}
public void genVarargs(ValueParameterDescriptor valueParameterDescriptor, VarargValueArgument valueArgument) {
JetType outType = valueParameterDescriptor.getType();
@@ -2576,7 +2644,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
@NotNull
private ReceiverValue computeAndSaveReceiver(
@NotNull JvmMethodSignature signature,
@NotNull JvmMethodSignature signature,
@NotNull ExpressionCodegen codegen,
@Nullable ReceiverParameterDescriptor receiver
) {
@@ -2951,7 +3019,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
receiver.put(receiver.type, v);
}
pushArgumentsAndInvoke(resolvedCall, callable);
pushArgumentsAndInvoke(resolvedCall, callable, Inliner.NOT_INLINE);
if (keepReturnValue) {
value.store(callable.getReturnType(), v);
@@ -3823,4 +3891,26 @@ The "returned" value of try expression with no finally is either the last expres
}
throw new IllegalStateException("Script codegen should be present in codegen tree");
}
public InstructionAdapter getInstructionAdapter() {
return v;
}
public JetTypeMapper getTypeMapper() {
return typeMapper;
}
public MethodVisitor getMethodVisitor() {
return methodVisitor;
}
@NotNull
public FrameMap getFrameMap() {
return myFrameMap;
}
@NotNull
public MethodContext getContext() {
return context;
}
}
@@ -70,6 +70,10 @@ public class FrameMap {
return new Mark(myMaxIndex);
}
public int getCurrentSize() {
return myMaxIndex;
}
public class Mark {
private final int myIndex;
@@ -130,7 +130,7 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
mv,
jvmSignature,
functionDescriptor,
getThisTypeForFunction(functionDescriptor, methodContext),
getThisTypeForFunction(functionDescriptor, methodContext, state.getTypeMapper()),
new Label(),
new Label(),
methodContext.getContextKind()
@@ -138,7 +138,7 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
return;
}
generateMethodBody(mv, functionDescriptor, methodContext, jvmSignature, strategy);
generateMethodBody(mv, functionDescriptor, methodContext, jvmSignature, strategy, getParentCodegen());
endVisit(mv, null, origin);
@@ -237,7 +237,7 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
}
@Nullable
private Type getThisTypeForFunction(@NotNull FunctionDescriptor functionDescriptor, @NotNull MethodContext context) {
private static Type getThisTypeForFunction(@NotNull FunctionDescriptor functionDescriptor, @NotNull MethodContext context, @NotNull JetTypeMapper typeMapper) {
ReceiverParameterDescriptor expectedThisObject = functionDescriptor.getExpectedThisObject();
if (functionDescriptor instanceof ConstructorDescriptor) {
return typeMapper.mapType(functionDescriptor);
@@ -253,18 +253,22 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
}
}
private void generateMethodBody(
public static void generateMethodBody(
@NotNull MethodVisitor mv,
@NotNull FunctionDescriptor functionDescriptor,
@NotNull MethodContext context,
@NotNull JvmMethodSignature signature,
@NotNull FunctionGenerationStrategy strategy
@NotNull FunctionGenerationStrategy strategy,
@NotNull MemberCodegen parentCodegen
) {
mv.visitCode();
Label methodBegin = new Label();
mv.visitLabel(methodBegin);
GenerationState state = parentCodegen.getState();
JetTypeMapper typeMapper = state.getTypeMapper();
if (context.getParentContext() instanceof PackageFacadeContext) {
generateStaticDelegateMethodBody(mv, signature.getAsmMethod(), (PackageFacadeContext) context.getParentContext());
}
@@ -283,15 +287,16 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
genNotNullAssertionsForParameters(new InstructionAdapter(mv), state, functionDescriptor, frameMap);
}
strategy.generateBody(mv, signature, context, getParentCodegen());
strategy.generateBody(mv, signature, context, parentCodegen);
}
Label methodEnd = new Label();
mv.visitLabel(methodEnd);
Type thisType = getThisTypeForFunction(functionDescriptor, context);
generateLocalVariableTable(mv, signature, functionDescriptor, thisType, methodBegin, methodEnd, context.getContextKind());
if (strategy.generateLocalVarTable()) {
Type thisType = getThisTypeForFunction(functionDescriptor, context, typeMapper);
generateLocalVariableTable(mv, signature, functionDescriptor, thisType, methodBegin, methodEnd, context.getContextKind());
}
}
private static void generateLocalVariableTable(
@@ -50,6 +50,10 @@ public abstract class FunctionGenerationStrategy {
return frameMap;
}
public boolean generateLocalVarTable() {
return true;
}
public static class FunctionDefault extends CodegenBased<CallableDescriptor> {
private final JetDeclarationWithBody declaration;
@@ -24,16 +24,34 @@ import org.jetbrains.jet.codegen.context.CodegenContext;
import org.jetbrains.jet.codegen.context.FieldOwnerContext;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.name.SpecialNames;
import org.jetbrains.jet.lang.types.ErrorUtils;
import java.util.HashMap;
import java.util.Map;
public class MemberCodegen extends ParentCodegenAwareImpl {
public MemberCodegen(@NotNull GenerationState state, @Nullable MemberCodegen parentCodegen) {
protected final FieldOwnerContext context;
private final ClassBuilder builder;
@NotNull
private Map<FunctionDescriptor, ClosureCodegen> inlinedClosures = new HashMap<FunctionDescriptor, ClosureCodegen>();
public MemberCodegen(
@NotNull GenerationState state,
@Nullable MemberCodegen parentCodegen,
FieldOwnerContext context,
ClassBuilder builder
) {
super(state, parentCodegen);
this.context = context;
this.builder = builder;
}
public void genFunctionOrProperty(
@@ -75,35 +93,45 @@ public class MemberCodegen extends ParentCodegenAwareImpl {
}
}
public void genClassOrObject(CodegenContext parentContext, JetClassOrObject aClass) {
public static void genClassOrObject(
@NotNull CodegenContext parentContext,
@NotNull JetClassOrObject aClass,
@NotNull GenerationState state,
@Nullable MemberCodegen parentCodegen
) {
ClassDescriptor descriptor = state.getBindingContext().get(BindingContext.CLASS, aClass);
if (descriptor == null || ErrorUtils.isError(descriptor)) {
badDescriptor(descriptor);
badDescriptor(descriptor, state.getClassBuilderMode());
return;
}
if (descriptor.getName().equals(SpecialNames.NO_NAME_PROVIDED)) {
badDescriptor(descriptor);
badDescriptor(descriptor, state.getClassBuilderMode());
}
ClassBuilder classBuilder = state.getFactory().forClassImplementation(descriptor, aClass.getContainingFile());
ClassContext classContext = parentContext.intoClass(descriptor, OwnerKind.IMPLEMENTATION, state);
new ImplementationBodyCodegen(aClass, classContext, classBuilder, state, this).generate();
new ImplementationBodyCodegen(aClass, classContext, classBuilder, state, parentCodegen).generate();
classBuilder.done();
if (aClass instanceof JetClass && ((JetClass) aClass).isTrait()) {
ClassBuilder traitBuilder = state.getFactory().forTraitImplementation(descriptor, state, aClass.getContainingFile());
new TraitImplBodyCodegen(aClass, parentContext.intoClass(descriptor, OwnerKind.TRAIT_IMPL, state), traitBuilder, state, this)
new TraitImplBodyCodegen(aClass, parentContext.intoClass(descriptor, OwnerKind.TRAIT_IMPL, state), traitBuilder, state, parentCodegen)
.generate();
traitBuilder.done();
}
}
private void badDescriptor(ClassDescriptor descriptor) {
if (state.getClassBuilderMode() != ClassBuilderMode.LIGHT_CLASSES) {
private static void badDescriptor(ClassDescriptor descriptor, ClassBuilderMode mode) {
if (mode != ClassBuilderMode.LIGHT_CLASSES) {
throw new IllegalStateException(
"Generating bad descriptor in ClassBuilderMode = " + state.getClassBuilderMode() + ": " + descriptor);
"Generating bad descriptor in ClassBuilderMode = " + mode + ": " + descriptor);
}
}
@NotNull
public ClassBuilder getBuilder() {
return builder;
}
}
@@ -31,6 +31,7 @@ import org.jetbrains.asm4.Type;
import org.jetbrains.jet.codegen.context.CodegenContext;
import org.jetbrains.jet.codegen.context.FieldOwnerContext;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.codegen.state.GenerationStateAware;
import org.jetbrains.jet.descriptors.serialization.BitEncoding;
import org.jetbrains.jet.descriptors.serialization.DescriptorSerializer;
import org.jetbrains.jet.descriptors.serialization.PackageData;
@@ -53,7 +54,8 @@ import static org.jetbrains.jet.codegen.AsmUtil.asmTypeByFqNameWithoutInnerClass
import static org.jetbrains.jet.descriptors.serialization.NameSerializationUtil.createNameResolver;
import static org.jetbrains.jet.lang.resolve.java.PackageClassUtils.getPackageClassFqName;
public class PackageCodegen extends MemberCodegen {
public class PackageCodegen extends GenerationStateAware {
@NotNull
private final ClassBuilderOnDemand v;
@NotNull
@@ -69,7 +71,7 @@ public class PackageCodegen extends MemberCodegen {
@NotNull GenerationState state,
@NotNull Collection<JetFile> packageFiles
) {
super(state, null);
super(state);
checkAllFilesHaveSamePackage(packageFiles);
this.v = v;
@@ -192,10 +194,10 @@ public class PackageCodegen extends MemberCodegen {
new PackagePartCodegen(builder, file, packagePartType, packagePartContext, state).generate();
FieldOwnerContext packageFacade = CodegenContext.STATIC.intoPackageFacade(packagePartType, getPackageFragment(file));
MemberCodegen memberCodegen = new MemberCodegen(state, null, packageFacade, null);
for (JetDeclaration declaration : file.getDeclarations()) {
if (declaration instanceof JetNamedFunction || declaration instanceof JetProperty) {
genFunctionOrProperty(packageFacade, (JetTypeParameterListOwner) declaration, v.getClassBuilder());
memberCodegen.genFunctionOrProperty(packageFacade, (JetTypeParameterListOwner) declaration, v.getClassBuilder());
}
}
@@ -211,7 +213,7 @@ public class PackageCodegen extends MemberCodegen {
public void generateClassOrObject(@NotNull JetClassOrObject classOrObject) {
CodegenContext context = CodegenContext.STATIC.intoPackagePart(getPackageFragment((JetFile) classOrObject.getContainingFile()));
genClassOrObject(context, classOrObject);
MemberCodegen.genClassOrObject(context, classOrObject, state, null);
}
/**
@@ -59,7 +59,7 @@ public class PackagePartCodegen extends MemberCodegen {
@NotNull FieldOwnerContext context,
@NotNull GenerationState state
) {
super(state, null);
super(state, null, context, v);
this.v = v;
this.jetFile = jetFile;
this.packagePartName = packagePartName;
@@ -53,8 +53,12 @@ public class ScriptCodegen extends MemberCodegen {
ClassDescriptor classDescriptorForScript = state.getBindingContext().get(CLASS_FOR_SCRIPT, scriptDescriptor);
assert classDescriptorForScript != null;
Type className = state.getBindingContext().get(ASM_TYPE, classDescriptorForScript);
assert className != null;
ClassBuilder builder = state.getFactory().newVisitor(className, declaration.getContainingFile());
ScriptContext scriptContext = parentContext.intoScript(scriptDescriptor, classDescriptorForScript);
return new ScriptCodegen(declaration, state, scriptContext, state.getEarlierScriptsForReplInterpreter());
return new ScriptCodegen(declaration, state, scriptContext, state.getEarlierScriptsForReplInterpreter(), builder);
}
@NotNull
@@ -70,9 +74,10 @@ public class ScriptCodegen extends MemberCodegen {
@NotNull JetScript scriptDeclaration,
@NotNull GenerationState state,
@NotNull ScriptContext context,
@Nullable List<ScriptDescriptor> earlierScripts
@Nullable List<ScriptDescriptor> earlierScripts,
@NotNull ClassBuilder builder
) {
super(state, null);
super(state, null, context, builder);
this.scriptDeclaration = scriptDeclaration;
this.context = context;
this.earlierScripts = earlierScripts == null ? Collections.<ScriptDescriptor>emptyList() : earlierScripts;
@@ -84,8 +89,7 @@ public class ScriptCodegen extends MemberCodegen {
Type classType = bindingContext.get(ASM_TYPE, classDescriptorForScript);
assert classType != null;
ClassBuilder classBuilder = state.getFactory().newVisitor(classType, scriptDeclaration.getContainingFile());
ClassBuilder classBuilder = getBuilder();
classBuilder.defineClass(scriptDeclaration,
V1_6,
ACC_PUBLIC,
@@ -387,6 +387,10 @@ public abstract class StackValue {
coerceFrom(topOfStackType, v);
v.store(index, this.type);
}
public int getIndex() {
return index;
}
}
public static class OnStack extends StackValue {
@@ -0,0 +1,141 @@
/*
* 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.asm4.tree.MethodNode;
import org.jetbrains.jet.codegen.AsmUtil;
import org.jetbrains.jet.codegen.binding.CalculatedClosure;
import org.jetbrains.jet.codegen.context.EnclosedValueDescriptor;
import org.jetbrains.jet.codegen.state.JetTypeMapper;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.psi.JetFunctionLiteral;
import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.java.JvmClassName;
import java.util.*;
import static org.jetbrains.jet.codegen.binding.CodegenBinding.CLOSURE;
import static org.jetbrains.jet.codegen.binding.CodegenBinding.anonymousClassForFunction;
import static org.jetbrains.jet.codegen.binding.CodegenBinding.asmTypeForAnonymousClass;
public class ClosureInfo {
public final JetFunctionLiteralExpression expression;
@NotNull private final JetTypeMapper typeMapper;
public final CalculatedClosure closure;
private MethodNode node;
private Collection<EnclosedValueDescriptor> capturedVars;
private final FunctionDescriptor functionDescriptor;
private final ClassDescriptor classDescriptor;
private final Type closureClassType;
private int paramOffset;
ClosureInfo(@NotNull JetFunctionLiteralExpression expression, @NotNull JetTypeMapper typeMapper) {
this.expression = expression;
this.typeMapper = typeMapper;
BindingContext bindingContext = typeMapper.getBindingContext();
functionDescriptor = bindingContext.get(BindingContext.FUNCTION, expression.getFunctionLiteral());
assert functionDescriptor != null : "Function is not resolved to descriptor: " + expression.getText();
classDescriptor = anonymousClassForFunction(bindingContext, functionDescriptor);
closureClassType = asmTypeForAnonymousClass(bindingContext, functionDescriptor);
closure = bindingContext.get(CLOSURE, classDescriptor);
}
public MethodNode getNode() {
return node;
}
public void setNode(MethodNode node) {
this.node = node;
}
public FunctionDescriptor getFunctionDescriptor() {
return functionDescriptor;
}
public JetFunctionLiteral getFunctionLiteral() {
return expression.getFunctionLiteral();
}
public ClassDescriptor getClassDescriptor() {
return classDescriptor;
}
public Type getClosureClassType() {
return closureClassType;
}
public Collection<EnclosedValueDescriptor> getCapturedVars() {
//lazy initialization cause it would be calculated after object creation
if (capturedVars == null) {
capturedVars = new ArrayList<EnclosedValueDescriptor>();
if (closure.getCaptureThis() != null) {
EnclosedValueDescriptor descriptor = new EnclosedValueDescriptor(AsmUtil.CAPTURED_THIS_FIELD, null, null, typeMapper.mapType(closure.getCaptureThis()));
capturedVars.add(descriptor);
}
if (closure.getCaptureReceiverType() != null) {
EnclosedValueDescriptor descriptor = new EnclosedValueDescriptor(AsmUtil.CAPTURED_RECEIVER_FIELD, null, null, typeMapper.mapType(closure.getCaptureReceiverType()));
capturedVars.add(descriptor);
}
if (closure != null) {
capturedVars.addAll(closure.getCaptureVariables().values());
}
}
return capturedVars;
}
public int getParamOffset() {
return paramOffset;
}
public void setParamOffset(int paramOffset) {
this.paramOffset = paramOffset;
}
public List<Type> getParamsWithoutCapturedValOrVar() {
Type[] types = typeMapper.mapSignature(functionDescriptor).getAsmMethod().getArgumentTypes();
return Arrays.asList(types);
}
public int getCapturedVarsSize() {
int size = 0;
for (Iterator<EnclosedValueDescriptor> iterator = getCapturedVars().iterator(); iterator.hasNext(); ) {
EnclosedValueDescriptor next = iterator.next();
size += next.getType().getSize();
}
return size;
}
}
@@ -0,0 +1,33 @@
/*
* 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;
class ClosureUsage {
public final int index;
public final boolean inlinable;
ClosureUsage(int index, boolean isInlinable) {
this.index = index;
inlinable = isInlinable;
}
public boolean isInlinable() {
return inlinable;
}
}
@@ -0,0 +1,598 @@
/*
* 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.Lists;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.*;
import org.jetbrains.asm4.commons.InstructionAdapter;
import org.jetbrains.asm4.commons.Method;
import org.jetbrains.asm4.tree.*;
import org.jetbrains.asm4.tree.analysis.*;
import org.jetbrains.asm4.tree.analysis.Frame;
import org.jetbrains.asm4.util.Textifier;
import org.jetbrains.asm4.util.TraceMethodVisitor;
import org.jetbrains.jet.codegen.*;
import org.jetbrains.jet.codegen.context.CodegenContext;
import org.jetbrains.jet.codegen.context.EnclosedValueDescriptor;
import org.jetbrains.jet.codegen.context.MethodContext;
import org.jetbrains.jet.codegen.signature.JvmMethodParameterKind;
import org.jetbrains.jet.codegen.signature.JvmMethodParameterSignature;
import org.jetbrains.jet.codegen.signature.JvmMethodSignature;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.codegen.state.JetTypeMapper;
import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedSimpleFunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
import org.jetbrains.jet.renderer.DescriptorRenderer;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.*;
import static org.jetbrains.jet.codegen.AsmUtil.*;
public class InlineCodegen implements ParentCodegenAware, Inliner {
public final static String INVOKE = "invoke";
private final ExpressionCodegen codegen;
private final boolean notSeparateInline;
private final GenerationState state;
private final boolean disabled;
private final SimpleFunctionDescriptor functionDescriptor;
private final List<ParameterInfo> tempTypes = new ArrayList<ParameterInfo>();
private final List<ClosureUsage> closures = new ArrayList<ClosureUsage>();
private final Map<Integer, ClosureInfo> expressionMap = new HashMap<Integer, ClosureInfo>();
private final JetTypeMapper typeMapper;
private final BindingContext bindingContext;
private final MethodContext context;
private final FrameMap originalFunctionFrame;
private final int initialFrameSize;
private final JvmMethodSignature jvmSignature;
public InlineCodegen(
@NotNull ExpressionCodegen codegen,
boolean notSeparateInline,
@NotNull GenerationState state,
boolean disabled,
@NotNull SimpleFunctionDescriptor functionDescriptor
) {
this.codegen = codegen;
this.notSeparateInline = notSeparateInline;
this.state = state;
this.disabled = disabled;
this.functionDescriptor = functionDescriptor.getOriginal();
typeMapper = codegen.getTypeMapper();
bindingContext = codegen.getBindingContext();
initialFrameSize = codegen.getFrameMap().getCurrentSize();
context = (MethodContext) getContext(functionDescriptor, state);
originalFunctionFrame = context.prepareFrame(typeMapper);
jvmSignature = typeMapper.mapSignature(functionDescriptor, context.getContextKind());
}
@Override
public void inlineCall(CallableMethod callableMethod, ClassVisitor visitor) {
MethodNode node = null;
try {
node = createMethodNode(callableMethod);
inlineCall(node, true);
}
catch (CompilationException e) {
throw e;
}
catch (Exception e) {
String text = getNodeText(node);
PsiElement element = BindingContextUtils.descriptorToDeclaration(bindingContext, codegen.getContext().getContextDescriptor());
throw new CompilationException("Couldn't inline method call '" +
functionDescriptor.getName() +
"' into \n" + (element != null ? element.getText() : "null psi element " + codegen.getContext().getContextDescriptor()) +
"\ncause: " +
text, e, null);
}
}
@NotNull
private MethodNode createMethodNode(CallableMethod callableMethod)
throws ClassNotFoundException, IOException {
MethodNode node = null;
if (functionDescriptor instanceof DeserializedSimpleFunctionDescriptor) {
VirtualFile file = InlineCodegenUtil.getVirtualFileForCallable((DeserializedSimpleFunctionDescriptor) functionDescriptor, state);
node = InlineCodegenUtil.getMethodNode(file.getInputStream(), functionDescriptor.getName().asString(),
callableMethod.getAsmMethod().getDescriptor());
if (node == null) {
throw new RuntimeException("Couldn't obtain compiled function body for " + descriptorName(functionDescriptor));
}
}
else {
PsiElement element = BindingContextUtils.descriptorToDeclaration(bindingContext, functionDescriptor);
if (element == null) {
throw new RuntimeException("Couldn't find declaration for function " + descriptorName(functionDescriptor));
}
JvmMethodSignature jvmSignature = typeMapper.mapSignature(functionDescriptor, context.getContextKind());
Method asmMethod = jvmSignature.getAsmMethod();
node = new MethodNode(Opcodes.ASM4,
getMethodAsmFlags(functionDescriptor, context.getContextKind()),
asmMethod.getName(),
asmMethod.getDescriptor(),
jvmSignature.getGenericsSignature(),
null);
FunctionCodegen.generateMethodBody(node, functionDescriptor, context.getParentContext().intoFunction(functionDescriptor),
jvmSignature,
new FunctionGenerationStrategy.FunctionDefault(state,
functionDescriptor,
(JetDeclarationWithBody) element),
getParentCodegen());
//TODO
node.visitMaxs(20, 20);
node.visitEnd();
}
return node;
}
private void inlineCall(MethodNode node, boolean inlineClosures) {
if (inlineClosures) {
removeClosureAssertions(node);
try {
markPlacesForInlineAndRemoveInlinable(node);
}
catch (AnalyzerException e) {
throw new RuntimeException(e);
}
}
int valueParamSize = originalFunctionFrame.getCurrentSize();
int originalSize = codegen.getFrameMap().getCurrentSize();
generateClosuresBodies();
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);
}
private void removeClosureAssertions(MethodNode node) {
AbstractInsnNode cur = node.instructions.getFirst();
while (cur != null && cur.getNext() != null) {
AbstractInsnNode next = cur.getNext();
if (next.getType() == AbstractInsnNode.METHOD_INSN) {
MethodInsnNode methodInsnNode = (MethodInsnNode) next;
if (methodInsnNode.name.equals("checkParameterIsNotNull") && methodInsnNode.owner.equals("jet/runtime/Intrinsics")) {
AbstractInsnNode prev = cur.getPrevious();
assert prev.getType() == AbstractInsnNode.VAR_INSN && prev.getOpcode() == Opcodes.ALOAD;
int varIndex = ((VarInsnNode) prev).var;
ClosureInfo closure = expressionMap.get(varIndex);
if (closure != null) {
node.instructions.remove(prev);
node.instructions.remove(cur);
cur = next.getNext();
node.instructions.remove(next);
next = cur;
}
}
}
cur = next;
}
}
private void markPlacesForInlineAndRemoveInlinable(@NotNull MethodNode node) throws AnalyzerException {
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;
//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();
assert insnNode.getType() == AbstractInsnNode.VAR_INSN && insnNode.getOpcode() == Opcodes.ALOAD;
int varIndex = ((VarInsnNode) insnNode).var;
ClosureInfo closureInfo = expressionMap.get(varIndex);
if (closureInfo != null) { //TODO: maybe add separate map for noninlinable closures
closures.add(new ClosureUsage(varIndex, true));
node.instructions.remove(insnNode);
} else {
closures.add(new ClosureUsage(varIndex, false));
}
}
}
cur = cur.getNext();
index++;
}
}
private void doInline(
int access,
String desc,
MethodVisitor mv,
MethodNode methodNode,
int frameSize,
final boolean inlineClosures,
@NotNull VarRemapper remapper
) {
Label end = new Label();
final LinkedList<ClosureUsage> infos = new LinkedList<ClosureUsage>(closures);
methodNode.instructions.resetLabels();
MethodVisitor methodVisitor = codegen.getMethodVisitor();
InliningAdapter inliner = new InliningAdapter(methodVisitor, Opcodes.ASM4, desc, end, frameSize, remapper) {
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
if (inlineClosures && /*INLINE_RUNTIME.equals(owner) &&*/ isInvokeOnInlinable(owner, name)) { //TODO add method
assert !infos.isEmpty();
ClosureUsage closureUsage = infos.remove();
ClosureInfo info = expressionMap.get(closureUsage.index);
if (!closureUsage.isInlinable()) {
//noninlinable closure
super.visitMethodInsn(opcode, owner, name, desc);
return;
}
//TODO replace with codegen
int valueParamShift = getNextLocalIndex();
remapper.setNestedRemap(true);
putStackValuesIntoLocals(info.getParamsWithoutCapturedValOrVar(), valueParamShift, this, desc);
Label closureEnd = new Label();
InliningAdapter closureInliner = new InliningAdapter(mv, Opcodes.ASM4, desc, closureEnd, getNextLocalIndex(),
new VarRemapper.ClosureRemapper(info, valueParamShift, tempTypes));
info.getNode().instructions.resetLabels();
info.getNode().accept(closureInliner); //TODO
remapper.setNestedRemap(false);
mv.visitLabel(closureEnd);
Method bridge = typeMapper.mapSignature(ClosureCodegen.getInvokeFunction(info.getFunctionDescriptor())).getAsmMethod();
Method delegate = typeMapper.mapSignature(info.getFunctionDescriptor()).getAsmMethod();
StackValue.onStack(delegate.getReturnType()).put(bridge.getReturnType(), closureInliner);
}
else {
super.visitMethodInsn(opcode, owner, name, desc);
}
}
};
methodNode.accept(inliner);
methodVisitor.visitLabel(end);
}
private boolean isInvokeOnInlinable(String owner, String name) {
return INVOKE.equals(name) && /*TODO: check type*/owner.contains("Function");
}
private void generateClosuresBodies() {
for (ClosureInfo info : expressionMap.values()) {
info.setNode(generateClosureBody(info));
}
}
private MethodNode generateClosureBody(ClosureInfo info) {
JetFunctionLiteral declaration = info.getFunctionLiteral();
FunctionDescriptor descriptor = info.getFunctionDescriptor();
MethodContext parentContext = codegen.getContext();
MethodContext context = parentContext.intoClosure(descriptor, codegen, typeMapper).intoFunction(descriptor);
JvmMethodSignature jvmMethodSignature = typeMapper.mapSignature(descriptor);
Method asmMethod = jvmMethodSignature.getAsmMethod();
MethodNode methodNode = new MethodNode(Opcodes.ASM4, getMethodAsmFlags(descriptor, context.getContextKind()), asmMethod.getName(), asmMethod.getDescriptor(), jvmMethodSignature.getGenericsSignature(), null);
FunctionCodegen.generateMethodBody(methodNode, descriptor, context, jvmMethodSignature, new FunctionGenerationStrategy.FunctionDefault(state, descriptor, declaration) {
@Override
public boolean generateLocalVarTable() {
return false;
}
}, codegen.getParentCodegen());
return transformClosure(methodNode, info);
}
@NotNull
private static MethodNode transformClosure(@NotNull MethodNode node, @NotNull ClosureInfo info) {
//remove all this and shift all variables to captured ones size
final int localVarSHift = info.getCapturedVarsSize();
MethodNode transformedNode = new MethodNode(node.access, node.name, node.desc, node.signature, null) {
private boolean remappingCaptured = false;
@Override
public void visitVarInsn(int opcode, int var) {
super.visitVarInsn(opcode, var + (remappingCaptured ? 0 : localVarSHift - 1/*remove this*/));
}
};
node.accept(transformedNode);
//remove all field access to local var
AbstractInsnNode cur = transformedNode.instructions.getFirst();
while (cur != null) {
if (cur.getType() == AbstractInsnNode.FIELD_INSN) {
FieldInsnNode fieldInsnNode = (FieldInsnNode) cur;
//TODO check closure
String owner = fieldInsnNode.owner;
if (info.getClosureClassType().getInternalName().equals(fieldInsnNode.owner)) {
int opcode = fieldInsnNode.getOpcode();
String name = fieldInsnNode.name;
String desc = fieldInsnNode.desc;
Collection<EnclosedValueDescriptor> vars = info.getCapturedVars();
int index = 0;//skip this
boolean found = false;
for (EnclosedValueDescriptor valueDescriptor : vars) {
Type type = valueDescriptor.getType();
if (valueDescriptor.getFieldName().equals(name)) {
opcode = opcode == Opcodes.GETFIELD ? type.getOpcode(Opcodes.ILOAD) : type.getOpcode(Opcodes.ISTORE);
found = true;
break;
}
index += type.getSize();
}
if (!found) {
throw new UnsupportedOperationException("Coudn't find field " +
owner +
"." +
name +
" (" +
desc +
") in captured vars of " +
info.getFunctionLiteral().getText());
}
VarInsnNode varInsNode = new VarInsnNode(opcode, index);
AbstractInsnNode prev = cur.getPrevious();
while (prev.getType() == AbstractInsnNode.LABEL || prev.getType() == AbstractInsnNode.LINE) {
prev = prev.getPrevious();
}
assert prev.getType() == AbstractInsnNode.VAR_INSN;
VarInsnNode loadThis = (VarInsnNode) prev;
assert /*loadThis.var == info.getCapturedVarsSize() - 1 && */loadThis.getOpcode() == Opcodes.ALOAD;
transformedNode.instructions.remove(prev);
transformedNode.instructions.insertBefore(cur, varInsNode);
transformedNode.instructions.remove(cur);
cur = varInsNode;
}
}
cur = cur.getNext();
}
return transformedNode;
}
@Override
public void putInLocal(Type type, StackValue stackValue) {
if (!disabled && notSeparateInline && Type.VOID_TYPE != type) {
//TODO remap only inlinable closure => otherwise we could get a lot of problem
boolean couldBeRemapped = !shouldPutValue(type, stackValue, codegen.getContext());
int remappedIndex = couldBeRemapped ? ((StackValue.Local) stackValue).getIndex() : -1;
ParameterInfo info = new ParameterInfo(type, false, remappedIndex, couldBeRemapped ? -1 : codegen.getFrameMap().enterTemp(type));
doWithParameter(info);
}
}
@Override
public boolean shouldPutValue(Type type, StackValue stackValue, MethodContext context) {
if (stackValue != null && context.isInlineFunction() && stackValue instanceof StackValue.Local) {
if (isInvokeOnInlinable(type.getClassName(), "invoke")) {
//TODO remap only inlinable closure => otherwise we could get a lot of problem
return false; //TODO check annotations
}
}
return true;
}
private void doWithParameter(ParameterInfo info) {
recordParamInfo(info, true);
putParameterOnStack(info);
}
private int recordParamInfo(ParameterInfo info, boolean addToFrame) {
Type type = info.type;
tempTypes.add(info);
if (info.getType().getSize() == 2) {
tempTypes.add(ParameterInfo.STUB);
}
if (addToFrame) {
return originalFunctionFrame.enterTemp(type);
}
return -1;
}
private void putParameterOnStack(ParameterInfo info) {
if (!info.isSkippedOrRemapped()) {
int index = info.index;
Type type = info.type;
StackValue.local(index, type).store(type, codegen.getInstructionAdapter());
}
}
@Override
public void putHiddenParams() {
List<JvmMethodParameterSignature> types = jvmSignature.getValueParameters();
if (!isStaticMethod(functionDescriptor, context)) {
Type type = AsmTypeConstants.OBJECT_TYPE;
ParameterInfo info = new ParameterInfo(type, false, -1, codegen.getFrameMap().enterTemp(type));
recordParamInfo(info, false);
}
for (JvmMethodParameterSignature param : types) {
if (param.getKind() == JvmMethodParameterKind.VALUE) {
break;
}
Type type = param.getAsmType();
ParameterInfo info = new ParameterInfo(type, false, -1, codegen.getFrameMap().enterTemp(type));
recordParamInfo(info, false);
}
for (ListIterator<ParameterInfo> iterator = tempTypes.listIterator(tempTypes.size()); iterator.hasPrevious(); ) {
ParameterInfo param = iterator.previous();
putParameterOnStack(param);
}
}
@Override
public void leaveTemps() {
FrameMap frameMap = codegen.getFrameMap();
for (ListIterator<ParameterInfo> iterator = tempTypes.listIterator(tempTypes.size()); iterator.hasPrevious(); ) {
ParameterInfo param = iterator.previous();
if (!param.isSkippedOrRemapped()) {
frameMap.leaveTemp(param.type);
}
}
}
public boolean isDisabled() {
return disabled;
}
private static void putStackValuesIntoLocals(List<Type> directOrder, int shift, InstructionAdapter mv, String descriptor) {
Type [] actualParams = Type.getArgumentTypes(descriptor); //last param is closure itself
assert actualParams.length == directOrder.size() : "Number of expected and actual params should be equals!";
int size = 0;
for (Type next : directOrder) {
size += next.getSize();
}
shift += size;
int index = directOrder.size();
for (Type next : Lists.reverse(directOrder)) {
shift -= next.getSize();
Type typeOnStack = actualParams[--index];
if (!typeOnStack.equals(next)) {
StackValue.onStack(typeOnStack).put(next, mv);
}
mv.visitVarInsn(next.getOpcode(Opcodes.ISTORE), shift);
}
}
@Override
public boolean isInliningClosure(JetExpression expression) {
return !disabled && expression instanceof JetFunctionLiteralExpression;
}
@Override
public void rememberClosure(JetFunctionLiteralExpression expression, Type type) {
ParameterInfo closureInfo = new ParameterInfo(type, true, -1, -1);
int index = recordParamInfo(closureInfo, true);
ClosureInfo info = new ClosureInfo(expression, typeMapper);
expressionMap.put(index, info);
}
private void putClosureParametersOnStack() {
//TODO: SORT
for (ClosureInfo next : expressionMap.values()) {
if (next.closure != null) {
int size = tempTypes.size();
next.setParamOffset(size);
codegen.pushClosureOnStack(next.closure, false, this);
}
}
}
@Nullable
@Override
public MemberCodegen getParentCodegen() {
return codegen.getParentCodegen();
}
public static CodegenContext getContext(DeclarationDescriptor descriptor, GenerationState state) {
if (descriptor instanceof NamespaceDescriptor) {
return new NamespaceContext((NamespaceDescriptor) descriptor, null);
}
CodegenContext parent = getContext(descriptor.getContainingDeclaration(), state);
if (descriptor instanceof ClassDescriptor) {
return parent.intoClass((ClassDescriptor) descriptor, OwnerKind.IMPLEMENTATION, state);
}
else if (descriptor instanceof FunctionDescriptor) {
return parent.intoFunction((FunctionDescriptor) descriptor);
}
throw new IllegalStateException("Coudn't build context for " + descriptorName(descriptor));
}
private static boolean isStaticMethod(FunctionDescriptor functionDescriptor, MethodContext context) {
return (getMethodAsmFlags(functionDescriptor, context.getContextKind()) & Opcodes.ACC_STATIC) == Opcodes.ACC_STATIC;
}
@NotNull
private static String getNodeText(@Nullable MethodNode node) {
if (node == null) {
return "Not generated";
}
Textifier p = new Textifier();
node.accept(new TraceMethodVisitor(p));
StringWriter sw = new StringWriter();
p.print(new PrintWriter(sw));
sw.flush();
return node.name + ": \n " + sw.getBuffer().toString();
}
private static String descriptorName(DeclarationDescriptor descriptor) {
return DescriptorRenderer.SHORT_NAMES_IN_TYPES.render(descriptor);
}
}
@@ -0,0 +1,138 @@
/*
* 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.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.ClassReader;
import org.jetbrains.asm4.ClassVisitor;
import org.jetbrains.asm4.MethodVisitor;
import org.jetbrains.asm4.Opcodes;
import org.jetbrains.asm4.tree.MethodNode;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.descriptors.serialization.JavaProtoBuf;
import org.jetbrains.jet.descriptors.serialization.ProtoBuf;
import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedSimpleFunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
import org.jetbrains.jet.lang.resolve.kotlin.VirtualFileFinder;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.Name;
import java.io.IOException;
import java.io.InputStream;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getFQName;
public class InlineCodegenUtil {
private final static int API = Opcodes.ASM4;
@Nullable
public static MethodNode getMethodNode(
InputStream classData,
final String methodName,
final String methodDescriptor
) throws ClassNotFoundException, IOException {
ClassReader cr = new ClassReader(classData);
final MethodNode[] methodNode = new MethodNode[1];
cr.accept(new ClassVisitor(API) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (methodName.equals(name) && methodDescriptor.equals(desc)) {
return methodNode[0] = new MethodNode(access, name, desc, signature, exceptions);
}
return null;
}
}, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return methodNode[0];
}
@NotNull
public static VirtualFile getVirtualFileForCallable(DeserializedSimpleFunctionDescriptor deserializedDescriptor, GenerationState state) {
VirtualFile file = null;
DeclarationDescriptor parentDeclatation = deserializedDescriptor.getContainingDeclaration();
if (parentDeclatation instanceof NamespaceDescriptor) {
ProtoBuf.Callable proto = deserializedDescriptor.getFunctionProto();
if (proto.hasExtension(JavaProtoBuf.implClassName)) {
Name name = deserializedDescriptor.getNameResolver().getName(proto.getExtension(JavaProtoBuf.implClassName));
FqName namespaceFqName =
PackageClassUtils.getPackageClassFqName(((NamespaceDescriptor) parentDeclatation).getFqName()).parent().child(
name);
file = findVirtualFile(state.getProject(), namespaceFqName);
} else {
assert false : "Function in namespace should have implClassName property in proto: " + deserializedDescriptor;
}
} else {
file = findVirtualFileContainingDescriptor(state.getProject(), deserializedDescriptor);
}
if (file == null) {
throw new RuntimeException("Couldn't find declaration file for " + deserializedDescriptor.getName());
}
return file;
}
@Nullable
private static VirtualFile findVirtualFile(@NotNull Project project, @NotNull FqName containerFqName) {
VirtualFileFinder fileFinder = ServiceManager.getService(project, VirtualFileFinder.class);
VirtualFile virtualFile = fileFinder.find(containerFqName);
if (virtualFile == null) {
return null;
}
return virtualFile;
}
//TODO: navigate to inner classes
@Nullable
private static FqName getContainerFqName(@NotNull DeclarationDescriptor referencedDescriptor) {
ClassOrNamespaceDescriptor
containerDescriptor = DescriptorUtils.getParentOfType(referencedDescriptor, ClassOrNamespaceDescriptor.class, false);
if (containerDescriptor instanceof NamespaceDescriptor) {
return PackageClassUtils.getPackageClassFqName(getFQName(containerDescriptor).toSafe());
}
if (containerDescriptor instanceof ClassDescriptor) {
ClassKind classKind = ((ClassDescriptor) containerDescriptor).getKind();
if (classKind == ClassKind.CLASS_OBJECT || classKind == ClassKind.ENUM_ENTRY) {
return getContainerFqName(containerDescriptor.getContainingDeclaration());
}
return getFQName(containerDescriptor).toSafe();
}
return null;
}
@Nullable
private static VirtualFile findVirtualFileContainingDescriptor(
@NotNull Project project,
@NotNull DeclarationDescriptor referencedDescriptor
) {
FqName containerFqName = getContainerFqName(referencedDescriptor);
if (containerFqName == null) {
return null;
}
return findVirtualFile(project, containerFqName);
}
}
@@ -0,0 +1,83 @@
/*
* 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.ClassVisitor;
import org.jetbrains.asm4.Type;
import org.jetbrains.jet.codegen.CallableMethod;
import org.jetbrains.jet.codegen.StackValue;
import org.jetbrains.jet.codegen.context.MethodContext;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression;
public interface Inliner {
Inliner NOT_INLINE = new Inliner() {
@Override
public void inlineCall(
CallableMethod callableMethod, ClassVisitor visitor
) {
throw new UnsupportedOperationException();
}
@Override
public void putInLocal(Type type, StackValue stackValue) {
}
@Override
public void leaveTemps() {
}
@Override
public boolean isInliningClosure(JetExpression expression) {
return false;
}
@Override
public void rememberClosure(JetFunctionLiteralExpression expression, Type type) {
throw new UnsupportedOperationException();
}
@Override
public void putHiddenParams() {
}
@Override
public boolean shouldPutValue(
Type type, StackValue stackValue, MethodContext context
) {
return true;
}
};
void inlineCall(CallableMethod callableMethod, ClassVisitor visitor);
void putInLocal(Type type, StackValue stackValue);
boolean shouldPutValue(Type type, StackValue stackValue, MethodContext context);
void putHiddenParams();
void leaveTemps();
boolean isInliningClosure(JetExpression expression);
void rememberClosure(JetFunctionLiteralExpression expression, Type type);
}
@@ -0,0 +1,94 @@
/*
* 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.*;
import org.jetbrains.asm4.commons.InstructionAdapter;
import java.util.ArrayList;
import java.util.List;
//http://asm.ow2.org/current/asm-transformations.pdf
public class InliningAdapter extends InstructionAdapter {
private final Label end;
protected final VarRemapper remapper;
private int nextLocalIndex = 0;
public InliningAdapter(MethodVisitor mv, int api, String desc, Label end, int localsStart, VarRemapper remapper) {
super(api, mv);
this.end = end;
this.remapper = remapper;
nextLocalIndex = localsStart;
}
public void visitInsn(int opcode) {
if (opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) {
super.visitJumpInsn(Opcodes.GOTO, end);
}
else {
super.visitInsn(opcode);
}
}
@Override
public void visitIincInsn(int var, int increment) {
super.visitIincInsn(remapper.remap(var), increment);
}
@Override
public void visitVarInsn(int opcode, int var) {
int newVar = remapper.remap(var);
super.visitVarInsn(opcode, newVar);
int size = newVar + (opcode == Opcodes.DSTORE || opcode == Opcodes.LSTORE ? 2 : 1);
if (size > nextLocalIndex) {
nextLocalIndex = size;
}
}
public int getNextLocalIndex() {
return nextLocalIndex;
}
@Override
public void visitLocalVariable(
String name, String desc, String signature, Label start, Label end, int index
) {
//super.visitLocalVariable(name, desc, signature, start, end, index);
}
@Override
public AnnotationVisitor visitAnnotationDefault() {
return null;
}
@Override
public void visitMaxs(int maxStack, int maxLocals) {
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return null;
}
@Override
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
return null;
}
}
@@ -0,0 +1,56 @@
/*
* 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 org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
class ParameterInfo {
public static final ParameterInfo STUB = new ParameterInfo(AsmTypeConstants.OBJECT_TYPE, true, -1, -1);
public final int index;
public final Type type;
public final boolean isSkipped;
public final int remapIndex;
ParameterInfo(Type type, boolean skipped, int remapIndex, int index) {
this.type = type;
this.isSkipped = skipped;
this.remapIndex = remapIndex;
this.index = index;
}
public boolean isSkippedOrRemapped() {
return isSkipped || remapIndex != -1;
}
public int getInlinedIndex() {
return remapIndex != -1 ? remapIndex : index;
}
public boolean isSkipped() {
return isSkipped;
}
public Type getType() {
return type;
}
}
@@ -0,0 +1,114 @@
/*
* 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 java.util.List;
public abstract class VarRemapper {
public static class ShiftRemapper extends VarRemapper {
private final int shift;
public ShiftRemapper(int shift) {
this.shift = shift;
}
@Override
public int doRemap(int index) {
return index + shift;
}
}
public static class ClosureRemapper extends ShiftRemapper {
private final ClosureInfo info;
private final List<ParameterInfo> originalParams;
private final int capturedSize;
public ClosureRemapper(ClosureInfo info, int valueParametersShift, List<ParameterInfo> originalParams) {
super(valueParametersShift);
this.info = info;
this.originalParams = originalParams;
capturedSize = info.getCapturedVarsSize();
}
@Override
public int doRemap(int index) {
if (index < capturedSize) {
return originalParams.get(info.getParamOffset() + index).getInlinedIndex();
}
return super.doRemap(index - capturedSize);
}
}
public static class ParamRemapper extends VarRemapper {
private final int paramShift;
private final int paramSize;
private final int additionalParamsSize;
private final List<ParameterInfo> params;
private final int actualParams;
public ParamRemapper(int paramShift, int paramSize, int additionalParamsSize, List<ParameterInfo> params) {
this.paramShift = paramShift;
this.paramSize = paramSize;
this.additionalParamsSize = additionalParamsSize;
this.params = params;
int paramCount = 0;
for (int i = 0; i < params.size(); i++) {
ParameterInfo info = params.get(i);
if (!info.isSkippedOrRemapped()) {
paramCount += info.getType().getSize();
}
}
actualParams = paramCount;
}
@Override
public int doRemap(int index) {
if (index < paramSize) {
ParameterInfo info = params.get(index);
if (info.isSkipped) {
throw new RuntimeException("Trying to access skipped parameter: " + info.type + " at " +index);
}
return info.getInlinedIndex();
} else {
return paramShift + actualParams + index; //captured params not used directly in this inlined method, they used in closure
}
}
}
protected boolean nestedRemmapper;
public int remap(int index) {
if (nestedRemmapper) {
return index;
}
return doRemap(index);
}
abstract public int doRemap(int index);
public void setNestedRemap(boolean nestedRemap) {
nestedRemmapper = nestedRemap;
}
}
@@ -23,11 +23,13 @@ import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
public final class EnclosedValueDescriptor {
private final String fieldName;
private final DeclarationDescriptor descriptor;
private final StackValue innerValue;
private final Type type;
public EnclosedValueDescriptor(DeclarationDescriptor descriptor, StackValue innerValue, Type type) {
public EnclosedValueDescriptor(String fieldName, DeclarationDescriptor descriptor, StackValue innerValue, Type type) {
this.fieldName = fieldName;
this.descriptor = descriptor;
this.innerValue = innerValue;
this.type = type;
@@ -55,4 +57,8 @@ public final class EnclosedValueDescriptor {
throw new IllegalStateException();
}
public String getFieldName() {
return fieldName;
}
}
@@ -61,7 +61,7 @@ public interface LocalLookup {
: StackValue.field(type, classType, fieldName, false);
closure.recordField(fieldName, type);
closure.captureVariable(new EnclosedValueDescriptor(d, innerValue, type));
closure.captureVariable(new EnclosedValueDescriptor(fieldName, d, innerValue, type));
return innerValue;
}
@@ -92,7 +92,7 @@ public interface LocalLookup {
StackValue innerValue = StackValue.field(localType, classType, fieldName, false);
closure.recordField(fieldName, localType);
closure.captureVariable(new EnclosedValueDescriptor(d, innerValue, localType));
closure.captureVariable(new EnclosedValueDescriptor(fieldName, d, innerValue, localType));
return innerValue;
}
@@ -23,10 +23,7 @@ import org.jetbrains.jet.codegen.OwnerKind;
import org.jetbrains.jet.codegen.StackValue;
import org.jetbrains.jet.codegen.binding.MutableClosure;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.PropertyAccessorDescriptor;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
public class MethodContext extends CodegenContext<CallableMemberDescriptor> {
@@ -81,4 +78,11 @@ public class MethodContext extends CodegenContext<CallableMemberDescriptor> {
return "Method: " + getContextDescriptor();
}
public boolean isInlineFunction() {
DeclarationDescriptor descriptor = getContextDescriptor();
if (descriptor instanceof SimpleFunctionDescriptor) {
return ((SimpleFunctionDescriptor) descriptor).isInline();
}
return false;
}
}