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