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