001    /*
002     * Copyright 2010-2013 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.jet.codegen;
018    
019    import com.google.common.collect.Lists;
020    import com.intellij.psi.PsiElement;
021    import com.intellij.util.ArrayUtil;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.jet.codegen.binding.CalculatedClosure;
025    import org.jetbrains.jet.codegen.context.CodegenContext;
026    import org.jetbrains.jet.codegen.context.LocalLookup;
027    import org.jetbrains.jet.codegen.signature.BothSignatureWriter;
028    import org.jetbrains.jet.codegen.state.GenerationState;
029    import org.jetbrains.jet.codegen.state.JetTypeMapper;
030    import org.jetbrains.jet.lang.descriptors.*;
031    import org.jetbrains.jet.lang.resolve.BindingContext;
032    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
033    import org.jetbrains.jet.lang.resolve.java.JvmAbi;
034    import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodSignature;
035    import org.jetbrains.jet.lang.resolve.name.Name;
036    import org.jetbrains.jet.lang.types.JetType;
037    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
038    import org.jetbrains.org.objectweb.asm.MethodVisitor;
039    import org.jetbrains.org.objectweb.asm.Type;
040    import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
041    import org.jetbrains.org.objectweb.asm.commons.Method;
042    
043    import java.util.ArrayList;
044    import java.util.Collections;
045    import java.util.List;
046    
047    import static org.jetbrains.jet.codegen.AsmUtil.*;
048    import static org.jetbrains.jet.codegen.JvmCodegenUtil.isConst;
049    import static org.jetbrains.jet.codegen.binding.CodegenBinding.*;
050    import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.KotlinSyntheticClass;
051    import static org.jetbrains.org.objectweb.asm.Opcodes.*;
052    
053    public class ClosureCodegen extends ParentCodegenAware {
054        private final PsiElement fun;
055        private final FunctionDescriptor funDescriptor;
056        private final SamType samType;
057        private final JetType superClassType;
058        private final List<JetType> superInterfaceTypes;
059        private final CodegenContext context;
060        private final FunctionGenerationStrategy strategy;
061        private final CalculatedClosure closure;
062        private final Type asmType;
063        private final int visibilityFlag;
064        private final KotlinSyntheticClass.Kind syntheticClassKind;
065    
066        private Method constructor;
067    
068        public ClosureCodegen(
069                @NotNull GenerationState state,
070                @NotNull PsiElement fun,
071                @NotNull FunctionDescriptor funDescriptor,
072                @Nullable SamType samType,
073                @NotNull CodegenContext parentContext,
074                @NotNull KotlinSyntheticClass.Kind syntheticClassKind,
075                @NotNull LocalLookup localLookup,
076                @NotNull FunctionGenerationStrategy strategy,
077                @Nullable MemberCodegen<?> parentCodegen
078        ) {
079            super(state, parentCodegen);
080    
081            this.fun = fun;
082            this.funDescriptor = funDescriptor;
083            this.samType = samType;
084            this.context = parentContext.intoClosure(funDescriptor, localLookup, typeMapper);
085            this.syntheticClassKind = syntheticClassKind;
086            this.strategy = strategy;
087    
088            ClassDescriptor classDescriptor = anonymousClassForFunction(bindingContext, funDescriptor);
089    
090            if (samType == null) {
091                this.superInterfaceTypes = new ArrayList<JetType>();
092    
093                JetType superClassType = null;
094                for (JetType supertype : classDescriptor.getTypeConstructor().getSupertypes()) {
095                    ClassifierDescriptor classifier = supertype.getConstructor().getDeclarationDescriptor();
096                    if (DescriptorUtils.isTrait(classifier)) {
097                        superInterfaceTypes.add(supertype);
098                    }
099                    else {
100                        assert superClassType == null : "Closure class can't have more than one superclass: " + funDescriptor;
101                        superClassType = supertype;
102                    }
103                }
104                assert superClassType != null : "Closure class should have a superclass: " + funDescriptor;
105    
106                this.superClassType = superClassType;
107            }
108            else {
109                this.superInterfaceTypes = Collections.singletonList(samType.getType());
110                this.superClassType = KotlinBuiltIns.getInstance().getAnyType();
111            }
112    
113            this.closure = bindingContext.get(CLOSURE, classDescriptor);
114            assert closure != null : "Closure must be calculated for class: " + classDescriptor;
115    
116            this.asmType = asmTypeForAnonymousClass(bindingContext, funDescriptor);
117    
118            visibilityFlag = AsmUtil.getVisibilityAccessFlagForAnonymous(classDescriptor);
119        }
120    
121        public void gen() {
122            ClassBuilder cv = state.getFactory().newVisitor(asmType, fun.getContainingFile());
123    
124            FunctionDescriptor erasedInterfaceFunction;
125            if (samType == null) {
126                erasedInterfaceFunction = getErasedInvokeFunction(funDescriptor);
127            }
128            else {
129                erasedInterfaceFunction = samType.getAbstractMethod().getOriginal();
130            }
131    
132            BothSignatureWriter sw = new BothSignatureWriter(BothSignatureWriter.Mode.CLASS);
133            if (samType != null) {
134                typeMapper.writeFormalTypeParameters(samType.getType().getConstructor().getParameters(), sw);
135            }
136            sw.writeSuperclass();
137            Type superClassAsmType = typeMapper.mapSupertype(superClassType, sw);
138            sw.writeSuperclassEnd();
139            String[] superInterfaceAsmTypes = new String[superInterfaceTypes.size()];
140            for (int i = 0; i < superInterfaceTypes.size(); i++) {
141                JetType superInterfaceType = superInterfaceTypes.get(i);
142                sw.writeInterface();
143                superInterfaceAsmTypes[i] = typeMapper.mapSupertype(superInterfaceType, sw).getInternalName();
144                sw.writeInterfaceEnd();
145            }
146    
147            cv.defineClass(fun,
148                           V1_6,
149                           ACC_FINAL | ACC_SUPER | visibilityFlag,
150                           asmType.getInternalName(),
151                           sw.makeJavaGenericSignature(),
152                           superClassAsmType.getInternalName(),
153                           superInterfaceAsmTypes
154            );
155            cv.visitSource(fun.getContainingFile().getName(), null);
156    
157            writeKotlinSyntheticClassAnnotation(cv, syntheticClassKind);
158    
159            JvmMethodSignature jvmMethodSignature =
160                    typeMapper.mapSignature(funDescriptor).replaceName(erasedInterfaceFunction.getName().toString());
161            generateBridge(cv, typeMapper.mapSignature(erasedInterfaceFunction).getAsmMethod(), jvmMethodSignature.getAsmMethod());
162    
163            FunctionCodegen fc = new FunctionCodegen(context, cv, state, getParentCodegen());
164            fc.generateMethod(fun, jvmMethodSignature, funDescriptor, strategy);
165    
166            this.constructor = generateConstructor(cv, superClassAsmType);
167    
168            if (isConst(closure)) {
169                generateConstInstance(cv);
170            }
171    
172            genClosureFields(closure, cv, typeMapper);
173    
174            fc.generateDefaultIfNeeded(context.intoFunction(funDescriptor),
175                                       typeMapper.mapSignature(funDescriptor),
176                                       funDescriptor,
177                                       context.getContextKind(),
178                                       DefaultParameterValueLoader.DEFAULT,
179                                       null);
180    
181    
182            AsmUtil.writeOuterClassAndEnclosingMethod(anonymousClassForFunction(bindingContext, funDescriptor), funDescriptor, typeMapper, cv);
183            cv.done();
184        }
185    
186        @NotNull
187        public StackValue putInstanceOnStack(@NotNull InstructionAdapter v, @NotNull ExpressionCodegen codegen) {
188            if (isConst(closure)) {
189                v.getstatic(asmType.getInternalName(), JvmAbi.INSTANCE_FIELD, asmType.getDescriptor());
190            }
191            else {
192                v.anew(asmType);
193                v.dup();
194    
195                codegen.pushClosureOnStack(closure, false, codegen.defaultCallGenerator);
196                v.invokespecial(asmType.getInternalName(), "<init>", constructor.getDescriptor());
197            }
198            return StackValue.onStack(asmType);
199        }
200    
201    
202        private void generateConstInstance(@NotNull ClassBuilder cv) {
203            MethodVisitor mv = cv.newMethod(fun, ACC_STATIC | ACC_SYNTHETIC, "<clinit>", "()V", null, ArrayUtil.EMPTY_STRING_ARRAY);
204            InstructionAdapter iv = new InstructionAdapter(mv);
205    
206            cv.newField(fun, ACC_STATIC | ACC_FINAL, JvmAbi.INSTANCE_FIELD, asmType.getDescriptor(), null, null);
207    
208            if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
209                mv.visitCode();
210                iv.anew(asmType);
211                iv.dup();
212                iv.invokespecial(asmType.getInternalName(), "<init>", "()V");
213                iv.putstatic(asmType.getInternalName(), JvmAbi.INSTANCE_FIELD, asmType.getDescriptor());
214                mv.visitInsn(RETURN);
215                FunctionCodegen.endVisit(mv, "<clinit>", fun);
216            }
217        }
218    
219        private void generateBridge(@NotNull ClassBuilder cv, @NotNull Method bridge, @NotNull Method delegate) {
220            if (bridge.equals(delegate)) return;
221    
222            MethodVisitor mv =
223                    cv.newMethod(fun, ACC_PUBLIC | ACC_BRIDGE, bridge.getName(), bridge.getDescriptor(), null, ArrayUtil.EMPTY_STRING_ARRAY);
224    
225            if (state.getClassBuilderMode() != ClassBuilderMode.FULL) return;
226    
227            mv.visitCode();
228    
229            InstructionAdapter iv = new InstructionAdapter(mv);
230            iv.load(0, asmType);
231    
232            ReceiverParameterDescriptor receiver = funDescriptor.getReceiverParameter();
233            int count = 1;
234            if (receiver != null) {
235                StackValue.local(count, bridge.getArgumentTypes()[count - 1]).put(typeMapper.mapType(receiver.getType()), iv);
236                count++;
237            }
238    
239            List<ValueParameterDescriptor> params = funDescriptor.getValueParameters();
240            for (ValueParameterDescriptor param : params) {
241                StackValue.local(count, bridge.getArgumentTypes()[count - 1]).put(typeMapper.mapType(param.getType()), iv);
242                count++;
243            }
244    
245            iv.invokevirtual(asmType.getInternalName(), delegate.getName(), delegate.getDescriptor());
246            StackValue.onStack(delegate.getReturnType()).put(bridge.getReturnType(), iv);
247    
248            iv.areturn(bridge.getReturnType());
249    
250            FunctionCodegen.endVisit(mv, "bridge", fun);
251        }
252    
253        @NotNull
254        private Method generateConstructor(@NotNull ClassBuilder cv, @NotNull Type superClassAsmType) {
255            List<FieldInfo> args = calculateConstructorParameters(typeMapper, closure, asmType);
256    
257            return generateConstructor(cv, args, fun, superClassAsmType, state, visibilityFlag);
258        }
259    
260        public static Method generateConstructor(
261                @NotNull ClassBuilder cv,
262                @NotNull List<FieldInfo> args,
263                @Nullable PsiElement fun,
264                @NotNull Type superClass,
265                @NotNull GenerationState state,
266                int flags
267        ) {
268            Type[] argTypes = fieldListToTypeArray(args);
269    
270            Method constructor = new Method("<init>", Type.VOID_TYPE, argTypes);
271            MethodVisitor mv = cv.newMethod(fun, flags, "<init>", constructor.getDescriptor(), null,
272                                            ArrayUtil.EMPTY_STRING_ARRAY);
273            if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
274                mv.visitCode();
275                InstructionAdapter iv = new InstructionAdapter(mv);
276    
277                int k = 1;
278                for (FieldInfo fieldInfo : args) {
279                    k = AsmUtil.genAssignInstanceFieldFromParam(fieldInfo, k, iv);
280                }
281    
282                iv.load(0, superClass);
283                iv.invokespecial(superClass.getInternalName(), "<init>", "()V");
284    
285                iv.visitInsn(RETURN);
286    
287                FunctionCodegen.endVisit(iv, "constructor", fun);
288            }
289            return constructor;
290        }
291    
292        @NotNull
293        public static List<FieldInfo> calculateConstructorParameters(
294                @NotNull JetTypeMapper typeMapper,
295                @NotNull CalculatedClosure closure,
296                @NotNull Type ownerType
297        ) {
298            BindingContext bindingContext = typeMapper.getBindingContext();
299            List<FieldInfo> args = Lists.newArrayList();
300            ClassDescriptor captureThis = closure.getCaptureThis();
301            if (captureThis != null) {
302                Type type = typeMapper.mapType(captureThis);
303                args.add(FieldInfo.createForHiddenField(ownerType, type, CAPTURED_THIS_FIELD));
304            }
305            JetType captureReceiverType = closure.getCaptureReceiverType();
306            if (captureReceiverType != null) {
307                args.add(FieldInfo.createForHiddenField(ownerType, typeMapper.mapType(captureReceiverType), CAPTURED_RECEIVER_FIELD));
308            }
309    
310            for (DeclarationDescriptor descriptor : closure.getCaptureVariables().keySet()) {
311                if (descriptor instanceof VariableDescriptor && !(descriptor instanceof PropertyDescriptor)) {
312                    Type sharedVarType = typeMapper.getSharedVarType(descriptor);
313    
314                    Type type = sharedVarType != null
315                                      ? sharedVarType
316                                      : typeMapper.mapType((VariableDescriptor) descriptor);
317                    args.add(FieldInfo.createForHiddenField(ownerType, type, "$" + descriptor.getName().asString()));
318                }
319                else if (isLocalNamedFun(descriptor)) {
320                    Type classType = asmTypeForAnonymousClass(bindingContext, (FunctionDescriptor) descriptor);
321                    args.add(FieldInfo.createForHiddenField(ownerType, classType, "$" + descriptor.getName().asString()));
322                }
323                else if (descriptor instanceof FunctionDescriptor) {
324                    assert captureReceiverType != null;
325                }
326            }
327            return args;
328        }
329    
330        private static Type[] fieldListToTypeArray(List<FieldInfo> args) {
331            Type[] argTypes = new Type[args.size()];
332            for (int i = 0; i != argTypes.length; ++i) {
333                argTypes[i] = args.get(i).getFieldType();
334            }
335            return argTypes;
336        }
337    
338        @NotNull
339        public static FunctionDescriptor getErasedInvokeFunction(@NotNull FunctionDescriptor funDescriptor) {
340            int arity = funDescriptor.getValueParameters().size();
341            ClassDescriptor funClass = funDescriptor.getReceiverParameter() == null
342                                       ? KotlinBuiltIns.getInstance().getFunction(arity)
343                                       : KotlinBuiltIns.getInstance().getExtensionFunction(arity);
344            return funClass.getDefaultType().getMemberScope().getFunctions(Name.identifier("invoke")).iterator().next();
345        }
346    }