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