001 /* 002 * Copyright 2010-2015 JetBrains s.r.o. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package org.jetbrains.kotlin.codegen; 018 019 import org.jetbrains.annotations.NotNull; 020 import org.jetbrains.kotlin.codegen.context.CodegenContext; 021 import org.jetbrains.kotlin.codegen.state.GenerationState; 022 import org.jetbrains.kotlin.codegen.state.JetTypeMapper; 023 import org.jetbrains.kotlin.descriptors.*; 024 import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl; 025 import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl; 026 import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor; 027 import org.jetbrains.kotlin.load.kotlin.PackageClassUtils; 028 import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils; 029 import org.jetbrains.kotlin.name.FqName; 030 import org.jetbrains.kotlin.name.Name; 031 import org.jetbrains.kotlin.psi.JetFile; 032 import org.jetbrains.kotlin.resolve.DescriptorUtils; 033 import org.jetbrains.kotlin.types.JetType; 034 import org.jetbrains.kotlin.types.expressions.OperatorConventions; 035 import org.jetbrains.org.objectweb.asm.MethodVisitor; 036 import org.jetbrains.org.objectweb.asm.Type; 037 import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; 038 039 import java.util.Collections; 040 041 import static org.jetbrains.kotlin.codegen.AsmUtil.*; 042 import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinSyntheticClass; 043 import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE; 044 import static org.jetbrains.kotlin.resolve.jvm.diagnostics.DiagnosticsPackage.OtherOrigin; 045 import static org.jetbrains.org.objectweb.asm.Opcodes.*; 046 047 public class SamWrapperCodegen { 048 private static final String FUNCTION_FIELD_NAME = "function"; 049 050 private final GenerationState state; 051 private final JetTypeMapper typeMapper; 052 private final SamType samType; 053 private final MemberCodegen<?> parentCodegen; 054 055 public SamWrapperCodegen(@NotNull GenerationState state, @NotNull SamType samType, @NotNull MemberCodegen<?> parentCodegen) { 056 this.state = state; 057 this.typeMapper = state.getTypeMapper(); 058 this.samType = samType; 059 this.parentCodegen = parentCodegen; 060 } 061 062 public Type genWrapper(@NotNull JetFile file) { 063 // Name for generated class, in form of whatever$1 064 FqName fqName = getWrapperName(file); 065 Type asmType = asmTypeByFqNameWithoutInnerClasses(fqName); 066 067 // e.g. (T, T) -> Int 068 JetType functionType = samType.getKotlinFunctionType(); 069 070 ClassDescriptor classDescriptor = new ClassDescriptorImpl( 071 samType.getJavaClassDescriptor().getContainingDeclaration(), 072 fqName.shortName(), 073 Modality.FINAL, 074 Collections.singleton(samType.getType()), 075 SourceElement.NO_SOURCE 076 ); 077 // e.g. compare(T, T) 078 SimpleFunctionDescriptor erasedInterfaceFunction = samType.getAbstractMethod().getOriginal().copy( 079 classDescriptor, 080 Modality.FINAL, 081 Visibilities.PUBLIC, 082 CallableMemberDescriptor.Kind.SYNTHESIZED, 083 /*copyOverrides=*/ false 084 ); 085 086 ClassBuilder cv = state.getFactory().newVisitor(OtherOrigin(erasedInterfaceFunction), asmType, file); 087 cv.defineClass(file, 088 V1_6, 089 ACC_FINAL, 090 asmType.getInternalName(), 091 null, 092 OBJECT_TYPE.getInternalName(), 093 new String[]{ typeMapper.mapType(samType.getType()).getInternalName() } 094 ); 095 cv.visitSource(file.getName(), null); 096 097 writeKotlinSyntheticClassAnnotation(cv, KotlinSyntheticClass.Kind.SAM_WRAPPER); 098 099 // e.g. ASM type for Function2 100 Type functionAsmType = typeMapper.mapType(functionType); 101 102 cv.newField(OtherOrigin(erasedInterfaceFunction), 103 ACC_SYNTHETIC | ACC_PRIVATE | ACC_FINAL, 104 FUNCTION_FIELD_NAME, 105 functionAsmType.getDescriptor(), 106 null, 107 null); 108 109 generateConstructor(asmType, functionAsmType, cv); 110 generateMethod(asmType, functionAsmType, cv, erasedInterfaceFunction, functionType); 111 112 cv.done(); 113 114 return asmType; 115 } 116 117 private void generateConstructor(Type ownerType, Type functionType, ClassBuilder cv) { 118 MethodVisitor mv = cv.newMethod(OtherOrigin(samType.getJavaClassDescriptor()), 119 NO_FLAG_PACKAGE_PRIVATE, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, functionType), null, null); 120 121 if (state.getClassBuilderMode() == ClassBuilderMode.FULL) { 122 mv.visitCode(); 123 InstructionAdapter iv = new InstructionAdapter(mv); 124 125 // super constructor 126 iv.load(0, OBJECT_TYPE); 127 iv.invokespecial(OBJECT_TYPE.getInternalName(), "<init>", "()V", false); 128 129 // save parameter to field 130 iv.load(0, OBJECT_TYPE); 131 iv.load(1, functionType); 132 iv.putfield(ownerType.getInternalName(), FUNCTION_FIELD_NAME, functionType.getDescriptor()); 133 134 iv.visitInsn(RETURN); 135 FunctionCodegen.endVisit(iv, "constructor of SAM wrapper", null); 136 } 137 } 138 139 private void generateMethod( 140 Type ownerType, 141 Type functionType, 142 ClassBuilder cv, 143 SimpleFunctionDescriptor erasedInterfaceFunction, 144 JetType functionJetType 145 ) { 146 // using static context to avoid creating ClassDescriptor and everything else 147 FunctionCodegen codegen = new FunctionCodegen(CodegenContext.STATIC.intoClass( 148 (ClassDescriptor) erasedInterfaceFunction.getContainingDeclaration(), OwnerKind.IMPLEMENTATION, state), cv, state, parentCodegen); 149 150 FunctionDescriptor invokeFunction = 151 functionJetType.getMemberScope().getFunctions(OperatorConventions.INVOKE).iterator().next().getOriginal(); 152 StackValue functionField = StackValue.field(functionType, ownerType, FUNCTION_FIELD_NAME, false, StackValue.none()); 153 codegen.genDelegate(erasedInterfaceFunction, invokeFunction, functionField); 154 155 // generate sam bridges 156 // TODO: erasedInterfaceFunction is actually not an interface function, but function in generated class 157 SimpleFunctionDescriptor originalInterfaceErased = samType.getAbstractMethod().getOriginal(); 158 SimpleFunctionDescriptorImpl descriptorForBridges = SimpleFunctionDescriptorImpl 159 .create(erasedInterfaceFunction.getContainingDeclaration(), erasedInterfaceFunction.getAnnotations(), originalInterfaceErased.getName(), 160 CallableMemberDescriptor.Kind.DECLARATION, erasedInterfaceFunction.getSource()); 161 162 descriptorForBridges 163 .initialize(null, originalInterfaceErased.getDispatchReceiverParameter(), originalInterfaceErased.getTypeParameters(), 164 originalInterfaceErased.getValueParameters(), originalInterfaceErased.getReturnType(), Modality.OPEN, 165 originalInterfaceErased.getVisibility()); 166 167 descriptorForBridges.addOverriddenDescriptor(originalInterfaceErased); 168 codegen.generateBridges(descriptorForBridges); 169 } 170 171 @NotNull 172 private FqName getWrapperName(@NotNull JetFile containingFile) { 173 FqName packageClassFqName = PackageClassUtils.getPackageClassFqName(containingFile.getPackageFqName()); 174 JavaClassDescriptor descriptor = samType.getJavaClassDescriptor(); 175 int hash = PackagePartClassUtils.getPathHashCode(containingFile.getVirtualFile()) * 31 + 176 DescriptorUtils.getFqNameSafe(descriptor).hashCode(); 177 String shortName = String.format( 178 "%s$sam$%s$%08x", 179 packageClassFqName.shortName().asString(), 180 descriptor.getName().asString(), 181 hash 182 ); 183 return packageClassFqName.parent().child(Name.identifier(shortName)); 184 } 185 }