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.inline; 018 019 import com.intellij.openapi.vfs.VirtualFile; 020 import com.intellij.psi.PsiElement; 021 import org.jetbrains.annotations.NotNull; 022 import org.jetbrains.annotations.Nullable; 023 import org.jetbrains.jet.codegen.*; 024 import org.jetbrains.jet.codegen.binding.CodegenBinding; 025 import org.jetbrains.jet.codegen.context.CodegenContext; 026 import org.jetbrains.jet.codegen.context.MethodContext; 027 import org.jetbrains.jet.codegen.context.PackageContext; 028 import org.jetbrains.jet.codegen.state.GenerationState; 029 import org.jetbrains.jet.codegen.state.JetTypeMapper; 030 import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedSimpleFunctionDescriptor; 031 import org.jetbrains.jet.lang.descriptors.*; 032 import org.jetbrains.jet.lang.descriptors.impl.AnonymousFunctionDescriptor; 033 import org.jetbrains.jet.lang.psi.*; 034 import org.jetbrains.jet.lang.resolve.BindingContext; 035 import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils; 036 import org.jetbrains.jet.lang.resolve.DescriptorUtils; 037 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; 038 import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants; 039 import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodParameterKind; 040 import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodParameterSignature; 041 import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodSignature; 042 import org.jetbrains.jet.lang.types.lang.InlineStrategy; 043 import org.jetbrains.jet.lang.types.lang.InlineUtil; 044 import org.jetbrains.jet.renderer.DescriptorRenderer; 045 import org.jetbrains.org.objectweb.asm.MethodVisitor; 046 import org.jetbrains.org.objectweb.asm.Opcodes; 047 import org.jetbrains.org.objectweb.asm.Type; 048 import org.jetbrains.org.objectweb.asm.commons.Method; 049 import org.jetbrains.org.objectweb.asm.tree.MethodNode; 050 051 import java.io.IOException; 052 import java.util.HashMap; 053 import java.util.List; 054 import java.util.ListIterator; 055 import java.util.Map; 056 057 import static org.jetbrains.jet.codegen.AsmUtil.*; 058 import static org.jetbrains.jet.codegen.inline.InlineCodegenUtil.addInlineMarker; 059 060 public class InlineCodegen implements CallGenerator { 061 private final GenerationState state; 062 private final JetTypeMapper typeMapper; 063 private final BindingContext bindingContext; 064 065 private final SimpleFunctionDescriptor functionDescriptor; 066 private final JvmMethodSignature jvmSignature; 067 private final JetElement callElement; 068 private final MethodContext context; 069 private final ExpressionCodegen codegen; 070 071 private final boolean asFunctionInline; 072 private final int initialFrameSize; 073 private final boolean isSameModule; 074 075 protected final ParametersBuilder invocationParamBuilder = ParametersBuilder.newBuilder(); 076 protected final Map<Integer, LambdaInfo> expressionMap = new HashMap<Integer, LambdaInfo>(); 077 078 private LambdaInfo activeLambda; 079 080 public InlineCodegen( 081 @NotNull ExpressionCodegen codegen, 082 @NotNull GenerationState state, 083 @NotNull SimpleFunctionDescriptor functionDescriptor, 084 @NotNull JetElement callElement 085 ) { 086 assert functionDescriptor.getInlineStrategy().isInline() : "InlineCodegen could inline only inline function but " + functionDescriptor; 087 088 this.state = state; 089 this.typeMapper = state.getTypeMapper(); 090 this.codegen = codegen; 091 this.callElement = callElement; 092 this.functionDescriptor = functionDescriptor.getOriginal(); 093 bindingContext = codegen.getBindingContext(); 094 initialFrameSize = codegen.getFrameMap().getCurrentSize(); 095 096 context = (MethodContext) getContext(functionDescriptor, state); 097 jvmSignature = typeMapper.mapSignature(functionDescriptor, context.getContextKind()); 098 099 InlineStrategy inlineStrategy = 100 codegen.getContext().isInlineFunction() ? InlineStrategy.IN_PLACE : functionDescriptor.getInlineStrategy(); 101 this.asFunctionInline = false; 102 103 isSameModule = JvmCodegenUtil.isCallInsideSameModuleAsDeclared(functionDescriptor, codegen.getContext(), state.getOutDirectory()); 104 } 105 106 @Override 107 public void genCallWithoutAssertions( 108 @NotNull CallableMethod callableMethod, @NotNull ExpressionCodegen codegen 109 ) { 110 genCall(callableMethod, null, false, codegen); 111 } 112 113 @Override 114 public void genCall(@NotNull CallableMethod callableMethod, @Nullable ResolvedCall<?> resolvedCall, boolean callDefault, @NotNull ExpressionCodegen codegen) { 115 MethodNode node = null; 116 117 try { 118 node = createMethodNode(callDefault); 119 endCall(inlineCall(node)); 120 } 121 catch (CompilationException e) { 122 throw e; 123 } 124 catch (Exception e) { 125 boolean generateNodeText = !(e instanceof InlineException); 126 PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(this.codegen.getContext().getContextDescriptor()); 127 throw new CompilationException("Couldn't inline method call '" + 128 functionDescriptor.getName() + 129 "' into \n" + (element != null ? element.getText() : "null psi element " + this.codegen.getContext().getContextDescriptor()) + 130 (generateNodeText ? ("\ncause: " + InlineCodegenUtil.getNodeText(node)) : ""), 131 e, callElement); 132 } 133 134 135 } 136 137 private void endCall(@NotNull InlineResult result) { 138 leaveTemps(); 139 140 state.getFactory().removeInlinedClasses(result.getClassesToRemove()); 141 } 142 143 @NotNull 144 private MethodNode createMethodNode(boolean callDefault) throws ClassNotFoundException, IOException { 145 JvmMethodSignature jvmSignature = typeMapper.mapSignature(functionDescriptor, context.getContextKind()); 146 147 Method asmMethod; 148 if (callDefault) { 149 asmMethod = typeMapper.mapDefaultMethod(functionDescriptor, context.getContextKind(), context); 150 } 151 else { 152 asmMethod = jvmSignature.getAsmMethod(); 153 } 154 155 MethodNode node; 156 if (functionDescriptor instanceof DeserializedSimpleFunctionDescriptor) { 157 VirtualFile file = InlineCodegenUtil.getVirtualFileForCallable((DeserializedSimpleFunctionDescriptor) functionDescriptor, state); 158 node = InlineCodegenUtil.getMethodNode(file.contentsToByteArray(), asmMethod.getName(), asmMethod.getDescriptor()); 159 160 if (node == null) { 161 throw new RuntimeException("Couldn't obtain compiled function body for " + descriptorName(functionDescriptor)); 162 } 163 } 164 else { 165 PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor); 166 167 if (element == null) { 168 throw new RuntimeException("Couldn't find declaration for function " + descriptorName(functionDescriptor)); 169 } 170 171 node = new MethodNode(InlineCodegenUtil.API, 172 getMethodAsmFlags(functionDescriptor, context.getContextKind()) | (callDefault ? Opcodes.ACC_STATIC : 0), 173 asmMethod.getName(), 174 asmMethod.getDescriptor(), 175 jvmSignature.getGenericsSignature(), 176 null); 177 178 //for maxLocals calculation 179 MethodVisitor maxCalcAdapter = InlineCodegenUtil.wrapWithMaxLocalCalc(node); 180 MethodContext methodContext = context.getParentContext().intoFunction(functionDescriptor); 181 MemberCodegen<?> parentCodegen = codegen.getParentCodegen(); 182 if (callDefault) { 183 boolean isStatic = AsmUtil.isStaticMethod(context.getContextKind(), functionDescriptor); 184 FunctionCodegen.generateDefaultImplBody( 185 methodContext, jvmSignature, functionDescriptor, isStatic, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT, 186 (JetNamedFunction) element, parentCodegen, state 187 ); 188 } 189 else { 190 FunctionCodegen.generateMethodBody( 191 maxCalcAdapter, functionDescriptor, methodContext, jvmSignature, 192 new FunctionGenerationStrategy.FunctionDefault(state, functionDescriptor, (JetDeclarationWithBody) element), 193 parentCodegen 194 ); 195 } 196 maxCalcAdapter.visitMaxs(-1, -1); 197 maxCalcAdapter.visitEnd(); 198 } 199 return node; 200 } 201 202 private InlineResult inlineCall(MethodNode node) { 203 generateClosuresBodies(); 204 205 //through generation captured parameters will be added to invocationParamBuilder 206 putClosureParametersOnStack(); 207 208 addInlineMarker(codegen.v, true); 209 210 Parameters parameters = invocationParamBuilder.buildParameters(); 211 212 InliningContext info = new RootInliningContext(expressionMap, 213 state, 214 codegen.getInlineNameGenerator() 215 .subGenerator(functionDescriptor.getName().asString()), 216 codegen.getContext(), 217 callElement, 218 codegen.getParentCodegen().getClassName()); 219 220 MethodInliner inliner = new MethodInliner(node, parameters, info, new FieldRemapper(null, null, parameters), isSameModule, "Method inlining " + callElement.getText()); //with captured 221 222 LocalVarRemapper remapper = new LocalVarRemapper(parameters, initialFrameSize); 223 224 225 MethodNode adapter = InlineCodegenUtil.createEmptyMethodNode(); 226 InlineResult result = inliner.doInline(adapter, remapper, true, LabelOwner.SKIP_ALL); 227 228 LabelOwner labelOwner = new LabelOwner() { 229 230 final CallableMemberDescriptor descriptor = codegen.getContext().getContextDescriptor(); 231 232 final boolean isLambda = CodegenBinding.isLocalFunOrLambda(descriptor) && descriptor.getName().isSpecial(); 233 234 @Override 235 public boolean isMyLabel(@NotNull String name) { 236 if (InlineCodegenUtil.ROOT_LABEL.equals(name)) { 237 return !isLambda; 238 } 239 else { 240 return descriptor.getName().asString().equals(name); 241 } 242 } 243 }; 244 List<MethodInliner.ExternalFinallyBlockInfo> infos = MethodInliner.processReturns(adapter, labelOwner, true, null); 245 generateAndInsertFinallyBlocks(adapter, infos); 246 247 adapter.accept(new InliningInstructionAdapter(codegen.v)); 248 249 addInlineMarker(codegen.v, false); 250 251 return result; 252 } 253 254 private void generateClosuresBodies() { 255 for (LambdaInfo info : expressionMap.values()) { 256 info.setNode(generateLambdaBody(info)); 257 } 258 } 259 260 private MethodNode generateLambdaBody(LambdaInfo info) { 261 JetFunctionLiteral declaration = info.getFunctionLiteral(); 262 FunctionDescriptor descriptor = info.getFunctionDescriptor(); 263 264 MethodContext parentContext = codegen.getContext(); 265 266 MethodContext context = parentContext.intoClosure(descriptor, codegen, typeMapper).intoInlinedLambda(descriptor); 267 268 JvmMethodSignature jvmMethodSignature = typeMapper.mapSignature(descriptor); 269 Method asmMethod = jvmMethodSignature.getAsmMethod(); 270 MethodNode methodNode = new MethodNode(InlineCodegenUtil.API, getMethodAsmFlags(descriptor, context.getContextKind()), asmMethod.getName(), asmMethod.getDescriptor(), jvmMethodSignature.getGenericsSignature(), null); 271 272 MethodVisitor adapter = InlineCodegenUtil.wrapWithMaxLocalCalc(methodNode); 273 274 FunctionCodegen.generateMethodBody(adapter, descriptor, context, jvmMethodSignature, new FunctionGenerationStrategy.FunctionDefault(state, descriptor, declaration), codegen.getParentCodegen()); 275 adapter.visitMaxs(-1, -1); 276 277 return methodNode; 278 } 279 280 281 282 @Override 283 public void afterParameterPut(@NotNull Type type, @Nullable StackValue stackValue, @Nullable ValueParameterDescriptor valueParameterDescriptor) { 284 putCapturedInLocal(type, stackValue, valueParameterDescriptor, -1); 285 } 286 287 private void putCapturedInLocal( 288 @NotNull Type type, @Nullable StackValue stackValue, @Nullable ValueParameterDescriptor valueParameterDescriptor, int capturedParamIndex 289 ) { 290 if (!asFunctionInline && Type.VOID_TYPE != type) { 291 //TODO remap only inlinable closure => otherwise we could get a lot of problem 292 boolean couldBeRemapped = !shouldPutValue(type, stackValue, valueParameterDescriptor); 293 StackValue remappedIndex = couldBeRemapped ? stackValue : null; 294 295 ParameterInfo info; 296 if (capturedParamIndex >= 0) { 297 CapturedParamDesc capturedParamInfoInLambda = activeLambda.getCapturedVars().get(capturedParamIndex); 298 info = invocationParamBuilder.addCapturedParam(capturedParamInfoInLambda, capturedParamInfoInLambda.getFieldName()); 299 info.setRemapValue(remappedIndex); 300 } 301 else { 302 info = invocationParamBuilder.addNextParameter(type, false, remappedIndex); 303 } 304 305 putParameterOnStack(info); 306 } 307 } 308 309 /*descriptor is null for captured vars*/ 310 public boolean shouldPutValue( 311 @NotNull Type type, 312 @Nullable StackValue stackValue, 313 @Nullable ValueParameterDescriptor descriptor 314 ) { 315 316 if (stackValue == null) { 317 //default or vararg 318 return true; 319 } 320 321 //remap only inline functions (and maybe non primitives) 322 //TODO - clean asserion and remapping logic 323 if (isPrimitive(type) != isPrimitive(stackValue.type)) { 324 //don't remap boxing/unboxing primitives - lost identity and perfomance 325 return true; 326 } 327 328 if (stackValue instanceof StackValue.Local) { 329 return false; 330 } 331 332 if (stackValue instanceof StackValue.Composed) { 333 //see: Method.isSpecialStackValue: go through aload 0 334 if (codegen.getContext().isInliningLambda() && codegen.getContext().getContextDescriptor() instanceof AnonymousFunctionDescriptor) { 335 if (descriptor != null && !InlineUtil.hasNoinlineAnnotation(descriptor)) { 336 //TODO: check type of context 337 return false; 338 } 339 } 340 } 341 return true; 342 } 343 344 private void putParameterOnStack(ParameterInfo... infos) { 345 int[] index = new int[infos.length]; 346 for (int i = 0; i < infos.length; i++) { 347 ParameterInfo info = infos[i]; 348 if (!info.isSkippedOrRemapped()) { 349 index[i] = codegen.getFrameMap().enterTemp(info.getType()); 350 } 351 else { 352 index[i] = -1; 353 } 354 } 355 356 for (int i = infos.length - 1; i >= 0; i--) { 357 ParameterInfo info = infos[i]; 358 if (!info.isSkippedOrRemapped()) { 359 Type type = info.type; 360 StackValue.local(index[i], type).store(type, codegen.v); 361 } 362 } 363 } 364 365 @Override 366 public void putHiddenParams() { 367 List<JvmMethodParameterSignature> valueParameters = jvmSignature.getValueParameters(); 368 369 if (!isStaticMethod(functionDescriptor, context)) { 370 invocationParamBuilder.addNextParameter(AsmTypeConstants.OBJECT_TYPE, false, null); 371 } 372 373 for (JvmMethodParameterSignature param : valueParameters) { 374 if (param.getKind() == JvmMethodParameterKind.VALUE) { 375 break; 376 } 377 invocationParamBuilder.addNextParameter(param.getAsmType(), false, null); 378 } 379 380 List<ParameterInfo> infos = invocationParamBuilder.listNotCaptured(); 381 putParameterOnStack(infos.toArray(new ParameterInfo[infos.size()])); 382 } 383 384 public void leaveTemps() { 385 FrameMap frameMap = codegen.getFrameMap(); 386 List<ParameterInfo> infos = invocationParamBuilder.listAllParams(); 387 for (ListIterator<? extends ParameterInfo> iterator = infos.listIterator(infos.size()); iterator.hasPrevious(); ) { 388 ParameterInfo param = iterator.previous(); 389 if (!param.isSkippedOrRemapped()) { 390 frameMap.leaveTemp(param.type); 391 } 392 } 393 } 394 395 public static boolean isInliningClosure(JetExpression expression, ValueParameterDescriptor valueParameterDescriptora) { 396 //TODO deparenthisise typed 397 JetExpression deparenthesize = JetPsiUtil.deparenthesize(expression); 398 return deparenthesize instanceof JetFunctionLiteralExpression && 399 !InlineUtil.hasNoinlineAnnotation(valueParameterDescriptora); 400 } 401 402 public void rememberClosure(JetExpression expression, Type type) { 403 JetFunctionLiteralExpression lambda = (JetFunctionLiteralExpression) JetPsiUtil.deparenthesize(expression); 404 assert lambda != null : "Couldn't find lambda in " + expression.getText(); 405 406 String labelNameIfPresent = null; 407 PsiElement parent = lambda.getParent(); 408 if (parent instanceof JetLabeledExpression) { 409 labelNameIfPresent = ((JetLabeledExpression) parent).getLabelName(); 410 } 411 LambdaInfo info = new LambdaInfo(lambda, typeMapper, labelNameIfPresent); 412 413 ParameterInfo closureInfo = invocationParamBuilder.addNextParameter(type, true, null); 414 closureInfo.setLambda(info); 415 expressionMap.put(closureInfo.getIndex(), info); 416 } 417 418 private void putClosureParametersOnStack() { 419 for (LambdaInfo next : expressionMap.values()) { 420 activeLambda = next; 421 codegen.pushClosureOnStack(next.closure, false, this); 422 } 423 activeLambda = null; 424 } 425 426 public static CodegenContext getContext(DeclarationDescriptor descriptor, GenerationState state) { 427 if (descriptor instanceof PackageFragmentDescriptor) { 428 return new PackageContext((PackageFragmentDescriptor) descriptor, null, null); 429 } 430 431 CodegenContext parent = getContext(descriptor.getContainingDeclaration(), state); 432 433 if (descriptor instanceof ClassDescriptor) { 434 OwnerKind kind = DescriptorUtils.isTrait(descriptor) ? OwnerKind.TRAIT_IMPL : OwnerKind.IMPLEMENTATION; 435 return parent.intoClass((ClassDescriptor) descriptor, kind, state); 436 } 437 else if (descriptor instanceof FunctionDescriptor) { 438 return parent.intoFunction((FunctionDescriptor) descriptor); 439 } 440 441 throw new IllegalStateException("Couldn't build context for " + descriptorName(descriptor)); 442 } 443 444 private static boolean isStaticMethod(FunctionDescriptor functionDescriptor, MethodContext context) { 445 return (getMethodAsmFlags(functionDescriptor, context.getContextKind()) & Opcodes.ACC_STATIC) != 0; 446 } 447 448 private static String descriptorName(DeclarationDescriptor descriptor) { 449 return DescriptorRenderer.SHORT_NAMES_IN_TYPES.render(descriptor); 450 } 451 452 @Override 453 public void genValueAndPut( 454 @NotNull ValueParameterDescriptor valueParameterDescriptor, 455 @NotNull JetExpression argumentExpression, 456 @NotNull Type parameterType 457 ) { 458 //TODO deparenthisise 459 if (isInliningClosure(argumentExpression, valueParameterDescriptor)) { 460 rememberClosure(argumentExpression, parameterType); 461 } else { 462 StackValue value = codegen.gen(argumentExpression); 463 putValueIfNeeded(valueParameterDescriptor, parameterType, value); 464 } 465 } 466 467 @Override 468 public void putValueIfNeeded(@Nullable ValueParameterDescriptor valueParameterDescriptor, @NotNull Type parameterType, @NotNull StackValue value) { 469 if (shouldPutValue(parameterType, value, valueParameterDescriptor)) { 470 value.put(parameterType, codegen.v); 471 } 472 afterParameterPut(parameterType, value, valueParameterDescriptor); 473 } 474 475 @Override 476 public void putCapturedValueOnStack( 477 @NotNull StackValue stackValue, @NotNull Type valueType, int paramIndex 478 ) { 479 if (shouldPutValue(stackValue.type, stackValue, null)) { 480 stackValue.put(stackValue.type, codegen.v); 481 } 482 putCapturedInLocal(stackValue.type, stackValue, null, paramIndex); 483 } 484 485 486 public void generateAndInsertFinallyBlocks(MethodNode intoNode, List<MethodInliner.ExternalFinallyBlockInfo> insertPoints) { 487 if (!codegen.hasFinallyBlocks()) return; 488 489 for (MethodInliner.ExternalFinallyBlockInfo insertPoint : insertPoints) { 490 MethodNode finallyNode = InlineCodegenUtil.createEmptyMethodNode(); 491 ExpressionCodegen finallyCodegen = 492 new ExpressionCodegen(finallyNode, codegen.getFrameMap(), codegen.getReturnType(), 493 codegen.getContext(), codegen.getState(), codegen.getParentCodegen()); 494 finallyCodegen.addBlockStackElementsForNonLocalReturns(codegen.getBlockStackElements()); 495 496 finallyCodegen.generateFinallyBlocksIfNeeded(insertPoint.returnType); 497 498 InlineCodegenUtil.insertNodeBefore(finallyNode, intoNode, insertPoint.beforeIns); 499 } 500 } 501 502 }