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