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