Supported SAM constructor calls without function literal.

This commit is contained in:
Evgeny Gerashchenko
2013-03-28 20:52:30 +04:00
parent f11a1a2d28
commit 6ff9bfd4bb
13 changed files with 258 additions and 14 deletions
@@ -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);
}
}
@@ -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();
@@ -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");