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