Inline context refactoring & proper generation of outer class info
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
+16
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user