Generating SAM wrapper class per source file.
This commit is contained in:
@@ -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");
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user