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.DescriptorToSourceUtils; 035 import org.jetbrains.jet.lang.resolve.DescriptorUtils; 036 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; 037 import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants; 038 import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodParameterKind; 039 import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodParameterSignature; 040 import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodSignature; 041 import org.jetbrains.jet.lang.types.lang.InlineStrategy; 042 import org.jetbrains.jet.lang.types.lang.InlineUtil; 043 import org.jetbrains.jet.renderer.DescriptorRenderer; 044 import org.jetbrains.org.objectweb.asm.MethodVisitor; 045 import org.jetbrains.org.objectweb.asm.Opcodes; 046 import org.jetbrains.org.objectweb.asm.Type; 047 import org.jetbrains.org.objectweb.asm.commons.Method; 048 import org.jetbrains.org.objectweb.asm.tree.MethodNode; 049 050 import java.io.IOException; 051 import java.util.HashMap; 052 import java.util.List; 053 import java.util.ListIterator; 054 import java.util.Map; 055 056 import static org.jetbrains.jet.codegen.AsmUtil.getMethodAsmFlags; 057 import static org.jetbrains.jet.codegen.AsmUtil.isPrimitive; 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 064 private final SimpleFunctionDescriptor functionDescriptor; 065 private final JvmMethodSignature jvmSignature; 066 private final JetElement callElement; 067 private final MethodContext context; 068 private final ExpressionCodegen codegen; 069 070 private final boolean asFunctionInline; 071 private final int initialFrameSize; 072 private final boolean isSameModule; 073 074 protected final ParametersBuilder invocationParamBuilder = ParametersBuilder.newBuilder(); 075 protected final Map<Integer, LambdaInfo> expressionMap = new HashMap<Integer, LambdaInfo>(); 076 077 private LambdaInfo activeLambda; 078 079 public InlineCodegen( 080 @NotNull ExpressionCodegen codegen, 081 @NotNull GenerationState state, 082 @NotNull SimpleFunctionDescriptor functionDescriptor, 083 @NotNull JetElement callElement 084 ) { 085 assert functionDescriptor.getInlineStrategy().isInline() : "InlineCodegen could inline only inline function but " + functionDescriptor; 086 087 this.state = state; 088 this.typeMapper = state.getTypeMapper(); 089 this.codegen = codegen; 090 this.callElement = callElement; 091 this.functionDescriptor = functionDescriptor.getOriginal(); 092 initialFrameSize = codegen.getFrameMap().getCurrentSize(); 093 094 context = (MethodContext) getContext(functionDescriptor, state); 095 jvmSignature = typeMapper.mapSignature(functionDescriptor, context.getContextKind()); 096 097 InlineStrategy inlineStrategy = 098 codegen.getContext().isInlineFunction() ? InlineStrategy.IN_PLACE : functionDescriptor.getInlineStrategy(); 099 this.asFunctionInline = false; 100 101 isSameModule = JvmCodegenUtil.isCallInsideSameModuleAsDeclared(functionDescriptor, codegen.getContext(), state.getOutDirectory()); 102 } 103 104 @Override 105 public void genCallWithoutAssertions( 106 @NotNull CallableMethod callableMethod, @NotNull ExpressionCodegen codegen 107 ) { 108 genCall(callableMethod, null, false, codegen); 109 } 110 111 @Override 112 public void genCall(@NotNull CallableMethod callableMethod, @Nullable ResolvedCall<?> resolvedCall, boolean callDefault, @NotNull ExpressionCodegen codegen) { 113 MethodNode node = null; 114 115 try { 116 node = createMethodNode(callDefault); 117 endCall(inlineCall(node)); 118 } 119 catch (CompilationException e) { 120 throw e; 121 } 122 catch (Exception e) { 123 boolean generateNodeText = !(e instanceof InlineException); 124 PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(this.codegen.getContext().getContextDescriptor()); 125 throw new CompilationException("Couldn't inline method call '" + 126 functionDescriptor.getName() + 127 "' into \n" + (element != null ? element.getText() : "null psi element " + this.codegen.getContext().getContextDescriptor()) + 128 (generateNodeText ? ("\ncause: " + InlineCodegenUtil.getNodeText(node)) : ""), 129 e, callElement); 130 } 131 132 133 } 134 135 private void endCall(@NotNull InlineResult result) { 136 leaveTemps(); 137 138 state.getFactory().removeInlinedClasses(result.getClassesToRemove()); 139 } 140 141 @NotNull 142 private MethodNode createMethodNode(boolean callDefault) throws ClassNotFoundException, IOException { 143 JvmMethodSignature jvmSignature = typeMapper.mapSignature(functionDescriptor, context.getContextKind()); 144 145 Method asmMethod; 146 if (callDefault) { 147 asmMethod = typeMapper.mapDefaultMethod(functionDescriptor, context.getContextKind(), context); 148 } 149 else { 150 asmMethod = jvmSignature.getAsmMethod(); 151 } 152 153 MethodNode node; 154 if (functionDescriptor instanceof DeserializedSimpleFunctionDescriptor) { 155 VirtualFile file = InlineCodegenUtil.getVirtualFileForCallable((DeserializedSimpleFunctionDescriptor) functionDescriptor, state); 156 node = InlineCodegenUtil.getMethodNode(file.contentsToByteArray(), asmMethod.getName(), asmMethod.getDescriptor()); 157 158 if (node == null) { 159 throw new RuntimeException("Couldn't obtain compiled function body for " + descriptorName(functionDescriptor)); 160 } 161 } 162 else { 163 PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor); 164 165 if (element == null) { 166 throw new RuntimeException("Couldn't find declaration for function " + descriptorName(functionDescriptor)); 167 } 168 169 node = new MethodNode(InlineCodegenUtil.API, 170 getMethodAsmFlags(functionDescriptor, context.getContextKind()) | (callDefault ? Opcodes.ACC_STATIC : 0), 171 asmMethod.getName(), 172 asmMethod.getDescriptor(), 173 jvmSignature.getGenericsSignature(), 174 null); 175 176 //for maxLocals calculation 177 MethodVisitor maxCalcAdapter = InlineCodegenUtil.wrapWithMaxLocalCalc(node); 178 MethodContext methodContext = context.getParentContext().intoFunction(functionDescriptor); 179 MemberCodegen<?> parentCodegen = codegen.getParentCodegen(); 180 if (callDefault) { 181 boolean isStatic = AsmUtil.isStaticMethod(context.getContextKind(), functionDescriptor); 182 FunctionCodegen.generateDefaultImplBody( 183 methodContext, jvmSignature, functionDescriptor, isStatic, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT, 184 (JetNamedFunction) element, parentCodegen, state 185 ); 186 } 187 else { 188 FunctionCodegen.generateMethodBody( 189 maxCalcAdapter, functionDescriptor, methodContext, jvmSignature, 190 new FunctionGenerationStrategy.FunctionDefault(state, functionDescriptor, (JetDeclarationWithBody) element), 191 parentCodegen 192 ); 193 } 194 maxCalcAdapter.visitMaxs(-1, -1); 195 maxCalcAdapter.visitEnd(); 196 } 197 return node; 198 } 199 200 private InlineResult inlineCall(MethodNode node) { 201 generateClosuresBodies(); 202 203 //through generation captured parameters will be added to invocationParamBuilder 204 putClosureParametersOnStack(); 205 206 addInlineMarker(codegen.v, true); 207 208 Parameters parameters = invocationParamBuilder.buildParameters(); 209 210 InliningContext info = new RootInliningContext(expressionMap, 211 state, 212 codegen.getInlineNameGenerator() 213 .subGenerator(functionDescriptor.getName().asString()), 214 codegen.getContext(), 215 callElement, 216 codegen.getParentCodegen().getClassName()); 217 218 MethodInliner inliner = new MethodInliner(node, parameters, info, new FieldRemapper(null, null, parameters), isSameModule, "Method inlining " + callElement.getText()); //with captured 219 220 LocalVarRemapper remapper = new LocalVarRemapper(parameters, initialFrameSize); 221 222 223 MethodNode adapter = InlineCodegenUtil.createEmptyMethodNode(); 224 InlineResult result = inliner.doInline(adapter, remapper, true, LabelOwner.SKIP_ALL); 225 226 LabelOwner labelOwner = new LabelOwner() { 227 228 final CallableMemberDescriptor descriptor = codegen.getContext().getContextDescriptor(); 229 230 final boolean isLambda = CodegenBinding.isLocalFunOrLambda(descriptor) && descriptor.getName().isSpecial(); 231 232 @Override 233 public boolean isMyLabel(@NotNull String name) { 234 if (InlineCodegenUtil.ROOT_LABEL.equals(name)) { 235 return !isLambda; 236 } 237 else { 238 return descriptor.getName().asString().equals(name); 239 } 240 } 241 }; 242 List<MethodInliner.ExternalFinallyBlockInfo> infos = MethodInliner.processReturns(adapter, labelOwner, true, null); 243 generateAndInsertFinallyBlocks(adapter, infos); 244 245 adapter.accept(new InliningInstructionAdapter(codegen.v)); 246 247 addInlineMarker(codegen.v, false); 248 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.getClassDescriptor(), true, 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 private static String descriptorName(DeclarationDescriptor descriptor) { 447 return DescriptorRenderer.SHORT_NAMES_IN_TYPES.render(descriptor); 448 } 449 450 @Override 451 public void genValueAndPut( 452 @NotNull ValueParameterDescriptor valueParameterDescriptor, 453 @NotNull JetExpression argumentExpression, 454 @NotNull Type parameterType 455 ) { 456 //TODO deparenthisise 457 if (isInliningClosure(argumentExpression, valueParameterDescriptor)) { 458 rememberClosure(argumentExpression, parameterType); 459 } else { 460 StackValue value = codegen.gen(argumentExpression); 461 putValueIfNeeded(valueParameterDescriptor, parameterType, value); 462 } 463 } 464 465 @Override 466 public void putValueIfNeeded(@Nullable ValueParameterDescriptor valueParameterDescriptor, @NotNull Type parameterType, @NotNull StackValue value) { 467 if (shouldPutValue(parameterType, value, valueParameterDescriptor)) { 468 value.put(parameterType, codegen.v); 469 } 470 afterParameterPut(parameterType, value, valueParameterDescriptor); 471 } 472 473 @Override 474 public void putCapturedValueOnStack( 475 @NotNull StackValue stackValue, @NotNull Type valueType, int paramIndex 476 ) { 477 if (shouldPutValue(stackValue.type, stackValue, null)) { 478 stackValue.put(stackValue.type, codegen.v); 479 } 480 putCapturedInLocal(stackValue.type, stackValue, null, paramIndex); 481 } 482 483 484 public void generateAndInsertFinallyBlocks(MethodNode intoNode, List<MethodInliner.ExternalFinallyBlockInfo> insertPoints) { 485 if (!codegen.hasFinallyBlocks()) return; 486 487 for (MethodInliner.ExternalFinallyBlockInfo insertPoint : insertPoints) { 488 MethodNode finallyNode = InlineCodegenUtil.createEmptyMethodNode(); 489 ExpressionCodegen finallyCodegen = 490 new ExpressionCodegen(finallyNode, codegen.getFrameMap(), codegen.getReturnType(), 491 codegen.getContext(), codegen.getState(), codegen.getParentCodegen()); 492 finallyCodegen.addBlockStackElementsForNonLocalReturns(codegen.getBlockStackElements()); 493 494 finallyCodegen.generateFinallyBlocksIfNeeded(insertPoint.returnType); 495 496 InlineCodegenUtil.insertNodeBefore(finallyNode, intoNode, insertPoint.beforeIns); 497 } 498 } 499 500 }