001    /*
002     * Copyright 2010-2013 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.jet.codegen;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.jet.lang.resolve.kotlin.PackagePartClassUtils;
021    import org.jetbrains.org.objectweb.asm.MethodVisitor;
022    import org.jetbrains.org.objectweb.asm.Type;
023    import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
024    import org.jetbrains.jet.codegen.context.CodegenContext;
025    import org.jetbrains.jet.codegen.state.GenerationState;
026    import org.jetbrains.jet.codegen.state.JetTypeMapper;
027    import org.jetbrains.jet.lang.descriptors.*;
028    import org.jetbrains.jet.lang.descriptors.impl.ClassDescriptorImpl;
029    import org.jetbrains.jet.lang.psi.JetFile;
030    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
031    import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
032    import org.jetbrains.jet.lang.resolve.java.descriptor.JavaClassDescriptor;
033    import org.jetbrains.jet.lang.resolve.name.FqName;
034    import org.jetbrains.jet.lang.resolve.name.Name;
035    import org.jetbrains.jet.lang.types.JetType;
036    
037    import java.util.Collections;
038    
039    import static org.jetbrains.jet.codegen.AsmUtil.*;
040    import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
041    import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.KotlinSyntheticClass;
042    import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.OtherOrigin;
043    import static org.jetbrains.org.objectweb.asm.Opcodes.*;
044    
045    public class SamWrapperCodegen {
046        private static final String FUNCTION_FIELD_NAME = "function";
047    
048        private final GenerationState state;
049        private final JetTypeMapper typeMapper;
050        private final SamType samType;
051        private final MemberCodegen<?> parentCodegen;
052    
053        public SamWrapperCodegen(@NotNull GenerationState state, @NotNull SamType samType, @NotNull MemberCodegen<?> parentCodegen) {
054            this.state = state;
055            this.typeMapper = state.getTypeMapper();
056            this.samType = samType;
057            this.parentCodegen = parentCodegen;
058        }
059    
060        public Type genWrapper(@NotNull JetFile file) {
061            // Name for generated class, in form of whatever$1
062            FqName fqName = getWrapperName(file);
063            Type asmType = asmTypeByFqNameWithoutInnerClasses(fqName);
064    
065            // e.g. (T, T) -> Int
066            JetType functionType = samType.getKotlinFunctionType();
067    
068            ClassDescriptor classDescriptor = new ClassDescriptorImpl(
069                    samType.getJavaClassDescriptor().getContainingDeclaration(),
070                    fqName.shortName(),
071                    Modality.FINAL,
072                    Collections.singleton(samType.getType())
073            );
074            // e.g. compare(T, T)
075            SimpleFunctionDescriptor erasedInterfaceFunction = samType.getAbstractMethod().getOriginal().copy(
076                    classDescriptor,
077                    Modality.FINAL,
078                    Visibilities.PUBLIC,
079                    CallableMemberDescriptor.Kind.SYNTHESIZED,
080                    /*copyOverrides=*/ false
081            );
082    
083            ClassBuilder cv = state.getFactory().newVisitor(OtherOrigin(erasedInterfaceFunction), asmType, file);
084            cv.defineClass(file,
085                           V1_6,
086                           ACC_FINAL,
087                           asmType.getInternalName(),
088                           null,
089                           OBJECT_TYPE.getInternalName(),
090                           new String[]{ typeMapper.mapType(samType.getType()).getInternalName() }
091            );
092            cv.visitSource(file.getName(), null);
093    
094            writeKotlinSyntheticClassAnnotation(cv, KotlinSyntheticClass.Kind.SAM_WRAPPER);
095    
096            // e.g. ASM type for Function2
097            Type functionAsmType = typeMapper.mapType(functionType);
098    
099            cv.newField(OtherOrigin(erasedInterfaceFunction),
100                        ACC_SYNTHETIC | ACC_PRIVATE | ACC_FINAL,
101                        FUNCTION_FIELD_NAME,
102                        functionAsmType.getDescriptor(),
103                        null,
104                        null);
105    
106            generateConstructor(asmType, functionAsmType, cv);
107            generateMethod(asmType, functionAsmType, cv, erasedInterfaceFunction, functionType);
108    
109            cv.done();
110    
111            return asmType;
112        }
113    
114        private void generateConstructor(Type ownerType, Type functionType, ClassBuilder cv) {
115            MethodVisitor mv = cv.newMethod(OtherOrigin(samType.getJavaClassDescriptor()),
116                                            NO_FLAG_PACKAGE_PRIVATE, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, functionType), null, null);
117    
118            if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
119                mv.visitCode();
120                InstructionAdapter iv = new InstructionAdapter(mv);
121    
122                // super constructor
123                iv.load(0, OBJECT_TYPE);
124                iv.invokespecial(OBJECT_TYPE.getInternalName(), "<init>", "()V");
125    
126                // save parameter to field
127                iv.load(0, OBJECT_TYPE);
128                iv.load(1, functionType);
129                iv.putfield(ownerType.getInternalName(), FUNCTION_FIELD_NAME, functionType.getDescriptor());
130    
131                iv.visitInsn(RETURN);
132                FunctionCodegen.endVisit(iv, "constructor of SAM wrapper", null);
133            }
134        }
135    
136        private void generateMethod(
137                Type ownerType,
138                Type functionType,
139                ClassBuilder cv,
140                SimpleFunctionDescriptor erasedInterfaceFunction,
141                JetType functionJetType
142        ) {
143            // using static context to avoid creating ClassDescriptor and everything else
144            FunctionCodegen codegen = new FunctionCodegen(CodegenContext.STATIC.intoClass(
145                    (ClassDescriptor) erasedInterfaceFunction.getContainingDeclaration(), OwnerKind.IMPLEMENTATION, state), cv, state, parentCodegen);
146    
147            FunctionDescriptor invokeFunction = functionJetType.getMemberScope()
148                    .getFunctions(Name.identifier("invoke")).iterator().next().getOriginal();
149            StackValue functionField = StackValue.field(functionType, ownerType, FUNCTION_FIELD_NAME, false);
150            codegen.genDelegate(erasedInterfaceFunction, invokeFunction, functionField);
151        }
152    
153        @NotNull
154        private FqName getWrapperName(@NotNull JetFile containingFile) {
155            FqName packageClassFqName = PackageClassUtils.getPackageClassFqName(containingFile.getPackageFqName());
156            JavaClassDescriptor descriptor = samType.getJavaClassDescriptor();
157            String shortName = packageClassFqName.shortName().asString() + "$sam$" + descriptor.getName().asString() + "$" +
158                       Integer.toHexString(
159                               PackagePartClassUtils.getPathHashCode(containingFile.getVirtualFile()) * 31 +
160                               DescriptorUtils.getFqNameSafe(descriptor).hashCode()
161                       );
162            return packageClassFqName.parent().child(Name.identifier(shortName));
163        }
164    }