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