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