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