Generating SAM wrapper class per source file.

This commit is contained in:
Evgeny Gerashchenko
2013-06-13 19:36:26 +04:00
parent 0fbf203ff7
commit 7ef4c8cfa8
10 changed files with 118 additions and 33 deletions
@@ -1931,7 +1931,8 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
return genClosure(((JetFunctionLiteralExpression) argumentExpression).getFunctionLiteral(), samInterface);
}
else {
JvmClassName className = new SamWrapperCodegen(state, samInterface).genWrapper(expression, argumentExpression);
JvmClassName className =
state.getSamWrapperClasses().getSamWrapperClass(samInterface, (JetFile) expression.getContainingFile());
v.anew(className.getAsmType());
v.dup();
@@ -0,0 +1,52 @@
/*
* 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;
import com.google.common.collect.Maps;
import com.intellij.openapi.util.Factory;
import com.intellij.openapi.util.Pair;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.resolve.java.JvmClassName;
import org.jetbrains.jet.lang.resolve.java.descriptor.ClassDescriptorFromJvmBytecode;
import java.util.Map;
public class SamWrapperClasses {
private final GenerationState state;
private final Map<Pair<ClassDescriptorFromJvmBytecode, JetFile>, JvmClassName> samInterfaceToWrapperClass = Maps.newHashMap();
public SamWrapperClasses(GenerationState state) {
this.state = state;
}
@NotNull
public JvmClassName getSamWrapperClass(@NotNull final ClassDescriptorFromJvmBytecode samInterface, @NotNull final JetFile file) {
return ContainerUtil.getOrCreate(samInterfaceToWrapperClass, Pair.create(samInterface, file),
new Factory<JvmClassName>() {
@Override
public JvmClassName create() {
return new SamWrapperCodegen(state, samInterface).genWrapper(file);
}
});
}
}
@@ -20,25 +20,23 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.asm4.MethodVisitor;
import org.jetbrains.asm4.Type;
import org.jetbrains.asm4.commons.InstructionAdapter;
import org.jetbrains.jet.codegen.binding.CodegenBinding;
import org.jetbrains.jet.codegen.context.CodegenContext;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.codegen.state.GenerationStateAware;
import org.jetbrains.jet.codegen.state.JetTypeMapperMode;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor;
import org.jetbrains.jet.lang.psi.JetCallExpression;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.java.JvmClassName;
import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
import org.jetbrains.jet.lang.resolve.java.descriptor.ClassDescriptorFromJvmBytecode;
import org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import static org.jetbrains.asm4.Opcodes.*;
import static org.jetbrains.jet.codegen.AsmUtil.NO_FLAG_PACKAGE_PRIVATE;
@@ -55,33 +53,18 @@ public class SamWrapperCodegen extends GenerationStateAware {
this.samInterface = samInterface;
}
public JvmClassName genWrapper(JetCallExpression callExpression, JetExpression argumentExpression) {
// Example: we generate SAM constructor call Comparator(f), where f: (Int, Int) -> Int
public JvmClassName genWrapper(@NotNull JetFile file) {
// Name for generated class, in form of whatever$1
JvmClassName name = bindingContext.get(CodegenBinding.FQN_FOR_SAM_CONSTRUCTOR, callExpression);
assert name != null : "internal class name not found for " + callExpression.getText();
JvmClassName name = JvmClassName.byInternalName(getWrapperName(file));
// e.g. (Int, Int) -> Int
JetType functionType = bindingContext.get(BindingContext.EXPRESSION_TYPE, argumentExpression);
assert functionType != null && KotlinBuiltIns.getInstance().isFunctionType(functionType) :
"not a function type of " + argumentExpression.getText() + ": " + functionType;
// e.g. (T, T) -> Int
JetType functionType = samInterface.getFunctionTypeForSamInterface();
assert functionType != null : samInterface.toString();
// e.g. compare(T, T)
SimpleFunctionDescriptor interfaceFunction = SingleAbstractMethodUtils.getAbstractMethodOfSamInterface(samInterface);
// SAM constructor call
ResolvedCall<? extends CallableDescriptor> resolvedCall =
bindingContext.get(BindingContext.RESOLVED_CALL, callExpression.getCalleeExpression());
assert resolvedCall != null : "couldn't find resolved call for " + callExpression.getText();
// e.g. Comparator<Int, Int>
JetType resultType = resolvedCall.getResultingDescriptor().getReturnType();
assert resultType != null && resultType.getConstructor() == samInterface.getTypeConstructor() :
"unexpected result type: " + resultType;
// e.g. compare(Int, Int)
SimpleFunctionDescriptor interfaceFunction = SingleAbstractMethodUtils.getAbstractMethodOfSamType(resultType);
ClassBuilder cv = state.getFactory().newVisitor(name.getInternalName(), callExpression.getContainingFile());
cv.defineClass(callExpression,
ClassBuilder cv = state.getFactory().newVisitor(name.getInternalName(), file);
cv.defineClass(file,
V1_6,
ACC_FINAL,
name.getInternalName(),
@@ -89,7 +72,7 @@ public class SamWrapperCodegen extends GenerationStateAware {
OBJECT_TYPE.getInternalName(),
new String[]{JvmClassName.byClassDescriptor(samInterface).getInternalName()}
);
cv.visitSource(callExpression.getContainingFile().getName(), null);
cv.visitSource(file.getName(), null);
// e.g. ASM type for Function2
Type functionAsmType = state.getTypeMapper().mapType(functionType, JetTypeMapperMode.VALUE);
@@ -149,4 +132,14 @@ public class SamWrapperCodegen extends GenerationStateAware {
StackValue functionField = StackValue.field(functionType, JvmClassName.byType(ownerType), FUNCTION_FIELD_NAME, false);
codegen.genDelegate(interfaceFunction, invokeFunction, functionField);
}
private String getWrapperName(@NotNull JetFile containingFile) {
NamespaceDescriptor namespace = state.getBindingContext().get(BindingContext.FILE_TO_NAMESPACE, containingFile);
assert namespace != null : "couldn't find namespace for file: " + containingFile.getVirtualFile();
FqName fqName = DescriptorUtils.getFQName(namespace).toSafe();
String packageInternalName = JvmClassName.byFqNameWithoutInnerClasses(
PackageClassUtils.getPackageClassFqName(fqName)).getInternalName();
return packageInternalName + "$sam$" + samInterface.getName().asString() +
"$" + Integer.toHexString(CodegenUtil.getPathHashCode(containingFile)); // TODO add class FQ name hash
}
}
@@ -56,6 +56,9 @@ public class GenerationState {
@NotNull
private final IntrinsicMethods intrinsics;
@NotNull
private final SamWrapperClasses samWrapperClasses = new SamWrapperClasses(this);
@NotNull
private final BindingTrace bindingTrace;
@@ -154,6 +157,11 @@ public class GenerationState {
return intrinsics;
}
@NotNull
public SamWrapperClasses getSamWrapperClasses() {
return samWrapperClasses;
}
public boolean isGenerateNotNullAssertions() {
return generateNotNullAssertions;
}
@@ -0,0 +1,7 @@
fun box(): String {
val f = { }
val class1 = Runnable(f).getClass()
val class2 = Runnable(f).getClass()
return if (class1 == class2) "OK" else "$class1 $class2"
}
@@ -0,0 +1,4 @@
fun getWrapped1(): Runnable {
val f = { }
return Runnable(f)
}
@@ -0,0 +1,4 @@
fun getWrapped2(): Runnable {
val f = { }
return Runnable(f)
}
@@ -0,0 +1,6 @@
fun box(): String {
val class1 = getWrapped1().getClass()
val class2 = getWrapped2().getClass()
return if (class1 != class2) "OK" else "Same class: $class1"
}
@@ -3756,6 +3756,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest("compiler/testData/codegen/box/sam/runnableAccessingClosure2.kt");
}
@TestMetadata("sameWrapperClass.kt")
public void testSameWrapperClass() throws Exception {
doTest("compiler/testData/codegen/box/sam/sameWrapperClass.kt");
}
@TestMetadata("syntheticVsReal.kt")
public void testSyntheticVsReal() throws Exception {
doTest("compiler/testData/codegen/box/sam/syntheticVsReal.kt");
@@ -71,6 +71,11 @@ public class BlackBoxMultiFileCodegenTestGenerated extends AbstractBlackBoxCodeg
doTestMultiFile("compiler/testData/codegen/boxMultiFile/nestedPackages");
}
@TestMetadata("samWrappersDifferentFiles")
public void testSamWrappersDifferentFiles() throws Exception {
doTestMultiFile("compiler/testData/codegen/boxMultiFile/samWrappersDifferentFiles");
}
@TestMetadata("sameFileName")
public void testSameFileName() throws Exception {
doTestMultiFile("compiler/testData/codegen/boxMultiFile/sameFileName");