Supported SAM constructor calls without function literal.
This commit is contained in:
@@ -21,6 +21,8 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.asm4.*;
|
||||
|
||||
public abstract class ClassBuilder {
|
||||
private String thisName;
|
||||
|
||||
public static class Concrete extends ClassBuilder {
|
||||
private final ClassVisitor v;
|
||||
|
||||
@@ -78,6 +80,7 @@ public abstract class ClassBuilder {
|
||||
String superName,
|
||||
String[] interfaces
|
||||
) {
|
||||
thisName = name;
|
||||
getVisitor().visit(version, access, name, signature, superName, interfaces);
|
||||
}
|
||||
|
||||
@@ -92,4 +95,8 @@ public abstract class ClassBuilder {
|
||||
public void visitInnerClass(String name, String outerName, String innerName, int access) {
|
||||
getVisitor().visitInnerClass(name, outerName, innerName, access);
|
||||
}
|
||||
|
||||
public String getThisName() {
|
||||
return thisName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1899,7 +1899,17 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
return genClosure(((JetFunctionLiteralExpression) argumentExpression).getFunctionLiteral(), samInterface);
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedOperationException(); // TODO
|
||||
JvmClassName className = new SamWrapperCodegen(state, samInterface).genWrapper(expression, argumentExpression);
|
||||
|
||||
v.anew(className.getAsmType());
|
||||
v.dup();
|
||||
|
||||
JetType functionType = ((SimpleFunctionDescriptor) funDescriptor).getValueParameters().get(0).getType();
|
||||
gen(argumentExpression, typeMapper.mapType(functionType));
|
||||
|
||||
v.invokespecial(className.getInternalName(), "<init>",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, typeMapper.mapType(functionType)));
|
||||
return StackValue.onStack(className.getAsmType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -846,9 +846,7 @@ public class FunctionCodegen extends GenerationStateAware {
|
||||
reg += argTypes[i].getSize();
|
||||
}
|
||||
|
||||
iv.invokevirtual(state.getTypeMapper().mapType(
|
||||
(ClassDescriptor) owner.getContextDescriptor()).getInternalName(),
|
||||
jvmSignature.getName(), jvmSignature.getDescriptor());
|
||||
iv.invokevirtual(v.getThisName(), jvmSignature.getName(), jvmSignature.getDescriptor());
|
||||
|
||||
StackValue.onStack(jvmSignature.getReturnType()).put(overridden.getReturnType(), iv);
|
||||
|
||||
|
||||
@@ -1051,7 +1051,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
}
|
||||
|
||||
if (closure != null) {
|
||||
List<FieldInfo> argsFromClosure = ClosureCodegen.calculateConstructorParameters(typeMapper, bindingContext, state, closure, classAsmType);
|
||||
List<FieldInfo> argsFromClosure = ClosureCodegen.calculateConstructorParameters(typeMapper, bindingContext, closure, classAsmType);
|
||||
int k = 1;
|
||||
for (FieldInfo info : argsFromClosure) {
|
||||
k = AsmUtil.genAssignInstanceFieldFromParam(info, k, iv);
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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 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.*;
|
||||
import org.jetbrains.jet.lang.psi.JetCallExpression;
|
||||
import org.jetbrains.jet.lang.psi.JetExpression;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.jet.lang.resolve.java.JvmClassName;
|
||||
import org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils;
|
||||
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;
|
||||
import static org.jetbrains.jet.codegen.AsmUtil.genStubCode;
|
||||
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
|
||||
|
||||
public class SamWrapperCodegen extends GenerationStateAware {
|
||||
private static final String FUNCTION_FIELD_NAME = "function";
|
||||
|
||||
@NotNull private final ClassDescriptor samInterface;
|
||||
|
||||
public SamWrapperCodegen(@NotNull GenerationState state, @NotNull ClassDescriptor samInterface) {
|
||||
super(state);
|
||||
this.samInterface = samInterface;
|
||||
}
|
||||
|
||||
public JvmClassName genWrapper(JetCallExpression callExpression, JetExpression argumentExpression) {
|
||||
JvmClassName name = bindingContext.get(CodegenBinding.FQN_FOR_SAM_CONSTRUCTOR, callExpression);
|
||||
assert name != null : "internal class name not found for " + callExpression.getText();
|
||||
|
||||
JetType functionType = bindingContext.get(BindingContext.EXPRESSION_TYPE, argumentExpression);
|
||||
assert functionType != null && KotlinBuiltIns.getInstance().isFunctionType(functionType) :
|
||||
"not a function type of " + argumentExpression.getText() + ": " + functionType;
|
||||
|
||||
ResolvedCall<? extends CallableDescriptor> resolvedCall =
|
||||
bindingContext.get(BindingContext.RESOLVED_CALL, callExpression.getCalleeExpression());
|
||||
assert resolvedCall != null : "couldn't find resolved call for " + callExpression.getText();
|
||||
|
||||
JetType resultType = resolvedCall.getResultingDescriptor().getReturnType();
|
||||
assert resultType != null && resultType.getConstructor() == samInterface.getTypeConstructor() :
|
||||
"unexpected result type: " + resultType;
|
||||
|
||||
SimpleFunctionDescriptor interfaceFunction = SingleAbstractMethodUtils.getAbstractMethodOfSamType(resultType);
|
||||
|
||||
ClassBuilder cv = state.getFactory().newVisitor(name.getInternalName(), callExpression.getContainingFile());
|
||||
cv.defineClass(callExpression,
|
||||
V1_6,
|
||||
ACC_FINAL,
|
||||
name.getInternalName(),
|
||||
null,
|
||||
JvmClassName.byType(OBJECT_TYPE).getInternalName(),
|
||||
new String[]{JvmClassName.byClassDescriptor(samInterface).getInternalName()}
|
||||
);
|
||||
cv.visitSource(callExpression.getContainingFile().getName(), null);
|
||||
|
||||
Type functionAsmType = state.getTypeMapper().mapType(functionType, JetTypeMapperMode.VALUE);
|
||||
cv.newField(null,
|
||||
ACC_SYNTHETIC | ACC_PRIVATE | ACC_FINAL,
|
||||
FUNCTION_FIELD_NAME,
|
||||
functionAsmType.getDescriptor(),
|
||||
null,
|
||||
null);
|
||||
|
||||
generateConstructor(name.getAsmType(), functionAsmType, cv);
|
||||
generateMethod(name.getAsmType(), functionAsmType, cv, interfaceFunction, functionType);
|
||||
|
||||
cv.done();
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
private void generateConstructor(Type ownerType, Type functionType, ClassBuilder cv) {
|
||||
MethodVisitor mv = cv.newMethod(null, NO_FLAG_PACKAGE_PRIVATE, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, functionType), null, null);
|
||||
|
||||
if (state.getClassBuilderMode() == ClassBuilderMode.STUBS) {
|
||||
genStubCode(mv);
|
||||
}
|
||||
else if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
|
||||
mv.visitCode();
|
||||
InstructionAdapter iv = new InstructionAdapter(mv);
|
||||
|
||||
// super constructor
|
||||
iv.load(0, OBJECT_TYPE);
|
||||
iv.invokespecial(OBJECT_TYPE.getInternalName(), "<init>", "()V");
|
||||
|
||||
// save parameter to field
|
||||
iv.load(0, OBJECT_TYPE);
|
||||
iv.load(1, functionType);
|
||||
iv.putfield(ownerType.getInternalName(), FUNCTION_FIELD_NAME, functionType.getDescriptor());
|
||||
|
||||
iv.visitInsn(RETURN);
|
||||
FunctionCodegen.endVisit(iv, "constructor of SAM wrapper", null);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateMethod(
|
||||
Type ownerType,
|
||||
Type functionType,
|
||||
ClassBuilder cv,
|
||||
SimpleFunctionDescriptor interfaceFunction,
|
||||
JetType functionJetType
|
||||
) {
|
||||
|
||||
// using static context to avoid creating ClassDescriptor and everything else
|
||||
FunctionCodegen codegen = new FunctionCodegen(CodegenContext.STATIC, cv, state);
|
||||
|
||||
FunctionDescriptor invokeFunction = functionJetType.getMemberScope()
|
||||
.getFunctions(Name.identifier("invoke")).iterator().next().getOriginal();
|
||||
StackValue functionField = StackValue.field(functionType, JvmClassName.byType(ownerType), FUNCTION_FIELD_NAME, false);
|
||||
codegen.genDelegate(interfaceFunction, invokeFunction, functionField);
|
||||
}
|
||||
}
|
||||
+28
-2
@@ -26,6 +26,7 @@ import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.resolve.BindingTrace;
|
||||
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
|
||||
import org.jetbrains.jet.lang.resolve.java.JvmClassName;
|
||||
import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
|
||||
@@ -55,6 +56,30 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid {
|
||||
this.bindingContext = bindingTrace.getBindingContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCallExpression(JetCallExpression expression) {
|
||||
super.visitCallExpression(expression);
|
||||
|
||||
JetExpression callee = expression.getCalleeExpression();
|
||||
assert callee != null : "not found callee for " + expression.getText();
|
||||
|
||||
ResolvedCall<? extends CallableDescriptor> resolvedCall = bindingContext.get(BindingContext.RESOLVED_CALL, callee);
|
||||
if (resolvedCall == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
DeclarationDescriptor funDescriptor = resolvedCall.getResultingDescriptor();
|
||||
|
||||
if (funDescriptor instanceof SimpleFunctionDescriptor) {
|
||||
ClassDescriptor samTrait = bindingContext.get(
|
||||
BindingContext.SAM_CONSTRUCTOR_TO_INTERFACE, ((SimpleFunctionDescriptor) funDescriptor).getOriginal());
|
||||
if (samTrait != null) {
|
||||
String name = inventAnonymousClassName(expression);
|
||||
bindingTrace.record(FQN_FOR_SAM_CONSTRUCTOR, expression, JvmClassName.byInternalName(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ClassDescriptor recordClassForFunction(FunctionDescriptor funDescriptor) {
|
||||
ClassDescriptor classDescriptor;
|
||||
int arity = funDescriptor.getValueParameters().size();
|
||||
@@ -90,11 +115,12 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid {
|
||||
if (descriptor == null) {
|
||||
if (declaration instanceof JetFunctionLiteralExpression ||
|
||||
declaration instanceof JetNamedFunction ||
|
||||
declaration instanceof JetObjectLiteralExpression) {
|
||||
declaration instanceof JetObjectLiteralExpression ||
|
||||
declaration instanceof JetCallExpression) {
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(
|
||||
"Class-less declaration which is not JetFunctionLiteralExpression|JetNamedFunction|JetObjectLiteralExpression : " +
|
||||
"Class-less declaration which is not JetFunctionLiteralExpression|JetNamedFunction|JetObjectLiteralExpression|JetCallExpression : " +
|
||||
declaration.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,8 @@ public class CodegenBinding {
|
||||
|
||||
public static final WritableSlice<DeclarationDescriptor, JvmClassName> FQN = Slices.createSimpleSlice();
|
||||
|
||||
public static final WritableSlice<JetCallExpression, JvmClassName> FQN_FOR_SAM_CONSTRUCTOR = Slices.createSimpleSlice();
|
||||
|
||||
public static final WritableSlice<JvmClassName, Boolean> SCRIPT_NAMES = Slices.createSimpleSetSlice();
|
||||
|
||||
public static final WritableSlice<ClassDescriptor, Boolean> ENUM_ENTRY_CLASS_NEED_SUBCLASS = Slices.createSimpleSetSlice();
|
||||
|
||||
+12
-7
@@ -41,7 +41,7 @@ public class SingleAbstractMethodUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<CallableMemberDescriptor> abstractMembers = getAbstractMembers(klass);
|
||||
List<CallableMemberDescriptor> abstractMembers = getAbstractMembers(klass.getDefaultType());
|
||||
if (abstractMembers.size() == 1) {
|
||||
CallableMemberDescriptor member = abstractMembers.get(0);
|
||||
if (member instanceof SimpleFunctionDescriptor) {
|
||||
@@ -52,9 +52,9 @@ public class SingleAbstractMethodUtils {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<CallableMemberDescriptor> getAbstractMembers(@NotNull ClassDescriptor klass) {
|
||||
private static List<CallableMemberDescriptor> getAbstractMembers(@NotNull JetType type) {
|
||||
List<CallableMemberDescriptor> abstractMembers = Lists.newArrayList();
|
||||
for (DeclarationDescriptor member : klass.getDefaultType().getMemberScope().getAllDescriptors()) {
|
||||
for (DeclarationDescriptor member : type.getMemberScope().getAllDescriptors()) {
|
||||
if (member instanceof CallableMemberDescriptor && ((CallableMemberDescriptor) member).getModality() == Modality.ABSTRACT) {
|
||||
abstractMembers.add((CallableMemberDescriptor) member);
|
||||
}
|
||||
@@ -88,7 +88,7 @@ public class SingleAbstractMethodUtils {
|
||||
SignaturesUtil.recreateTypeParametersAndReturnMapping(samInterface.getTypeConstructor().getParameters(), result);
|
||||
TypeSubstitutor typeParametersSubstitutor = SignaturesUtil.createSubstitutorForTypeParameters(traitToFunTypeParameters);
|
||||
|
||||
JetType parameterTypeUnsubstituted = getFunctionTypeForFunction(getAbstractMethodOfSamInterface(samInterface));
|
||||
JetType parameterTypeUnsubstituted = getFunctionTypeForFunction(getAbstractMethodOfSamType(samInterface.getDefaultType()));
|
||||
JetType parameterType = typeParametersSubstitutor.substitute(parameterTypeUnsubstituted, Variance.IN_VARIANCE);
|
||||
assert parameterType != null : "couldn't substitute type: " + parameterType + ", substitutor = " + typeParametersSubstitutor;
|
||||
ValueParameterDescriptor parameter = new ValueParameterDescriptorImpl(
|
||||
@@ -125,10 +125,15 @@ public class SingleAbstractMethodUtils {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static SimpleFunctionDescriptor getAbstractMethodOfSamInterface(@NotNull ClassDescriptor samInterface) {
|
||||
return (SimpleFunctionDescriptor) getAbstractMembers(samInterface).get(0);
|
||||
public static SimpleFunctionDescriptor getAbstractMethodOfSamType(@NotNull JetType type) {
|
||||
return (SimpleFunctionDescriptor) getAbstractMembers(type).get(0);
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public static SimpleFunctionDescriptor getAbstractMethodOfSamInterface(@NotNull ClassDescriptor samInterface) {
|
||||
return getAbstractMethodOfSamType(samInterface.getDefaultType());
|
||||
}
|
||||
|
||||
private SingleAbstractMethodUtils() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import java.util.*
|
||||
|
||||
fun box(): String {
|
||||
val list = ArrayList(Arrays.asList(3, 2, 4, 8, 1, 5))
|
||||
val expected = ArrayList(Arrays.asList(8, 5, 4, 3, 2, 1))
|
||||
val comparatorFun: (Int, Int) -> Int = { a, b -> b - a }
|
||||
Collections.sort(list, Comparator(comparatorFun))
|
||||
return if (list == expected) "OK" else list.toString()
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import java.io.*
|
||||
|
||||
fun box(): String {
|
||||
val f : (File?, String?) -> Boolean = { dir, name -> if (name == null) false else (name as java.lang.String).endsWith(".md") }
|
||||
val filter = FilenameFilter(f)
|
||||
val listFiles = File(".").listFiles(filter)!!
|
||||
if (listFiles.size != 1) return "Wrong size: $listFiles.size"
|
||||
val name = listFiles[0].getName()
|
||||
return if (name == "ReadMe.md") "OK" else "Wrong name: $name"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fun box(): String {
|
||||
var result = "FAIL"
|
||||
val f = { result = "OK" }
|
||||
val r = Runnable(f)
|
||||
r.run()
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
var result = "FAIL"
|
||||
|
||||
fun getFun(): () -> Unit {
|
||||
return { result = "OK" }
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val r = Runnable(getFun())
|
||||
r.run()
|
||||
return result
|
||||
}
|
||||
@@ -3179,6 +3179,26 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
doTest("compiler/testData/codegen/box/sam/filenameFilter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nonLiteralComparator.kt")
|
||||
public void testNonLiteralComparator() throws Exception {
|
||||
doTest("compiler/testData/codegen/box/sam/nonLiteralComparator.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nonLiteralFilenameFilter.kt")
|
||||
public void testNonLiteralFilenameFilter() throws Exception {
|
||||
doTest("compiler/testData/codegen/box/sam/nonLiteralFilenameFilter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nonLiteralRunnable.kt")
|
||||
public void testNonLiteralRunnable() throws Exception {
|
||||
doTest("compiler/testData/codegen/box/sam/nonLiteralRunnable.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nonTrivialRunnable.kt")
|
||||
public void testNonTrivialRunnable() throws Exception {
|
||||
doTest("compiler/testData/codegen/box/sam/nonTrivialRunnable.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("runnable.kt")
|
||||
public void testRunnable() throws Exception {
|
||||
doTest("compiler/testData/codegen/box/sam/runnable.kt");
|
||||
|
||||
Reference in New Issue
Block a user