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