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 }