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