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.jvm.functions.Function1;
023    import org.jetbrains.annotations.NotNull;
024    import org.jetbrains.annotations.Nullable;
025    import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
026    import org.jetbrains.kotlin.codegen.context.ClosureContext;
027    import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil;
028    import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter;
029    import org.jetbrains.kotlin.codegen.state.GenerationState;
030    import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
031    import org.jetbrains.kotlin.descriptors.*;
032    import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl;
033    import org.jetbrains.kotlin.load.java.JvmAbi;
034    import org.jetbrains.kotlin.psi.JetElement;
035    import org.jetbrains.kotlin.resolve.BindingContext;
036    import org.jetbrains.kotlin.resolve.DescriptorUtils;
037    import org.jetbrains.kotlin.types.JetType;
038    import org.jetbrains.kotlin.types.expressions.OperatorConventions;
039    import org.jetbrains.org.objectweb.asm.MethodVisitor;
040    import org.jetbrains.org.objectweb.asm.Type;
041    import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
042    import org.jetbrains.org.objectweb.asm.commons.Method;
043    
044    import java.util.ArrayList;
045    import java.util.Collections;
046    import java.util.List;
047    
048    import static org.jetbrains.kotlin.codegen.AsmUtil.*;
049    import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isConst;
050    import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.CLOSURE;
051    import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.asmTypeForAnonymousClass;
052    import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinSyntheticClass;
053    import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage.getBuiltIns;
054    import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*;
055    import static org.jetbrains.kotlin.resolve.jvm.diagnostics.DiagnosticsPackage.OtherOrigin;
056    import static org.jetbrains.org.objectweb.asm.Opcodes.*;
057    
058    public class ClosureCodegen extends MemberCodegen<JetElement> {
059        private final FunctionDescriptor funDescriptor;
060        private final ClassDescriptor classDescriptor;
061        private final SamType samType;
062        private final JetType superClassType;
063        private final List<JetType> superInterfaceTypes;
064        private final FunctionGenerationStrategy strategy;
065        private final CalculatedClosure closure;
066        private final Type asmType;
067        private final int visibilityFlag;
068        private final KotlinSyntheticClass.Kind syntheticClassKind;
069    
070        private Method constructor;
071        private Type superClassAsmType;
072    
073        public ClosureCodegen(
074                @NotNull GenerationState state,
075                @NotNull JetElement element,
076                @Nullable SamType samType,
077                @NotNull ClosureContext context,
078                @NotNull KotlinSyntheticClass.Kind syntheticClassKind,
079                @NotNull FunctionGenerationStrategy strategy,
080                @NotNull MemberCodegen<?> parentCodegen,
081                @NotNull ClassBuilder classBuilder
082        ) {
083            super(state, parentCodegen, context, element, classBuilder);
084    
085            this.funDescriptor = context.getFunctionDescriptor();
086            this.classDescriptor = context.getContextDescriptor();
087            this.samType = samType;
088            this.syntheticClassKind = syntheticClassKind;
089            this.strategy = strategy;
090    
091            if (samType == null) {
092                this.superInterfaceTypes = new ArrayList<JetType>();
093    
094                JetType superClassType = null;
095                for (JetType supertype : classDescriptor.getTypeConstructor().getSupertypes()) {
096                    ClassifierDescriptor classifier = supertype.getConstructor().getDeclarationDescriptor();
097                    if (DescriptorUtils.isTrait(classifier)) {
098                        superInterfaceTypes.add(supertype);
099                    }
100                    else {
101                        assert superClassType == null : "Closure class can't have more than one superclass: " + funDescriptor;
102                        superClassType = supertype;
103                    }
104                }
105                assert superClassType != null : "Closure class should have a superclass: " + funDescriptor;
106    
107                this.superClassType = superClassType;
108            }
109            else {
110                this.superInterfaceTypes = Collections.singletonList(samType.getType());
111                this.superClassType = getBuiltIns(funDescriptor).getAnyType();
112            }
113    
114            this.closure = bindingContext.get(CLOSURE, classDescriptor);
115            assert closure != null : "Closure must be calculated for class: " + classDescriptor;
116    
117            this.asmType = typeMapper.mapClass(classDescriptor);
118    
119            visibilityFlag = AsmUtil.getVisibilityAccessFlagForAnonymous(classDescriptor);
120        }
121    
122        @Override
123        protected void generateDeclaration() {
124            BothSignatureWriter sw = new BothSignatureWriter(BothSignatureWriter.Mode.CLASS);
125            if (samType != null) {
126                typeMapper.writeFormalTypeParameters(samType.getType().getConstructor().getParameters(), sw);
127            }
128            sw.writeSuperclass();
129            superClassAsmType = typeMapper.mapSupertype(superClassType, sw);
130            sw.writeSuperclassEnd();
131            String[] superInterfaceAsmTypes = new String[superInterfaceTypes.size()];
132            for (int i = 0; i < superInterfaceTypes.size(); i++) {
133                JetType superInterfaceType = superInterfaceTypes.get(i);
134                sw.writeInterface();
135                superInterfaceAsmTypes[i] = typeMapper.mapSupertype(superInterfaceType, sw).getInternalName();
136                sw.writeInterfaceEnd();
137            }
138    
139            v.defineClass(element,
140                          V1_6,
141                          ACC_FINAL | ACC_SUPER | visibilityFlag,
142                          asmType.getInternalName(),
143                          sw.makeJavaGenericSignature(),
144                          superClassAsmType.getInternalName(),
145                          superInterfaceAsmTypes
146            );
147    
148            InlineCodegenUtil.initDefaultSourceMappingIfNeeded(context, this, state);
149    
150            v.visitSource(element.getContainingFile().getName(), null);
151        }
152    
153        @Nullable
154        @Override
155        protected ClassDescriptor classForInnerClassRecord() {
156            return JvmCodegenUtil.isArgumentWhichWillBeInlined(bindingContext, funDescriptor) ? null : classDescriptor;
157        }
158    
159        @Override
160        protected void generateBody() {
161            FunctionDescriptor erasedInterfaceFunction;
162            if (samType == null) {
163                erasedInterfaceFunction = getErasedInvokeFunction(funDescriptor);
164            }
165            else {
166                erasedInterfaceFunction = samType.getAbstractMethod().getOriginal();
167            }
168    
169            generateBridge(
170                    typeMapper.mapSignature(erasedInterfaceFunction).getAsmMethod(),
171                    typeMapper.mapSignature(funDescriptor).getAsmMethod()
172            );
173    
174            functionCodegen.generateMethod(OtherOrigin(element, funDescriptor), funDescriptor, strategy);
175    
176            //TODO: rewrite cause ugly hack
177            if (samType != null) {
178                SimpleFunctionDescriptorImpl descriptorForBridges = SimpleFunctionDescriptorImpl
179                        .create(funDescriptor.getContainingDeclaration(), funDescriptor.getAnnotations(),
180                                erasedInterfaceFunction.getName(),
181                                CallableMemberDescriptor.Kind.DECLARATION, funDescriptor.getSource());
182    
183                descriptorForBridges
184                        .initialize(null, erasedInterfaceFunction.getDispatchReceiverParameter(), erasedInterfaceFunction.getTypeParameters(),
185                                    erasedInterfaceFunction.getValueParameters(), erasedInterfaceFunction.getReturnType(), Modality.OPEN,
186                                    erasedInterfaceFunction.getVisibility());
187    
188                descriptorForBridges.addOverriddenDescriptor(erasedInterfaceFunction);
189                functionCodegen.generateBridges(descriptorForBridges);
190            }
191    
192            this.constructor = generateConstructor(superClassAsmType);
193    
194            if (isConst(closure)) {
195                generateConstInstance();
196            }
197    
198            genClosureFields(closure, v, typeMapper);
199    
200            functionCodegen.generateDefaultIfNeeded(
201                    context.intoFunction(funDescriptor), funDescriptor, context.getContextKind(), DefaultParameterValueLoader.DEFAULT, null
202            );
203        }
204    
205        @Override
206        protected void generateKotlinAnnotation() {
207            writeKotlinSyntheticClassAnnotation(v, syntheticClassKind);
208        }
209    
210        @Override
211        protected void done() {
212            writeOuterClassAndEnclosingMethod();
213            super.done();
214        }
215    
216        @NotNull
217        public StackValue putInstanceOnStack(
218                @NotNull final ExpressionCodegen codegen,
219                @Nullable final FunctionDescriptor functionReferenceTarget
220        ) {
221            return StackValue.operation(asmType, new Function1<InstructionAdapter, Unit>() {
222                @Override
223                public Unit invoke(InstructionAdapter v) {
224                    if (isConst(closure)) {
225                        v.getstatic(asmType.getInternalName(), JvmAbi.INSTANCE_FIELD, asmType.getDescriptor());
226                    }
227                    else {
228                        v.anew(asmType);
229                        v.dup();
230    
231                        codegen.pushClosureOnStack(classDescriptor, true, codegen.defaultCallGenerator);
232                        v.invokespecial(asmType.getInternalName(), "<init>", constructor.getDescriptor(), false);
233                    }
234    
235                    if (functionReferenceTarget != null) {
236                        equipFunctionReferenceWithReflection(v, functionReferenceTarget);
237                    }
238    
239                    return Unit.INSTANCE$;
240                }
241            });
242        }
243    
244        private static void equipFunctionReferenceWithReflection(@NotNull InstructionAdapter v, @NotNull FunctionDescriptor target) {
245            DeclarationDescriptor container = target.getContainingDeclaration();
246    
247            Type type;
248            if (container instanceof PackageFragmentDescriptor) {
249                type = target.getExtensionReceiverParameter() != null
250                       ? K_TOP_LEVEL_EXTENSION_FUNCTION
251                       : K_TOP_LEVEL_FUNCTION;
252            }
253            else if (container instanceof ClassDescriptor) {
254                type = K_MEMBER_FUNCTION;
255            }
256            else {
257                type = K_LOCAL_FUNCTION;
258            }
259    
260            Method method = method("function", K_FUNCTION, FUNCTION_REFERENCE);
261            v.invokestatic(REFLECTION, method.getName(), method.getDescriptor(), false);
262            StackValue.coerce(K_FUNCTION, type, v);
263        }
264    
265    
266        private void generateConstInstance() {
267            MethodVisitor mv = v.newMethod(OtherOrigin(element, funDescriptor), ACC_STATIC | ACC_SYNTHETIC, "<clinit>", "()V", null, ArrayUtil.EMPTY_STRING_ARRAY);
268            InstructionAdapter iv = new InstructionAdapter(mv);
269    
270            v.newField(OtherOrigin(element, funDescriptor), ACC_STATIC | ACC_FINAL | ACC_PUBLIC, JvmAbi.INSTANCE_FIELD, asmType.getDescriptor(), null, null);
271    
272            if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
273                mv.visitCode();
274                iv.anew(asmType);
275                iv.dup();
276                iv.invokespecial(asmType.getInternalName(), "<init>", "()V", false);
277                iv.putstatic(asmType.getInternalName(), JvmAbi.INSTANCE_FIELD, asmType.getDescriptor());
278                mv.visitInsn(RETURN);
279                FunctionCodegen.endVisit(mv, "<clinit>", element);
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, bridge.getName(), bridge.getDescriptor(), null, ArrayUtil.EMPTY_STRING_ARRAY);
288    
289            if (state.getClassBuilderMode() != ClassBuilderMode.FULL) return;
290    
291            mv.visitCode();
292    
293            InstructionAdapter iv = new InstructionAdapter(mv);
294            ImplementationBodyCodegen.markLineNumberForSyntheticFunction(DescriptorUtils.getParentOfType(funDescriptor, ClassDescriptor.class), iv);
295    
296            iv.load(0, asmType);
297    
298            ReceiverParameterDescriptor receiver = funDescriptor.getExtensionReceiverParameter();
299            int count = 1;
300            if (receiver != null) {
301                StackValue.local(count, bridge.getArgumentTypes()[count - 1]).put(typeMapper.mapType(receiver.getType()), iv);
302                count++;
303            }
304    
305            List<ValueParameterDescriptor> params = funDescriptor.getValueParameters();
306            for (ValueParameterDescriptor param : params) {
307                StackValue.local(count, bridge.getArgumentTypes()[count - 1]).put(typeMapper.mapType(param.getType()), iv);
308                count++;
309            }
310    
311            iv.invokevirtual(asmType.getInternalName(), delegate.getName(), delegate.getDescriptor(), false);
312            StackValue.onStack(delegate.getReturnType()).put(bridge.getReturnType(), iv);
313    
314            iv.areturn(bridge.getReturnType());
315    
316            FunctionCodegen.endVisit(mv, "bridge", element);
317        }
318    
319        @NotNull
320        private Method generateConstructor(@NotNull Type superClassAsmType) {
321            List<FieldInfo> args = calculateConstructorParameters(typeMapper, closure, asmType);
322    
323            Type[] argTypes = fieldListToTypeArray(args);
324    
325            Method constructor = new Method("<init>", Type.VOID_TYPE, argTypes);
326            MethodVisitor mv = v.newMethod(OtherOrigin(element, funDescriptor), visibilityFlag, "<init>", constructor.getDescriptor(), null,
327                                            ArrayUtil.EMPTY_STRING_ARRAY);
328            if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
329                mv.visitCode();
330                InstructionAdapter iv = new InstructionAdapter(mv);
331    
332                int k = 1;
333                for (FieldInfo fieldInfo : args) {
334                    k = genAssignInstanceFieldFromParam(fieldInfo, k, iv);
335                }
336    
337                iv.load(0, superClassAsmType);
338    
339                if (superClassAsmType.equals(LAMBDA)) {
340                    int arity = funDescriptor.getValueParameters().size();
341                    if (funDescriptor.getExtensionReceiverParameter() != null) arity++;
342                    if (funDescriptor.getDispatchReceiverParameter() != null) arity++;
343                    iv.iconst(arity);
344                    iv.invokespecial(superClassAsmType.getInternalName(), "<init>", "(I)V", false);
345                }
346                else {
347                    iv.invokespecial(superClassAsmType.getInternalName(), "<init>", "()V", false);
348                }
349    
350                iv.visitInsn(RETURN);
351    
352                FunctionCodegen.endVisit(iv, "constructor", element);
353            }
354            return constructor;
355        }
356    
357        @NotNull
358        public static List<FieldInfo> calculateConstructorParameters(
359                @NotNull JetTypeMapper typeMapper,
360                @NotNull CalculatedClosure closure,
361                @NotNull Type ownerType
362        ) {
363            BindingContext bindingContext = typeMapper.getBindingContext();
364            List<FieldInfo> args = Lists.newArrayList();
365            ClassDescriptor captureThis = closure.getCaptureThis();
366            if (captureThis != null) {
367                Type type = typeMapper.mapType(captureThis);
368                args.add(FieldInfo.createForHiddenField(ownerType, type, CAPTURED_THIS_FIELD));
369            }
370            JetType captureReceiverType = closure.getCaptureReceiverType();
371            if (captureReceiverType != null) {
372                args.add(FieldInfo.createForHiddenField(ownerType, typeMapper.mapType(captureReceiverType), CAPTURED_RECEIVER_FIELD));
373            }
374    
375            for (DeclarationDescriptor descriptor : closure.getCaptureVariables().keySet()) {
376                if (descriptor instanceof VariableDescriptor && !(descriptor instanceof PropertyDescriptor)) {
377                    Type sharedVarType = typeMapper.getSharedVarType(descriptor);
378    
379                    Type type = sharedVarType != null
380                                      ? sharedVarType
381                                      : typeMapper.mapType((VariableDescriptor) descriptor);
382                    args.add(FieldInfo.createForHiddenField(ownerType, type, "$" + descriptor.getName().asString()));
383                }
384                else if (DescriptorUtils.isLocalFunction(descriptor)) {
385                    Type classType = asmTypeForAnonymousClass(bindingContext, (FunctionDescriptor) descriptor);
386                    args.add(FieldInfo.createForHiddenField(ownerType, classType, "$" + descriptor.getName().asString()));
387                }
388                else if (descriptor instanceof FunctionDescriptor) {
389                    assert captureReceiverType != null;
390                }
391            }
392            return args;
393        }
394    
395        private static Type[] fieldListToTypeArray(List<FieldInfo> args) {
396            Type[] argTypes = new Type[args.size()];
397            for (int i = 0; i != argTypes.length; ++i) {
398                argTypes[i] = args.get(i).getFieldType();
399            }
400            return argTypes;
401        }
402    
403        @NotNull
404        public static FunctionDescriptor getErasedInvokeFunction(@NotNull FunctionDescriptor elementDescriptor) {
405            int arity = elementDescriptor.getValueParameters().size();
406            ClassDescriptor elementClass = elementDescriptor.getExtensionReceiverParameter() == null
407                                       ? getBuiltIns(elementDescriptor).getFunction(arity)
408                                       : getBuiltIns(elementDescriptor).getExtensionFunction(arity);
409            return elementClass.getDefaultType().getMemberScope().getFunctions(OperatorConventions.INVOKE).iterator().next();
410        }
411    }