Inline context refactoring & proper generation of outer class info

This commit is contained in:
Mikhael Bogdanov
2014-04-25 16:46:17 +04:00
parent b025d0b76c
commit dce8d5d3e1
10 changed files with 232 additions and 59 deletions
@@ -22,11 +22,6 @@ import com.intellij.openapi.util.Pair;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.org.objectweb.asm.AnnotationVisitor;
import org.jetbrains.org.objectweb.asm.Label;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import org.jetbrains.jet.codegen.binding.CalculatedClosure;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.codegen.state.JetTypeMapper;
@@ -39,19 +34,24 @@ import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.lexer.JetTokens;
import org.jetbrains.org.objectweb.asm.AnnotationVisitor;
import org.jetbrains.org.objectweb.asm.Label;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
import static org.jetbrains.jet.codegen.JvmCodegenUtil.*;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.*;
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.JAVA_STRING_TYPE;
import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.ABI_VERSION_FIELD_NAME;
import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.KotlinSyntheticClass;
import static org.jetbrains.jet.lang.resolve.java.mapping.PrimitiveTypesUtil.asmTypeForPrimitive;
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
public class AsmUtil {
private static final Set<ClassDescriptor> PRIMITIVE_NUMBER_CLASSES = Sets.newHashSet(
@@ -214,6 +214,24 @@ public class AsmUtil {
return NO_FLAG_PACKAGE_PRIVATE;
}
public static int getVisibilityAccessFlagForAnonymous(@NotNull ClassDescriptor descriptor) {
if (isDeclarationInsideInlineFunction(descriptor)) {
return ACC_PUBLIC;
}
return NO_FLAG_PACKAGE_PRIVATE;
}
public static boolean isDeclarationInsideInlineFunction(@NotNull ClassDescriptor descriptor) {
//NB: constructor context couldn't be inline
DeclarationDescriptor parentDeclaration = descriptor.getContainingDeclaration();
if (parentDeclaration instanceof SimpleFunctionDescriptor &&
((SimpleFunctionDescriptor) parentDeclaration).getInlineStrategy().isInline()) {
return true;
}
return false;
}
public static int getDeprecatedAccessFlag(@NotNull MemberDescriptor descriptor) {
if (descriptor instanceof PropertyAccessorDescriptor) {
return KotlinBuiltIns.getInstance().isDeprecated(descriptor)
@@ -24,7 +24,6 @@ import org.jetbrains.annotations.Nullable;
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;
@@ -92,12 +91,7 @@ public class ClosureCodegen extends ParentCodegenAwareImpl {
this.asmType = asmTypeForAnonymousClass(bindingContext, funDescriptor);
//TODO: We should write header on lambda class and filter all completions by header
if (parentContext instanceof MethodContext) {
visibilityFlag = ((MethodContext) parentContext).isInlineFunction() ? ACC_PUBLIC : NO_FLAG_PACKAGE_PRIVATE;
} else {
visibilityFlag = NO_FLAG_PACKAGE_PRIVATE;
}
visibilityFlag = AsmUtil.getVisibilityAccessFlagForAnonymous(classDescriptor);
}
public void gen() {
@@ -248,15 +248,15 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
// Do not emit enclosing method in "light-classes mode" since currently we generate local light classes as if they're top level
if (isLocalOrAnonymousClass && state.getClassBuilderMode() != ClassBuilderMode.LIGHT_CLASSES) {
String outerClassName = getOuterClassName(descriptor, typeMapper);
FunctionDescriptor function = DescriptorUtils.getParentOfType(descriptor, FunctionDescriptor.class);
FunctionDescriptor function = AsmUtil.isDeclarationInsideInlineFunction(descriptor)
? null
: DescriptorUtils.getParentOfType(descriptor, FunctionDescriptor.class);
if (function != null) {
Method method = typeMapper.mapSignature(function).getAsmMethod();
v.visitOuterClass(outerClassName, method.getName(), method.getDescriptor());
}
else {
assert isObjectLiteral
: "Function descriptor could be null only for object literal in package: " + descriptor.getName();
v.visitOuterClass(outerClassName, null, null);
}
}
@@ -346,4 +346,8 @@ public abstract class MemberCodegen<T extends JetElement/* TODO: & JetDeclaratio
iv.putstatic(thisAsmType.getInternalName(), JvmAbi.PROPERTY_METADATA_ARRAY_NAME, "[" + PROPERTY_METADATA_TYPE);
}
public String getClassName() {
return v.getThisName();
}
}
@@ -100,6 +100,21 @@ public class AnonymousObjectTransformer {
final List<MethodNode> methodsToTransform = new ArrayList<MethodNode>();
reader.accept(new ClassVisitor(InlineCodegenUtil.API, classBuilder.getVisitor()) {
@Override
public void visitOuterClass(@NotNull String owner, String name, String desc) {
InliningContext parent = inliningContext.getParent();
assert parent != null : "Context for transformer should have parent one: " + inliningContext;
//we don't write owner info for lamdbas and SAMs just only for objects
if (parent.isRoot() || parent.isInliningLambdaRootContext()) {
//TODO: think about writing method info - there is some problem with new constructor desc calculation
super.visitOuterClass(inliningContext.getParent().getClassNameToInline(), null, null);
return;
}
super.visitOuterClass(owner, name, desc);
}
@Override
public MethodVisitor visitMethod(
int access, @NotNull String name, @NotNull String desc, String signature, String[] exceptions
@@ -267,7 +282,7 @@ public class AnonymousObjectTransformer {
@NotNull
private ClassBuilder createClassBuilder() {
return new RemappingClassBuilder(state.getFactory().forLambdaInlining(newLambdaType, inliningContext.call.getCallElement().getContainingFile()),
return new RemappingClassBuilder(state.getFactory().forLambdaInlining(newLambdaType, inliningContext.getRoot().call.getCallElement().getContainingFile()),
new TypeRemapper(inliningContext.typeMapping));
}
@@ -196,10 +196,13 @@ public class InlineCodegen implements CallGenerator {
Parameters parameters = new Parameters(realParams, Parameters.shiftAndAddStubs(captured, realParams.size()));
InliningContext info =
new InliningContext(expressionMap, null, null, null, state,
codegen.getInlineNameGenerator().subGenerator(functionDescriptor.getName().asString()),
codegen.getContext(), call, Collections.<String, String>emptyMap(), false, false);
InliningContext info = new RootInliningContext(expressionMap,
state,
codegen.getInlineNameGenerator()
.subGenerator(functionDescriptor.getName().asString()),
codegen.getContext(),
call,
codegen.getParentCodegen().getClassName());
MethodInliner inliner = new MethodInliner(node, parameters, info, new FieldRemapper(null, null, parameters), isSameModule, "Method inlining " + call.getCallElement().getText()); //with captured
@@ -16,60 +16,44 @@
package org.jetbrains.jet.codegen.inline;
import org.jetbrains.jet.codegen.context.CodegenContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.lang.psi.Call;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class InliningContext {
@Nullable
private final InliningContext parent;
public final Map<Integer, LambdaInfo> expressionMap;
public final List<InvokeCall> invokeCalls;
public final List<ConstructorInvocation> constructorInvocation;
public final LocalVarRemapper remapper;
public final GenerationState state;
public final NameGenerator nameGenerator;
public final CodegenContext startContext;
public final Call call;
public final Map<String, String> typeMapping;
public final boolean isInliningLambda;
public final boolean classRegeneration;
public InliningContext(
Map<Integer, LambdaInfo> map,
List<InvokeCall> accesses,
List<ConstructorInvocation> invocation,
LocalVarRemapper remapper,
GenerationState state,
NameGenerator nameGenerator,
CodegenContext startContext,
Call call,
Map<String, String> typeMapping,
protected InliningContext(
@Nullable InliningContext parent,
@NotNull Map<Integer, LambdaInfo> map,
@NotNull GenerationState state,
@NotNull NameGenerator nameGenerator,
@NotNull Map<String, String> typeMapping,
boolean isInliningLambda,
boolean classRegeneration
) {
this.parent = parent;
expressionMap = map;
invokeCalls = accesses;
constructorInvocation = invocation;
this.remapper = remapper;
this.state = state;
this.nameGenerator = nameGenerator;
this.startContext = startContext;
this.call = call;
this.typeMapping = typeMapping;
this.isInliningLambda = isInliningLambda;
this.classRegeneration = classRegeneration;
@@ -80,7 +64,7 @@ public class InliningContext {
}
public InliningContext subInlineLambda(LambdaInfo lambdaInfo) {
Map<String, String> map = new HashMap();
Map<String, String> map = new HashMap<String, String>();
map.put(lambdaInfo.getLambdaClassType().getInternalName(), null); //mark lambda inlined
return subInline(nameGenerator.subGenerator("lambda"), map, true);
}
@@ -89,15 +73,58 @@ public class InliningContext {
return subInline(generator, additionalTypeMappings, isInliningLambda);
}
public InliningContext subInline(NameGenerator generator, Map<String, String> additionalTypeMappings, boolean isInliningLambda) {
public InliningContext subInlineWithClassRegeneration(@NotNull NameGenerator generator,
@NotNull Map<String, String> additionalTypeMappings,
@NotNull ConstructorInvocation constructorInvocation) {
Map<String, String> newTypeMappings = new HashMap<String, String>(typeMapping);
newTypeMappings.putAll(additionalTypeMappings);
return new InliningContext(expressionMap, invokeCalls, constructorInvocation, remapper, state, generator, startContext, call,
newTypeMappings, isInliningLambda, classRegeneration);
return new RegenetedClassContext(this, expressionMap, state, generator,
newTypeMappings, isInliningLambda, constructorInvocation);
}
public InliningContext classRegeneration() {
return new InliningContext(expressionMap, invokeCalls, constructorInvocation, remapper, state, nameGenerator, startContext, call,
typeMapping, isInliningLambda, true);
public InliningContext subInline(NameGenerator generator, Map<String, String> additionalTypeMappings, boolean isInliningLambda) {
return subInline(generator, additionalTypeMappings, isInliningLambda, classRegeneration);
}
private InliningContext subInline(
NameGenerator generator,
Map<String, String> additionalTypeMappings,
boolean isInliningLambda,
boolean isRegeneration
) {
Map<String, String> newTypeMappings = new HashMap<String, String>(typeMapping);
newTypeMappings.putAll(additionalTypeMappings);
return new InliningContext(this, expressionMap, state, generator,
newTypeMappings, isInliningLambda, isRegeneration);
}
public boolean isRoot() {
return parent == null;
}
@NotNull
public RootInliningContext getRoot() {
if (isRoot()) {
return (RootInliningContext) this;
}
else {
return parent.getRoot();
}
}
@Nullable
public InliningContext getParent() {
return parent;
}
public boolean isInliningLambdaRootContext() {
//noinspection ConstantConditions
return isInliningLambda && !getParent().isInliningLambda;
}
public String getClassNameToInline() {
assert parent != null : "At least root context should return proper value";
return parent.getClassNameToInline();
}
}
@@ -141,9 +141,15 @@ public class MethodInliner {
//TODO: need poping of type but what to do with local funs???
Type newLambdaType = Type.getObjectType(inliningContext.nameGenerator.genLambdaClassName());
currentTypeMapping.put(invocation.getOwnerInternalName(), newLambdaType.getInternalName());
AnonymousObjectTransformer transformer = new AnonymousObjectTransformer(invocation.getOwnerInternalName(),
inliningContext.subInline(inliningContext.nameGenerator, currentTypeMapping).classRegeneration(),
isSameModule, newLambdaType);
AnonymousObjectTransformer transformer =
new AnonymousObjectTransformer(invocation.getOwnerInternalName(),
inliningContext
.subInlineWithClassRegeneration(
inliningContext.nameGenerator,
currentTypeMapping,
invocation),
isSameModule, newLambdaType
);
InlineResult transformResult = transformer.doTransform(invocation, nodeRemapper);
result.addAllClassesToRemove(transformResult);
@@ -0,0 +1,47 @@
/*
* Copyright 2010-2014 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.inline;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.codegen.state.GenerationState;
import java.util.Map;
public class RegenetedClassContext extends InliningContext {
@NotNull
private final ConstructorInvocation constructorInvocation;
protected RegenetedClassContext(
@Nullable InliningContext parent,
@NotNull Map<Integer, LambdaInfo> map,
@NotNull GenerationState state,
@NotNull NameGenerator nameGenerator,
@NotNull Map<String, String> typeMapping,
boolean isInliningLambda,
@NotNull ConstructorInvocation constructorInvocation
) {
super(parent, map, state, nameGenerator, typeMapping, isInliningLambda, true);
this.constructorInvocation = constructorInvocation;
}
@Override
public String getClassNameToInline() {
return constructorInvocation.getOwnerInternalName();
}
}
@@ -0,0 +1,59 @@
/*
* Copyright 2010-2014 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.inline;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.codegen.context.CodegenContext;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.lang.psi.Call;
import java.util.Collections;
import java.util.Map;
public class RootInliningContext extends InliningContext {
public final CodegenContext startContext;
@NotNull
private final String classNameToInline;
public final Call call;
public RootInliningContext(
@NotNull Map<Integer, LambdaInfo> map,
@NotNull GenerationState state,
@NotNull NameGenerator nameGenerator,
@NotNull CodegenContext startContext,
@NotNull Call call,
@NotNull String classNameToInline
) {
super(null, map, state, nameGenerator, Collections.<String, String>emptyMap(), false, false);
this.call = call;
this.startContext = startContext;
this.classNameToInline = classNameToInline;
}
@Override
public boolean isRoot() {
return true;
}
@NotNull
public String getClassNameToInline() {
return classNameToInline;
}
}