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.state.GenerationState; 021 import org.jetbrains.kotlin.codegen.state.JetTypeMapper; 022 import org.jetbrains.kotlin.descriptors.*; 023 import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl; 024 import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl; 025 import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil; 026 import org.jetbrains.kotlin.incremental.components.NoLookupLocation; 027 import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor; 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.KtFile; 032 import org.jetbrains.kotlin.resolve.DescriptorUtils; 033 import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; 034 import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt; 035 import org.jetbrains.kotlin.types.KotlinType; 036 import org.jetbrains.kotlin.util.OperatorNameConventions; 037 import org.jetbrains.org.objectweb.asm.MethodVisitor; 038 import org.jetbrains.org.objectweb.asm.Type; 039 import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; 040 041 import java.util.Collections; 042 043 import static org.jetbrains.kotlin.codegen.AsmUtil.NO_FLAG_PACKAGE_PRIVATE; 044 import static org.jetbrains.kotlin.codegen.AsmUtil.asmTypeByFqNameWithoutInnerClasses; 045 import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE; 046 import static org.jetbrains.org.objectweb.asm.Opcodes.*; 047 048 public class SamWrapperCodegen { 049 private static final String FUNCTION_FIELD_NAME = "function"; 050 051 private final GenerationState state; 052 private final boolean isInsideInline; 053 private final JetTypeMapper typeMapper; 054 private final SamType samType; 055 private final MemberCodegen<?> parentCodegen; 056 private final int visibility; 057 058 public SamWrapperCodegen( 059 @NotNull GenerationState state, 060 @NotNull SamType samType, 061 @NotNull MemberCodegen<?> parentCodegen, 062 boolean isInsideInline 063 ) { 064 this.state = state; 065 this.isInsideInline = isInsideInline; 066 this.typeMapper = state.getTypeMapper(); 067 this.samType = samType; 068 this.parentCodegen = parentCodegen; 069 visibility = isInsideInline ? ACC_PUBLIC : NO_FLAG_PACKAGE_PRIVATE; 070 } 071 072 @NotNull 073 public Type genWrapper(@NotNull KtFile file) { 074 // Name for generated class, in form of whatever$1 075 FqName fqName = getWrapperName(file); 076 Type asmType = asmTypeByFqNameWithoutInnerClasses(fqName); 077 078 // e.g. (T, T) -> Int 079 KotlinType functionType = samType.getKotlinFunctionType(); 080 081 ClassDescriptor classDescriptor = new ClassDescriptorImpl( 082 samType.getJavaClassDescriptor().getContainingDeclaration(), 083 fqName.shortName(), 084 Modality.FINAL, 085 ClassKind.CLASS, 086 Collections.singleton(samType.getType()), 087 SourceElement.NO_SOURCE 088 ); 089 // e.g. compare(T, T) 090 SimpleFunctionDescriptor erasedInterfaceFunction = samType.getAbstractMethod().getOriginal().copy( 091 classDescriptor, 092 Modality.FINAL, 093 Visibilities.PUBLIC, 094 CallableMemberDescriptor.Kind.SYNTHESIZED, 095 /*copyOverrides=*/ false 096 ); 097 098 ClassBuilder cv = state.getFactory().newVisitor(JvmDeclarationOriginKt.OtherOrigin(erasedInterfaceFunction), asmType, file); 099 cv.defineClass(file, 100 V1_6, 101 ACC_FINAL | ACC_SUPER | visibility, 102 asmType.getInternalName(), 103 null, 104 OBJECT_TYPE.getInternalName(), 105 new String[]{ typeMapper.mapType(samType.getType()).getInternalName() } 106 ); 107 cv.visitSource(file.getName(), null); 108 109 WriteAnnotationUtilKt.writeSyntheticClassMetadata(cv); 110 111 // e.g. ASM type for Function2 112 Type functionAsmType = typeMapper.mapType(functionType); 113 114 cv.newField(JvmDeclarationOriginKt.OtherOrigin(erasedInterfaceFunction), 115 ACC_SYNTHETIC | ACC_PRIVATE | ACC_FINAL, 116 FUNCTION_FIELD_NAME, 117 functionAsmType.getDescriptor(), 118 null, 119 null); 120 121 generateConstructor(asmType, functionAsmType, cv); 122 generateMethod(asmType, functionAsmType, cv, erasedInterfaceFunction, functionType); 123 124 cv.done(); 125 126 return asmType; 127 } 128 129 private void generateConstructor(Type ownerType, Type functionType, ClassBuilder cv) { 130 MethodVisitor mv = cv.newMethod(JvmDeclarationOriginKt.OtherOrigin(samType.getJavaClassDescriptor()), 131 visibility, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, functionType), null, null); 132 133 if (state.getClassBuilderMode() == ClassBuilderMode.FULL) { 134 mv.visitCode(); 135 InstructionAdapter iv = new InstructionAdapter(mv); 136 137 // super constructor 138 iv.load(0, OBJECT_TYPE); 139 iv.invokespecial(OBJECT_TYPE.getInternalName(), "<init>", "()V", false); 140 141 // save parameter to field 142 iv.load(0, OBJECT_TYPE); 143 iv.load(1, functionType); 144 iv.putfield(ownerType.getInternalName(), FUNCTION_FIELD_NAME, functionType.getDescriptor()); 145 146 iv.visitInsn(RETURN); 147 FunctionCodegen.endVisit(iv, "constructor of SAM wrapper", null); 148 } 149 } 150 151 private void generateMethod( 152 Type ownerType, 153 Type functionType, 154 ClassBuilder cv, 155 SimpleFunctionDescriptor erasedInterfaceFunction, 156 KotlinType functionJetType 157 ) { 158 // using root context to avoid creating ClassDescriptor and everything else 159 FunctionCodegen codegen = new FunctionCodegen(state.getRootContext().intoClass( 160 (ClassDescriptor) erasedInterfaceFunction.getContainingDeclaration(), OwnerKind.IMPLEMENTATION, state), cv, state, parentCodegen); 161 162 FunctionDescriptor invokeFunction = 163 functionJetType.getMemberScope().getContributedFunctions(OperatorNameConventions.INVOKE, NoLookupLocation.FROM_BACKEND).iterator().next().getOriginal(); 164 StackValue functionField = StackValue.field(functionType, ownerType, FUNCTION_FIELD_NAME, false, StackValue.none()); 165 codegen.genDelegate(erasedInterfaceFunction, invokeFunction, functionField); 166 167 // generate sam bridges 168 // TODO: erasedInterfaceFunction is actually not an interface function, but function in generated class 169 SimpleFunctionDescriptor originalInterfaceErased = samType.getAbstractMethod().getOriginal(); 170 SimpleFunctionDescriptorImpl descriptorForBridges = SimpleFunctionDescriptorImpl 171 .create(erasedInterfaceFunction.getContainingDeclaration(), erasedInterfaceFunction.getAnnotations(), originalInterfaceErased.getName(), 172 CallableMemberDescriptor.Kind.DECLARATION, erasedInterfaceFunction.getSource()); 173 174 descriptorForBridges 175 .initialize(null, originalInterfaceErased.getDispatchReceiverParameter(), originalInterfaceErased.getTypeParameters(), 176 originalInterfaceErased.getValueParameters(), originalInterfaceErased.getReturnType(), 177 Modality.OPEN, originalInterfaceErased.getVisibility()); 178 179 DescriptorUtilsKt.setSingleOverridden(descriptorForBridges, originalInterfaceErased); 180 codegen.generateBridges(descriptorForBridges); 181 } 182 183 @NotNull 184 private FqName getWrapperName(@NotNull KtFile containingFile) { 185 FqName fileClassFqName = JvmFileClassUtil.getFileClassInfoNoResolve(containingFile).getFileClassFqName(); 186 JavaClassDescriptor descriptor = samType.getJavaClassDescriptor(); 187 int hash = PackagePartClassUtils.getPathHashCode(containingFile.getVirtualFile()) * 31 + 188 DescriptorUtils.getFqNameSafe(descriptor).hashCode(); 189 String shortName = String.format( 190 "%s$sam$%s%s$%08x", 191 fileClassFqName.shortName().asString(), 192 descriptor.getName().asString(), 193 (isInsideInline ? "$i" : ""), 194 hash 195 ); 196 return fileClassFqName.parent().child(Name.identifier(shortName)); 197 } 198 }