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 com.google.common.collect.Lists;
020    import com.intellij.util.ArrayUtil;
021    import kotlin.CollectionsKt;
022    import kotlin.Unit;
023    import kotlin.jvm.functions.Function1;
024    import org.jetbrains.annotations.NotNull;
025    import org.jetbrains.annotations.Nullable;
026    import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
027    import org.jetbrains.kotlin.codegen.context.ClosureContext;
028    import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil;
029    import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension;
030    import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter;
031    import org.jetbrains.kotlin.codegen.state.GenerationState;
032    import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
033    import org.jetbrains.kotlin.descriptors.*;
034    import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl;
035    import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
036    import org.jetbrains.kotlin.load.java.JvmAbi;
037    import org.jetbrains.kotlin.load.java.JvmAnnotationNames;
038    import org.jetbrains.kotlin.psi.KtElement;
039    import org.jetbrains.kotlin.resolve.BindingContext;
040    import org.jetbrains.kotlin.resolve.DescriptorUtils;
041    import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
042    import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
043    import org.jetbrains.kotlin.resolve.scopes.KtScope;
044    import org.jetbrains.kotlin.serialization.DescriptorSerializer;
045    import org.jetbrains.kotlin.serialization.ProtoBuf;
046    import org.jetbrains.kotlin.types.KotlinType;
047    import org.jetbrains.kotlin.util.OperatorNameConventions;
048    import org.jetbrains.kotlin.utils.FunctionsKt;
049    import org.jetbrains.org.objectweb.asm.AnnotationVisitor;
050    import org.jetbrains.org.objectweb.asm.MethodVisitor;
051    import org.jetbrains.org.objectweb.asm.Type;
052    import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
053    import org.jetbrains.org.objectweb.asm.commons.Method;
054    
055    import java.util.ArrayList;
056    import java.util.Collections;
057    import java.util.List;
058    
059    import static org.jetbrains.kotlin.codegen.AsmUtil.*;
060    import static org.jetbrains.kotlin.codegen.ExpressionCodegen.generateClassLiteralReference;
061    import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isConst;
062    import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.writeModuleName;
063    import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.CLOSURE;
064    import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.asmTypeForAnonymousClass;
065    import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*;
066    import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.NO_ORIGIN;
067    import static org.jetbrains.org.objectweb.asm.Opcodes.*;
068    
069    public class ClosureCodegen extends MemberCodegen<KtElement> {
070        private final FunctionDescriptor funDescriptor;
071        private final ClassDescriptor classDescriptor;
072        private final SamType samType;
073        private final KotlinType superClassType;
074        private final List<KotlinType> superInterfaceTypes;
075        private final FunctionDescriptor functionReferenceTarget;
076        private final FunctionGenerationStrategy strategy;
077        private final CalculatedClosure closure;
078        private final Type asmType;
079        private final int visibilityFlag;
080    
081        private Method constructor;
082        private Type superClassAsmType;
083    
084        public ClosureCodegen(
085                @NotNull GenerationState state,
086                @NotNull KtElement element,
087                @Nullable SamType samType,
088                @NotNull ClosureContext context,
089                @Nullable FunctionDescriptor functionReferenceTarget,
090                @NotNull FunctionGenerationStrategy strategy,
091                @NotNull MemberCodegen<?> parentCodegen,
092                @NotNull ClassBuilder classBuilder
093        ) {
094            super(state, parentCodegen, context, element, classBuilder);
095    
096            this.funDescriptor = context.getFunctionDescriptor();
097            this.classDescriptor = context.getContextDescriptor();
098            this.samType = samType;
099            this.functionReferenceTarget = functionReferenceTarget;
100            this.strategy = strategy;
101    
102            if (samType == null) {
103                this.superInterfaceTypes = new ArrayList<KotlinType>();
104    
105                KotlinType superClassType = null;
106                for (KotlinType supertype : classDescriptor.getTypeConstructor().getSupertypes()) {
107                    ClassifierDescriptor classifier = supertype.getConstructor().getDeclarationDescriptor();
108                    if (DescriptorUtils.isInterface(classifier)) {
109                        superInterfaceTypes.add(supertype);
110                    }
111                    else {
112                        assert superClassType == null : "Closure class can't have more than one superclass: " + funDescriptor;
113                        superClassType = supertype;
114                    }
115                }
116                assert superClassType != null : "Closure class should have a superclass: " + funDescriptor;
117    
118                this.superClassType = superClassType;
119            }
120            else {
121                this.superInterfaceTypes = Collections.singletonList(samType.getType());
122                this.superClassType = DescriptorUtilsKt.getBuiltIns(funDescriptor).getAnyType();
123            }
124    
125            this.closure = bindingContext.get(CLOSURE, classDescriptor);
126            assert closure != null : "Closure must be calculated for class: " + classDescriptor;
127    
128            this.asmType = typeMapper.mapClass(classDescriptor);
129    
130            visibilityFlag = AsmUtil.getVisibilityAccessFlagForAnonymous(classDescriptor);
131        }
132    
133        @Override
134        protected void generateDeclaration() {
135            BothSignatureWriter sw = new BothSignatureWriter(BothSignatureWriter.Mode.CLASS);
136            if (samType != null) {
137                typeMapper.writeFormalTypeParameters(samType.getType().getConstructor().getParameters(), sw);
138            }
139            sw.writeSuperclass();
140            superClassAsmType = typeMapper.mapSupertype(superClassType, sw);
141            sw.writeSuperclassEnd();
142            String[] superInterfaceAsmTypes = new String[superInterfaceTypes.size()];
143            for (int i = 0; i < superInterfaceTypes.size(); i++) {
144                KotlinType superInterfaceType = superInterfaceTypes.get(i);
145                sw.writeInterface();
146                superInterfaceAsmTypes[i] = typeMapper.mapSupertype(superInterfaceType, sw).getInternalName();
147                sw.writeInterfaceEnd();
148            }
149    
150            v.defineClass(element,
151                          V1_6,
152                          ACC_FINAL | ACC_SUPER | visibilityFlag,
153                          asmType.getInternalName(),
154                          sw.makeJavaGenericSignature(),
155                          superClassAsmType.getInternalName(),
156                          superInterfaceAsmTypes
157            );
158    
159            InlineCodegenUtil.initDefaultSourceMappingIfNeeded(context, this, state);
160    
161            v.visitSource(element.getContainingFile().getName(), null);
162        }
163    
164        @Nullable
165        @Override
166        protected ClassDescriptor classForInnerClassRecord() {
167            return JvmCodegenUtil.isArgumentWhichWillBeInlined(bindingContext, funDescriptor) ? null : classDescriptor;
168        }
169    
170        @Override
171        protected void generateBody() {
172            FunctionDescriptor erasedInterfaceFunction;
173            if (samType == null) {
174                erasedInterfaceFunction = getErasedInvokeFunction(funDescriptor);
175            }
176            else {
177                erasedInterfaceFunction = samType.getAbstractMethod().getOriginal();
178            }
179    
180            generateBridge(
181                    typeMapper.mapSignature(erasedInterfaceFunction).getAsmMethod(),
182                    typeMapper.mapSignature(funDescriptor).getAsmMethod()
183            );
184    
185            functionCodegen.generateMethod(JvmDeclarationOriginKt.OtherOrigin(element, funDescriptor), funDescriptor, strategy);
186    
187            //TODO: rewrite cause ugly hack
188            if (samType != null) {
189                SimpleFunctionDescriptorImpl descriptorForBridges = SimpleFunctionDescriptorImpl
190                        .create(funDescriptor.getContainingDeclaration(), funDescriptor.getAnnotations(),
191                                erasedInterfaceFunction.getName(),
192                                CallableMemberDescriptor.Kind.DECLARATION, funDescriptor.getSource());
193    
194                descriptorForBridges
195                        .initialize(null, erasedInterfaceFunction.getDispatchReceiverParameter(), erasedInterfaceFunction.getTypeParameters(),
196                                    erasedInterfaceFunction.getValueParameters(), erasedInterfaceFunction.getReturnType(),
197                                    Modality.OPEN, erasedInterfaceFunction.getVisibility());
198    
199                descriptorForBridges.addOverriddenDescriptor(erasedInterfaceFunction);
200                functionCodegen.generateBridges(descriptorForBridges);
201            }
202    
203            if (functionReferenceTarget != null) {
204                generateFunctionReferenceMethods(functionReferenceTarget);
205            }
206    
207            this.constructor = generateConstructor();
208    
209            if (isConst(closure)) {
210                generateConstInstance(asmType, asmType, FunctionsKt.<InstructionAdapter>doNothing());
211            }
212    
213            genClosureFields(closure, v, typeMapper);
214    
215            functionCodegen.generateDefaultIfNeeded(
216                    context.intoFunction(funDescriptor), funDescriptor, context.getContextKind(), DefaultParameterValueLoader.DEFAULT, null
217            );
218        }
219    
220        @Override
221        protected void generateKotlinAnnotation() {
222            writeKotlinSyntheticClassAnnotation(v, state);
223    
224            DescriptorSerializer serializer =
225                    DescriptorSerializer.createForLambda(
226                            new JvmSerializerExtension(v.getSerializationBindings(), typeMapper, state.getUseTypeTableInSerializer())
227                    );
228    
229            ProtoBuf.Function functionProto = serializer.functionProto(funDescriptor).build();
230    
231            AnnotationVisitor av = v.getVisitor().visitAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_FUNCTION), true);
232            writeAnnotationData(av, serializer, functionProto);
233            writeModuleName(av, state);
234            av.visitEnd();
235        }
236    
237        @Override
238        protected void done() {
239            writeOuterClassAndEnclosingMethod();
240            super.done();
241        }
242    
243        @NotNull
244        public StackValue putInstanceOnStack(@NotNull final ExpressionCodegen codegen) {
245            return StackValue.operation(
246                    functionReferenceTarget != null ? K_FUNCTION : asmType,
247                    new Function1<InstructionAdapter, Unit>() {
248                        @Override
249                        public Unit invoke(InstructionAdapter v) {
250                            if (isConst(closure)) {
251                                v.getstatic(asmType.getInternalName(), JvmAbi.INSTANCE_FIELD, asmType.getDescriptor());
252                            }
253                            else {
254                                v.anew(asmType);
255                                v.dup();
256    
257                                codegen.pushClosureOnStack(classDescriptor, true, codegen.defaultCallGenerator);
258                                v.invokespecial(asmType.getInternalName(), "<init>", constructor.getDescriptor(), false);
259                            }
260    
261                            if (functionReferenceTarget != null) {
262                                v.invokestatic(REFLECTION, "function", Type.getMethodDescriptor(K_FUNCTION, FUNCTION_REFERENCE), false);
263                            }
264    
265                            return Unit.INSTANCE$;
266                        }
267                    }
268            );
269        }
270    
271        private void generateBridge(@NotNull Method bridge, @NotNull Method delegate) {
272            if (bridge.equals(delegate)) return;
273    
274            MethodVisitor mv =
275                    v.newMethod(JvmDeclarationOriginKt.OtherOrigin(element, funDescriptor), ACC_PUBLIC | ACC_BRIDGE,
276                                bridge.getName(), bridge.getDescriptor(), null, ArrayUtil.EMPTY_STRING_ARRAY);
277    
278            if (state.getClassBuilderMode() != ClassBuilderMode.FULL) return;
279    
280            mv.visitCode();
281    
282            InstructionAdapter iv = new InstructionAdapter(mv);
283            MemberCodegen.markLineNumberForSyntheticFunction(DescriptorUtils.getParentOfType(funDescriptor, ClassDescriptor.class), iv);
284    
285            iv.load(0, asmType);
286    
287            Type[] myParameterTypes = bridge.getArgumentTypes();
288    
289            List<ParameterDescriptor> calleeParameters = CollectionsKt.<ParameterDescriptor>plus(
290                    org.jetbrains.kotlin.utils.CollectionsKt.<ParameterDescriptor>singletonOrEmptyList(funDescriptor.getExtensionReceiverParameter()),
291                    funDescriptor.getValueParameters()
292            );
293    
294            int slot = 1;
295            for (int i = 0; i < calleeParameters.size(); i++) {
296                Type type = myParameterTypes[i];
297                StackValue.local(slot, type).put(typeMapper.mapType(calleeParameters.get(i)), iv);
298                slot += type.getSize();
299            }
300    
301            iv.invokevirtual(asmType.getInternalName(), delegate.getName(), delegate.getDescriptor(), false);
302            StackValue.onStack(delegate.getReturnType()).put(bridge.getReturnType(), iv);
303    
304            iv.areturn(bridge.getReturnType());
305    
306            FunctionCodegen.endVisit(mv, "bridge", element);
307        }
308    
309        // TODO: ImplementationBodyCodegen.markLineNumberForSyntheticFunction?
310        private void generateFunctionReferenceMethods(@NotNull FunctionDescriptor descriptor) {
311            int flags = ACC_PUBLIC | ACC_FINAL;
312            boolean generateBody = state.getClassBuilderMode() == ClassBuilderMode.FULL;
313    
314            {
315                MethodVisitor mv =
316                        v.newMethod(NO_ORIGIN, flags, "getOwner", Type.getMethodDescriptor(K_DECLARATION_CONTAINER_TYPE), null, null);
317                if (generateBody) {
318                    mv.visitCode();
319                    InstructionAdapter iv = new InstructionAdapter(mv);
320                    generateCallableReferenceDeclarationContainer(iv, descriptor, state);
321                    iv.areturn(K_DECLARATION_CONTAINER_TYPE);
322                    FunctionCodegen.endVisit(iv, "function reference getOwner", element);
323                }
324            }
325    
326            {
327                MethodVisitor mv =
328                        v.newMethod(NO_ORIGIN, flags, "getName", Type.getMethodDescriptor(JAVA_STRING_TYPE), null, null);
329                if (generateBody) {
330                    mv.visitCode();
331                    InstructionAdapter iv = new InstructionAdapter(mv);
332                    iv.aconst(descriptor.getName().asString());
333                    iv.areturn(JAVA_STRING_TYPE);
334                    FunctionCodegen.endVisit(iv, "function reference getName", element);
335                }
336            }
337    
338            {
339                MethodVisitor mv = v.newMethod(NO_ORIGIN, flags, "getSignature", Type.getMethodDescriptor(JAVA_STRING_TYPE), null, null);
340                if (generateBody) {
341                    mv.visitCode();
342                    InstructionAdapter iv = new InstructionAdapter(mv);
343                    Method method = typeMapper.mapSignature(descriptor.getOriginal()).getAsmMethod();
344                    iv.aconst(method.getName() + method.getDescriptor());
345                    iv.areturn(JAVA_STRING_TYPE);
346                    FunctionCodegen.endVisit(iv, "function reference getSignature", element);
347                }
348            }
349        }
350    
351        public static void generateCallableReferenceDeclarationContainer(
352                @NotNull InstructionAdapter iv,
353                @NotNull CallableDescriptor descriptor,
354                @NotNull GenerationState state
355        ) {
356            DeclarationDescriptor container = descriptor.getContainingDeclaration();
357            if (container instanceof ClassDescriptor) {
358                // TODO: getDefaultType() here is wrong and won't work for arrays
359                StackValue value = generateClassLiteralReference(state.getTypeMapper(), ((ClassDescriptor) container).getDefaultType());
360                value.put(K_CLASS_TYPE, iv);
361            }
362            else if (container instanceof PackageFragmentDescriptor) {
363                iv.aconst(state.getTypeMapper().mapOwner(descriptor));
364                iv.aconst(state.getModuleName());
365                iv.invokestatic(REFLECTION, "getOrCreateKotlinPackage",
366                                Type.getMethodDescriptor(K_DECLARATION_CONTAINER_TYPE, getType(Class.class), getType(String.class)), false);
367            }
368            else if (container instanceof ScriptDescriptor) {
369                // TODO: correct container for scripts (KScript?)
370                StackValue value = generateClassLiteralReference(
371                        state.getTypeMapper(), ((ScriptDescriptor) container).getClassDescriptor().getDefaultType()
372                );
373                value.put(K_CLASS_TYPE, iv);
374            }
375            else {
376                iv.aconst(null);
377            }
378        }
379    
380        @NotNull
381        private Method generateConstructor() {
382            List<FieldInfo> args = calculateConstructorParameters(typeMapper, closure, asmType);
383    
384            Type[] argTypes = fieldListToTypeArray(args);
385    
386            Method constructor = new Method("<init>", Type.VOID_TYPE, argTypes);
387            MethodVisitor mv = v.newMethod(JvmDeclarationOriginKt.OtherOrigin(element, funDescriptor), visibilityFlag, "<init>", constructor.getDescriptor(), null,
388                                           ArrayUtil.EMPTY_STRING_ARRAY);
389            if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
390                mv.visitCode();
391                InstructionAdapter iv = new InstructionAdapter(mv);
392    
393                int k = 1;
394                for (FieldInfo fieldInfo : args) {
395                    k = genAssignInstanceFieldFromParam(fieldInfo, k, iv);
396                }
397    
398                iv.load(0, superClassAsmType);
399    
400                if (superClassAsmType.equals(LAMBDA) || superClassAsmType.equals(FUNCTION_REFERENCE)) {
401                    int arity = funDescriptor.getValueParameters().size();
402                    if (funDescriptor.getExtensionReceiverParameter() != null) arity++;
403                    if (funDescriptor.getDispatchReceiverParameter() != null) arity++;
404                    iv.iconst(arity);
405                    iv.invokespecial(superClassAsmType.getInternalName(), "<init>", "(I)V", false);
406                }
407                else {
408                    iv.invokespecial(superClassAsmType.getInternalName(), "<init>", "()V", false);
409                }
410    
411                iv.visitInsn(RETURN);
412    
413                FunctionCodegen.endVisit(iv, "constructor", element);
414            }
415            return constructor;
416        }
417    
418        @NotNull
419        public static List<FieldInfo> calculateConstructorParameters(
420                @NotNull JetTypeMapper typeMapper,
421                @NotNull CalculatedClosure closure,
422                @NotNull Type ownerType
423        ) {
424            BindingContext bindingContext = typeMapper.getBindingContext();
425            List<FieldInfo> args = Lists.newArrayList();
426            ClassDescriptor captureThis = closure.getCaptureThis();
427            if (captureThis != null) {
428                Type type = typeMapper.mapType(captureThis);
429                args.add(FieldInfo.createForHiddenField(ownerType, type, CAPTURED_THIS_FIELD));
430            }
431            KotlinType captureReceiverType = closure.getCaptureReceiverType();
432            if (captureReceiverType != null) {
433                args.add(FieldInfo.createForHiddenField(ownerType, typeMapper.mapType(captureReceiverType), CAPTURED_RECEIVER_FIELD));
434            }
435    
436            for (DeclarationDescriptor descriptor : closure.getCaptureVariables().keySet()) {
437                if (descriptor instanceof VariableDescriptor && !(descriptor instanceof PropertyDescriptor)) {
438                    Type sharedVarType = typeMapper.getSharedVarType(descriptor);
439    
440                    Type type = sharedVarType != null
441                                      ? sharedVarType
442                                      : typeMapper.mapType((VariableDescriptor) descriptor);
443                    args.add(FieldInfo.createForHiddenField(ownerType, type, "$" + descriptor.getName().asString()));
444                }
445                else if (DescriptorUtils.isLocalFunction(descriptor)) {
446                    Type classType = asmTypeForAnonymousClass(bindingContext, (FunctionDescriptor) descriptor);
447                    args.add(FieldInfo.createForHiddenField(ownerType, classType, "$" + descriptor.getName().asString()));
448                }
449                else if (descriptor instanceof FunctionDescriptor) {
450                    assert captureReceiverType != null;
451                }
452            }
453            return args;
454        }
455    
456        private static Type[] fieldListToTypeArray(List<FieldInfo> args) {
457            Type[] argTypes = new Type[args.size()];
458            for (int i = 0; i != argTypes.length; ++i) {
459                argTypes[i] = args.get(i).getFieldType();
460            }
461            return argTypes;
462        }
463    
464        @NotNull
465        public static FunctionDescriptor getErasedInvokeFunction(@NotNull FunctionDescriptor elementDescriptor) {
466            int arity = elementDescriptor.getValueParameters().size();
467            ClassDescriptor elementClass = elementDescriptor.getExtensionReceiverParameter() == null
468                                       ? DescriptorUtilsKt.getBuiltIns(elementDescriptor).getFunction(arity)
469                                       : DescriptorUtilsKt.getBuiltIns(elementDescriptor).getExtensionFunction(arity);
470            KtScope scope = elementClass.getDefaultType().getMemberScope();
471            return scope.getFunctions(OperatorNameConventions.INVOKE, NoLookupLocation.FROM_BACKEND).iterator().next();
472        }
473    }