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);
|
return genClosure(((JetFunctionLiteralExpression) argumentExpression).getFunctionLiteral(), samInterface);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
JvmClassName className = new SamWrapperCodegen(state, samInterface).genWrapper(expression, argumentExpression);
|
JvmClassName className =
|
||||||
|
state.getSamWrapperClasses().getSamWrapperClass(samInterface, (JetFile) expression.getContainingFile());
|
||||||
|
|
||||||
v.anew(className.getAsmType());
|
v.anew(className.getAsmType());
|
||||||
v.dup();
|
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.MethodVisitor;
|
||||||
import org.jetbrains.asm4.Type;
|
import org.jetbrains.asm4.Type;
|
||||||
import org.jetbrains.asm4.commons.InstructionAdapter;
|
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.context.CodegenContext;
|
||||||
import org.jetbrains.jet.codegen.state.GenerationState;
|
import org.jetbrains.jet.codegen.state.GenerationState;
|
||||||
import org.jetbrains.jet.codegen.state.GenerationStateAware;
|
import org.jetbrains.jet.codegen.state.GenerationStateAware;
|
||||||
import org.jetbrains.jet.codegen.state.JetTypeMapperMode;
|
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.FunctionDescriptor;
|
||||||
|
import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
|
||||||
import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor;
|
import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor;
|
||||||
import org.jetbrains.jet.lang.psi.JetCallExpression;
|
import org.jetbrains.jet.lang.psi.JetFile;
|
||||||
import org.jetbrains.jet.lang.psi.JetExpression;
|
|
||||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
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.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.descriptor.ClassDescriptorFromJvmBytecode;
|
||||||
import org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils;
|
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.resolve.name.Name;
|
||||||
import org.jetbrains.jet.lang.types.JetType;
|
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.asm4.Opcodes.*;
|
||||||
import static org.jetbrains.jet.codegen.AsmUtil.NO_FLAG_PACKAGE_PRIVATE;
|
import static org.jetbrains.jet.codegen.AsmUtil.NO_FLAG_PACKAGE_PRIVATE;
|
||||||
@@ -55,33 +53,18 @@ public class SamWrapperCodegen extends GenerationStateAware {
|
|||||||
this.samInterface = samInterface;
|
this.samInterface = samInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JvmClassName genWrapper(JetCallExpression callExpression, JetExpression argumentExpression) {
|
public JvmClassName genWrapper(@NotNull JetFile file) {
|
||||||
// Example: we generate SAM constructor call Comparator(f), where f: (Int, Int) -> Int
|
|
||||||
|
|
||||||
// Name for generated class, in form of whatever$1
|
// Name for generated class, in form of whatever$1
|
||||||
JvmClassName name = bindingContext.get(CodegenBinding.FQN_FOR_SAM_CONSTRUCTOR, callExpression);
|
JvmClassName name = JvmClassName.byInternalName(getWrapperName(file));
|
||||||
assert name != null : "internal class name not found for " + callExpression.getText();
|
|
||||||
|
|
||||||
// e.g. (Int, Int) -> Int
|
// e.g. (T, T) -> Int
|
||||||
JetType functionType = bindingContext.get(BindingContext.EXPRESSION_TYPE, argumentExpression);
|
JetType functionType = samInterface.getFunctionTypeForSamInterface();
|
||||||
assert functionType != null && KotlinBuiltIns.getInstance().isFunctionType(functionType) :
|
assert functionType != null : samInterface.toString();
|
||||||
"not a function type of " + argumentExpression.getText() + ": " + functionType;
|
// e.g. compare(T, T)
|
||||||
|
SimpleFunctionDescriptor interfaceFunction = SingleAbstractMethodUtils.getAbstractMethodOfSamInterface(samInterface);
|
||||||
|
|
||||||
// SAM constructor call
|
ClassBuilder cv = state.getFactory().newVisitor(name.getInternalName(), file);
|
||||||
ResolvedCall<? extends CallableDescriptor> resolvedCall =
|
cv.defineClass(file,
|
||||||
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,
|
|
||||||
V1_6,
|
V1_6,
|
||||||
ACC_FINAL,
|
ACC_FINAL,
|
||||||
name.getInternalName(),
|
name.getInternalName(),
|
||||||
@@ -89,7 +72,7 @@ public class SamWrapperCodegen extends GenerationStateAware {
|
|||||||
OBJECT_TYPE.getInternalName(),
|
OBJECT_TYPE.getInternalName(),
|
||||||
new String[]{JvmClassName.byClassDescriptor(samInterface).getInternalName()}
|
new String[]{JvmClassName.byClassDescriptor(samInterface).getInternalName()}
|
||||||
);
|
);
|
||||||
cv.visitSource(callExpression.getContainingFile().getName(), null);
|
cv.visitSource(file.getName(), null);
|
||||||
|
|
||||||
// e.g. ASM type for Function2
|
// e.g. ASM type for Function2
|
||||||
Type functionAsmType = state.getTypeMapper().mapType(functionType, JetTypeMapperMode.VALUE);
|
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);
|
StackValue functionField = StackValue.field(functionType, JvmClassName.byType(ownerType), FUNCTION_FIELD_NAME, false);
|
||||||
codegen.genDelegate(interfaceFunction, invokeFunction, functionField);
|
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
|
@NotNull
|
||||||
private final IntrinsicMethods intrinsics;
|
private final IntrinsicMethods intrinsics;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final SamWrapperClasses samWrapperClasses = new SamWrapperClasses(this);
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private final BindingTrace bindingTrace;
|
private final BindingTrace bindingTrace;
|
||||||
|
|
||||||
@@ -154,6 +157,11 @@ public class GenerationState {
|
|||||||
return intrinsics;
|
return intrinsics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public SamWrapperClasses getSamWrapperClasses() {
|
||||||
|
return samWrapperClasses;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isGenerateNotNullAssertions() {
|
public boolean isGenerateNotNullAssertions() {
|
||||||
return generateNotNullAssertions;
|
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");
|
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")
|
@TestMetadata("syntheticVsReal.kt")
|
||||||
public void testSyntheticVsReal() throws Exception {
|
public void testSyntheticVsReal() throws Exception {
|
||||||
doTest("compiler/testData/codegen/box/sam/syntheticVsReal.kt");
|
doTest("compiler/testData/codegen/box/sam/syntheticVsReal.kt");
|
||||||
|
|||||||
+5
@@ -71,6 +71,11 @@ public class BlackBoxMultiFileCodegenTestGenerated extends AbstractBlackBoxCodeg
|
|||||||
doTestMultiFile("compiler/testData/codegen/boxMultiFile/nestedPackages");
|
doTestMultiFile("compiler/testData/codegen/boxMultiFile/nestedPackages");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("samWrappersDifferentFiles")
|
||||||
|
public void testSamWrappersDifferentFiles() throws Exception {
|
||||||
|
doTestMultiFile("compiler/testData/codegen/boxMultiFile/samWrappersDifferentFiles");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("sameFileName")
|
@TestMetadata("sameFileName")
|
||||||
public void testSameFileName() throws Exception {
|
public void testSameFileName() throws Exception {
|
||||||
doTestMultiFile("compiler/testData/codegen/boxMultiFile/sameFileName");
|
doTestMultiFile("compiler/testData/codegen/boxMultiFile/sameFileName");
|
||||||
|
|||||||
Reference in New Issue
Block a user