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