001 /*
002 * Copyright 2010-2015 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.kotlin.codegen.inline;
018
019 import com.intellij.openapi.vfs.VirtualFile;
020 import com.intellij.psi.PsiElement;
021 import com.intellij.psi.PsiFile;
022 import org.jetbrains.annotations.NotNull;
023 import org.jetbrains.annotations.Nullable;
024 import org.jetbrains.kotlin.backend.common.CodegenUtil;
025 import org.jetbrains.kotlin.codegen.*;
026 import org.jetbrains.kotlin.codegen.context.CodegenContext;
027 import org.jetbrains.kotlin.codegen.context.FieldOwnerContext;
028 import org.jetbrains.kotlin.codegen.context.MethodContext;
029 import org.jetbrains.kotlin.codegen.context.PackageContext;
030 import org.jetbrains.kotlin.codegen.state.GenerationState;
031 import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
032 import org.jetbrains.kotlin.descriptors.*;
033 import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache;
034 import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents;
035 import org.jetbrains.kotlin.modules.TargetId;
036 import org.jetbrains.kotlin.name.ClassId;
037 import org.jetbrains.kotlin.name.Name;
038 import org.jetbrains.kotlin.psi.*;
039 import org.jetbrains.kotlin.renderer.DescriptorRenderer;
040 import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
041 import org.jetbrains.kotlin.resolve.DescriptorUtils;
042 import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
043 import org.jetbrains.kotlin.resolve.inline.InlineStrategy;
044 import org.jetbrains.kotlin.resolve.inline.InlineUtil;
045 import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
046 import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind;
047 import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature;
048 import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
049 import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor;
050 import org.jetbrains.kotlin.types.expressions.LabelResolver;
051 import org.jetbrains.org.objectweb.asm.Label;
052 import org.jetbrains.org.objectweb.asm.MethodVisitor;
053 import org.jetbrains.org.objectweb.asm.Opcodes;
054 import org.jetbrains.org.objectweb.asm.Type;
055 import org.jetbrains.org.objectweb.asm.commons.Method;
056 import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode;
057 import org.jetbrains.org.objectweb.asm.tree.InsnList;
058 import org.jetbrains.org.objectweb.asm.tree.LabelNode;
059 import org.jetbrains.org.objectweb.asm.tree.MethodNode;
060
061 import java.io.IOException;
062 import java.util.*;
063
064 import static org.jetbrains.kotlin.codegen.AsmUtil.getMethodAsmFlags;
065 import static org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive;
066 import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.CLASS_FOR_SCRIPT;
067 import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.addInlineMarker;
068 import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.getConstant;
069 import static org.jetbrains.kotlin.codegen.inline.InlinePackage.getClassFilePath;
070 import static org.jetbrains.kotlin.codegen.inline.InlinePackage.getSourceFilePath;
071 import static org.jetbrains.kotlin.resolve.DescriptorUtils.isFunctionLiteral;
072 import static org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage.getResolvedCallWithAssert;
073
074 public class InlineCodegen extends CallGenerator {
075 private final GenerationState state;
076 private final JetTypeMapper typeMapper;
077
078 private final SimpleFunctionDescriptor functionDescriptor;
079 private final JvmMethodSignature jvmSignature;
080 private final JetElement callElement;
081 private final MethodContext context;
082 private final ExpressionCodegen codegen;
083
084 private final boolean asFunctionInline;
085 private final int initialFrameSize;
086 private final boolean isSameModule;
087
088 protected final ParametersBuilder invocationParamBuilder = ParametersBuilder.newBuilder();
089 protected final Map<Integer, LambdaInfo> expressionMap = new HashMap<Integer, LambdaInfo>();
090
091 private final ReifiedTypeInliner reifiedTypeInliner;
092
093 private LambdaInfo activeLambda;
094
095 private final SourceMapper sourceMapper;
096
097 public InlineCodegen(
098 @NotNull ExpressionCodegen codegen,
099 @NotNull GenerationState state,
100 @NotNull SimpleFunctionDescriptor functionDescriptor,
101 @NotNull JetElement callElement,
102 @Nullable ReifiedTypeParameterMappings typeParameterMappings
103 ) {
104 assert InlineUtil.isInline(functionDescriptor) : "InlineCodegen could inline only inline function: " + functionDescriptor;
105
106 this.state = state;
107 this.typeMapper = state.getTypeMapper();
108 this.codegen = codegen;
109 this.callElement = callElement;
110 this.functionDescriptor = functionDescriptor.getOriginal();
111
112 reifiedTypeInliner = new ReifiedTypeInliner(typeParameterMappings);
113
114 initialFrameSize = codegen.getFrameMap().getCurrentSize();
115
116 context = (MethodContext) getContext(functionDescriptor, state);
117 jvmSignature = typeMapper.mapSignature(functionDescriptor, context.getContextKind());
118
119 // TODO: implement AS_FUNCTION inline strategy
120 InlineStrategy inlineStrategy = InlineUtil.getInlineStrategy(functionDescriptor);
121 this.asFunctionInline = false;
122
123 isSameModule = JvmCodegenUtil.isCallInsideSameModuleAsDeclared(functionDescriptor, codegen.getContext(), state.getOutDirectory());
124
125 sourceMapper = codegen.getParentCodegen().getOrCreateSourceMapper();
126 reportIncrementalInfo(functionDescriptor, codegen.getContext().getFunctionDescriptor().getOriginal());
127 }
128
129 @Override
130 public void genCallWithoutAssertions(
131 @NotNull CallableMethod callableMethod, @NotNull ExpressionCodegen codegen
132 ) {
133 genCall(callableMethod, null, false, codegen);
134 }
135
136 @Override
137 public void genCallInner(@NotNull Callable callableMethod, @Nullable ResolvedCall<?> resolvedCall, boolean callDefault, @NotNull ExpressionCodegen codegen) {
138 SMAPAndMethodNode nodeAndSmap = null;
139 if (!state.getInlineCycleReporter().enterIntoInlining(resolvedCall)) {
140 generateStub(resolvedCall, codegen);
141 return;
142 }
143
144 try {
145 nodeAndSmap = createMethodNode(callDefault);
146 endCall(inlineCall(nodeAndSmap));
147 }
148 catch (CompilationException e) {
149 throw e;
150 }
151 catch (Exception e) {
152 boolean generateNodeText = !(e instanceof InlineException);
153 PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(this.codegen.getContext().getContextDescriptor());
154 throw new CompilationException("Couldn't inline method call '" +
155 functionDescriptor.getName() +
156 "' into \n" + (element != null ? element.getText() : "null psi element " + this.codegen.getContext().getContextDescriptor()) +
157 (generateNodeText ? ("\ncause: " + InlineCodegenUtil.getNodeText(nodeAndSmap != null ? nodeAndSmap.getNode(): null)) : ""),
158 e, callElement);
159 }
160 finally {
161 state.getInlineCycleReporter().exitFromInliningOf(resolvedCall);
162 }
163 }
164
165 protected void generateStub(@Nullable ResolvedCall<?> resolvedCall, @NotNull ExpressionCodegen codegen) {
166 leaveTemps();
167 assert resolvedCall != null;
168 String message = "Call is part of inline cycle: " + resolvedCall.getCall().getCallElement().getText();
169 AsmUtil.genThrow(codegen.v, "java/lang/UnsupportedOperationException", message);
170 }
171
172 private void endCall(@NotNull InlineResult result) {
173 leaveTemps();
174
175 codegen.propagateChildReifiedTypeParametersUsages(result.getReifiedTypeParametersUsages());
176
177 state.getFactory().removeInlinedClasses(result.getClassesToRemove());
178
179 codegen.markLineNumberAfterInlineIfNeeded();
180 }
181
182 @NotNull
183 private SMAPAndMethodNode createMethodNode(boolean callDefault) throws ClassNotFoundException, IOException {
184 JvmMethodSignature jvmSignature = typeMapper.mapSignature(functionDescriptor, context.getContextKind());
185
186 Method asmMethod;
187 if (callDefault) {
188 asmMethod = typeMapper.mapDefaultMethod(functionDescriptor, context.getContextKind(), context);
189 }
190 else {
191 asmMethod = jvmSignature.getAsmMethod();
192 }
193
194 SMAPAndMethodNode nodeAndSMAP;
195 if (functionDescriptor instanceof DeserializedSimpleFunctionDescriptor) {
196 ClassId containerClassId = InlineCodegenUtil.getContainerClassIdForInlineCallable(
197 (DeserializedSimpleFunctionDescriptor) functionDescriptor);
198
199 VirtualFile file = InlineCodegenUtil.getVirtualFileForCallable(containerClassId, state);
200 //if (functionDescriptor.getContainingDeclaration() instanceof PackageFragmentDescriptor) {
201 // /*use facade class*/
202 // containerClassId = PackageClassUtils.getPackageClassId(containerClassId.getPackageFqName());
203 //}
204 nodeAndSMAP = InlineCodegenUtil.getMethodNode(file.contentsToByteArray(),
205 asmMethod.getName(),
206 asmMethod.getDescriptor(),
207 containerClassId);
208
209 if (nodeAndSMAP == null) {
210 throw new RuntimeException("Couldn't obtain compiled function body for " + descriptorName(functionDescriptor));
211 }
212 }
213 else {
214 PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor);
215
216 if (element == null || !(element instanceof JetNamedFunction)) {
217 throw new RuntimeException("Couldn't find declaration for function " + descriptorName(functionDescriptor));
218 }
219 JetNamedFunction inliningFunction = (JetNamedFunction) element;
220
221 MethodNode node = new MethodNode(InlineCodegenUtil.API,
222 getMethodAsmFlags(functionDescriptor, context.getContextKind()) | (callDefault ? Opcodes.ACC_STATIC : 0),
223 asmMethod.getName(),
224 asmMethod.getDescriptor(),
225 jvmSignature.getGenericsSignature(),
226 null);
227
228 //for maxLocals calculation
229 MethodVisitor maxCalcAdapter = InlineCodegenUtil.wrapWithMaxLocalCalc(node);
230 MethodContext methodContext = context.getParentContext().intoFunction(functionDescriptor);
231
232 SMAP smap;
233 if (callDefault) {
234 Type ownerType = typeMapper.mapOwner(functionDescriptor, true/*TODO: false, migration*/);
235 FakeMemberCodegen parentCodegen = new FakeMemberCodegen(codegen.getParentCodegen(), inliningFunction,
236 (FieldOwnerContext) methodContext.getParentContext(),
237 ownerType.getInternalName());
238 FunctionCodegen.generateDefaultImplBody(
239 methodContext, functionDescriptor, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT,
240 inliningFunction, parentCodegen
241 );
242 smap = createSMAPWithDefaultMapping(inliningFunction, parentCodegen.getOrCreateSourceMapper().getResultMappings());
243 }
244 else {
245 smap = generateMethodBody(maxCalcAdapter, functionDescriptor, methodContext, inliningFunction, jvmSignature, false);
246 }
247
248 nodeAndSMAP = new SMAPAndMethodNode(node, smap);
249 maxCalcAdapter.visitMaxs(-1, -1);
250 maxCalcAdapter.visitEnd();
251 }
252 return nodeAndSMAP;
253 }
254
255 private InlineResult inlineCall(SMAPAndMethodNode nodeAndSmap) {
256 MethodNode node = nodeAndSmap.getNode();
257 ReifiedTypeParametersUsages reificationResult = reifiedTypeInliner.reifyInstructions(node.instructions);
258 generateClosuresBodies();
259
260 //through generation captured parameters will be added to invocationParamBuilder
261 putClosureParametersOnStack();
262
263 addInlineMarker(codegen.v, true);
264
265 Parameters parameters = invocationParamBuilder.buildParameters();
266
267 InliningContext info = new RootInliningContext(expressionMap,
268 state,
269 codegen.getInlineNameGenerator()
270 .subGenerator(functionDescriptor.getName().asString()),
271 codegen.getContext(),
272 callElement,
273 codegen.getParentCodegen().getClassName(), reifiedTypeInliner);
274
275 MethodInliner inliner = new MethodInliner(node, parameters, info, new FieldRemapper(null, null, parameters), isSameModule,
276 "Method inlining " + callElement.getText(),
277 createNestedSourceMapper(nodeAndSmap)); //with captured
278
279 LocalVarRemapper remapper = new LocalVarRemapper(parameters, initialFrameSize);
280
281
282 MethodNode adapter = InlineCodegenUtil.createEmptyMethodNode();
283 //hack to keep linenumber info, otherwise jdi will skip begin of linenumber chain
284 adapter.visitInsn(Opcodes.NOP);
285
286 InlineResult result = inliner.doInline(adapter, remapper, true, LabelOwner.SKIP_ALL);
287 result.getReifiedTypeParametersUsages().mergeAll(reificationResult);
288
289 CallableMemberDescriptor descriptor = codegen.getContext().getContextDescriptor();
290 final Set<String> labels = getDeclarationLabels(DescriptorToSourceUtils.descriptorToDeclaration(descriptor), descriptor);
291 LabelOwner labelOwner = new LabelOwner() {
292 @Override
293 public boolean isMyLabel(@NotNull String name) {
294 return labels.contains(name);
295 }
296 };
297
298 List<MethodInliner.PointForExternalFinallyBlocks> infos = MethodInliner.processReturns(adapter, labelOwner, true, null);
299 generateAndInsertFinallyBlocks(adapter, infos, ((StackValue.Local)remapper.remap(parameters.totalSize() + 1).value).index);
300 removeFinallyMarkers(adapter);
301
302 adapter.accept(new InliningInstructionAdapter(codegen.v));
303
304 addInlineMarker(codegen.v, false);
305
306 return result;
307 }
308
309 private void generateClosuresBodies() {
310 for (LambdaInfo info : expressionMap.values()) {
311 info.setNode(generateLambdaBody(info));
312 }
313 }
314
315 private SMAPAndMethodNode generateLambdaBody(LambdaInfo info) {
316 JetExpression declaration = info.getFunctionWithBodyOrCallableReference();
317 FunctionDescriptor descriptor = info.getFunctionDescriptor();
318
319 MethodContext parentContext = codegen.getContext();
320
321 MethodContext context = parentContext.intoClosure(descriptor, codegen, typeMapper).intoInlinedLambda(descriptor);
322
323 JvmMethodSignature jvmMethodSignature = typeMapper.mapSignature(descriptor);
324 Method asmMethod = jvmMethodSignature.getAsmMethod();
325 MethodNode methodNode = new MethodNode(InlineCodegenUtil.API, getMethodAsmFlags(descriptor, context.getContextKind()), asmMethod.getName(), asmMethod.getDescriptor(), jvmMethodSignature.getGenericsSignature(), null);
326
327 MethodVisitor adapter = InlineCodegenUtil.wrapWithMaxLocalCalc(methodNode);
328
329 SMAP smap = generateMethodBody(adapter, descriptor, context, declaration, jvmMethodSignature, true);
330 adapter.visitMaxs(-1, -1);
331 return new SMAPAndMethodNode(methodNode, smap);
332 }
333
334 private SMAP generateMethodBody(
335 @NotNull MethodVisitor adapter,
336 @NotNull FunctionDescriptor descriptor,
337 @NotNull MethodContext context,
338 @NotNull JetExpression expression,
339 @NotNull JvmMethodSignature jvmMethodSignature,
340 boolean isLambda
341 ) {
342 FakeMemberCodegen parentCodegen =
343 new FakeMemberCodegen(codegen.getParentCodegen(), expression,
344 (FieldOwnerContext) context.getParentContext(),
345 isLambda ? codegen.getParentCodegen().getClassName()
346 : typeMapper.mapOwner(descriptor, true /*TODO: false, migration*/).getInternalName());
347
348 FunctionGenerationStrategy strategy =
349 expression instanceof JetCallableReferenceExpression ?
350 new FunctionReferenceGenerationStrategy(
351 state,
352 descriptor,
353 getResolvedCallWithAssert(((JetCallableReferenceExpression) expression).getCallableReference(),
354 codegen.getBindingContext()
355 )) :
356 new FunctionGenerationStrategy.FunctionDefault(state, descriptor, (JetDeclarationWithBody) expression);
357
358 FunctionCodegen.generateMethodBody(
359 adapter, descriptor, context, jvmMethodSignature,
360 strategy,
361 // Wrapping for preventing marking actual parent codegen as containing reifier markers
362 parentCodegen
363 );
364
365 return createSMAPWithDefaultMapping(expression, parentCodegen.getOrCreateSourceMapper().getResultMappings());
366 }
367
368 private static SMAP createSMAPWithDefaultMapping(
369 @NotNull JetExpression declaration,
370 @NotNull List<FileMapping> mappings
371 ) {
372 PsiFile containingFile = declaration.getContainingFile();
373 Integer lineNumbers = CodegenUtil.getLineNumberForElement(containingFile, true);
374 assert lineNumbers != null : "Couldn't extract line count in " + containingFile;
375
376 return new SMAP(mappings);
377 }
378
379 private static class FakeMemberCodegen extends MemberCodegen {
380
381 private final MemberCodegen delegate;
382 @NotNull private final String className;
383
384 public FakeMemberCodegen(@NotNull MemberCodegen wrapped, @NotNull JetElement declaration, @NotNull FieldOwnerContext codegenContext, @NotNull String className) {
385 super(wrapped, declaration, codegenContext);
386 delegate = wrapped;
387 this.className = className;
388 }
389
390 @Override
391 protected void generateDeclaration() {
392 throw new IllegalStateException();
393 }
394
395 @Override
396 protected void generateBody() {
397 throw new IllegalStateException();
398 }
399
400 @Override
401 protected void generateKotlinAnnotation() {
402 throw new IllegalStateException();
403 }
404
405 @NotNull
406 @Override
407 public NameGenerator getInlineNameGenerator() {
408 return delegate.getInlineNameGenerator();
409 }
410
411 @NotNull
412 @Override
413 //TODO: obtain name from context
414 public String getClassName() {
415 return className;
416 }
417 }
418
419 @Override
420 public void afterParameterPut(@NotNull Type type, @Nullable StackValue stackValue, @Nullable ValueParameterDescriptor valueParameterDescriptor) {
421 putCapturedInLocal(type, stackValue, valueParameterDescriptor, -1);
422 }
423
424 private void putCapturedInLocal(
425 @NotNull Type type, @Nullable StackValue stackValue, @Nullable ValueParameterDescriptor valueParameterDescriptor, int capturedParamIndex
426 ) {
427 if (!asFunctionInline && Type.VOID_TYPE != type) {
428 //TODO remap only inlinable closure => otherwise we could get a lot of problem
429 boolean couldBeRemapped = !shouldPutValue(type, stackValue, valueParameterDescriptor);
430 StackValue remappedIndex = couldBeRemapped ? stackValue : null;
431
432 ParameterInfo info;
433 if (capturedParamIndex >= 0) {
434 CapturedParamDesc capturedParamInfoInLambda = activeLambda.getCapturedVars().get(capturedParamIndex);
435 info = invocationParamBuilder.addCapturedParam(capturedParamInfoInLambda, capturedParamInfoInLambda.getFieldName());
436 info.setRemapValue(remappedIndex);
437 }
438 else {
439 info = invocationParamBuilder.addNextParameter(type, false, remappedIndex);
440 }
441
442 putParameterOnStack(info);
443 }
444 }
445
446 /*descriptor is null for captured vars*/
447 public boolean shouldPutValue(
448 @NotNull Type type,
449 @Nullable StackValue stackValue,
450 @Nullable ValueParameterDescriptor descriptor
451 ) {
452
453 if (stackValue == null) {
454 //default or vararg
455 return true;
456 }
457
458 //remap only inline functions (and maybe non primitives)
459 //TODO - clean asserion and remapping logic
460 if (isPrimitive(type) != isPrimitive(stackValue.type)) {
461 //don't remap boxing/unboxing primitives - lost identity and perfomance
462 return true;
463 }
464
465 if (stackValue instanceof StackValue.Local) {
466 return false;
467 }
468
469 StackValue field = stackValue;
470 if (stackValue instanceof StackValue.FieldForSharedVar) {
471 field = ((StackValue.FieldForSharedVar) stackValue).receiver;
472 }
473
474 //check that value corresponds to captured inlining parameter
475 if (field instanceof StackValue.Field) {
476 DeclarationDescriptor varDescriptor = ((StackValue.Field) field).descriptor;
477 //check that variable is inline function parameter
478 return !(varDescriptor instanceof ParameterDescriptor &&
479 InlineUtil.isInlineLambdaParameter((ParameterDescriptor) varDescriptor) &&
480 InlineUtil.isInline(varDescriptor.getContainingDeclaration()));
481 }
482
483 return true;
484 }
485
486 private void putParameterOnStack(ParameterInfo... infos) {
487 int[] index = new int[infos.length];
488 for (int i = 0; i < infos.length; i++) {
489 ParameterInfo info = infos[i];
490 if (!info.isSkippedOrRemapped()) {
491 index[i] = codegen.getFrameMap().enterTemp(info.getType());
492 }
493 else {
494 index[i] = -1;
495 }
496 }
497
498 for (int i = infos.length - 1; i >= 0; i--) {
499 ParameterInfo info = infos[i];
500 if (!info.isSkippedOrRemapped()) {
501 Type type = info.type;
502 StackValue.local(index[i], type).store(StackValue.onStack(type), codegen.v);
503 }
504 }
505 }
506
507 @Override
508 public void putHiddenParams() {
509 List<JvmMethodParameterSignature> valueParameters = jvmSignature.getValueParameters();
510
511 if (!isStaticMethod(functionDescriptor, context)) {
512 invocationParamBuilder.addNextParameter(AsmTypes.OBJECT_TYPE, false, null);
513 }
514
515 for (JvmMethodParameterSignature param : valueParameters) {
516 if (param.getKind() == JvmMethodParameterKind.VALUE) {
517 break;
518 }
519 invocationParamBuilder.addNextParameter(param.getAsmType(), false, null);
520 }
521
522 List<ParameterInfo> infos = invocationParamBuilder.listNotCaptured();
523 putParameterOnStack(infos.toArray(new ParameterInfo[infos.size()]));
524 }
525
526 public void leaveTemps() {
527 FrameMap frameMap = codegen.getFrameMap();
528 List<ParameterInfo> infos = invocationParamBuilder.listAllParams();
529 for (ListIterator<? extends ParameterInfo> iterator = infos.listIterator(infos.size()); iterator.hasPrevious(); ) {
530 ParameterInfo param = iterator.previous();
531 if (!param.isSkippedOrRemapped()) {
532 frameMap.leaveTemp(param.type);
533 }
534 }
535 }
536
537 /*lambda or callable reference*/
538 public static boolean isInliningParameter(JetExpression expression, ValueParameterDescriptor valueParameterDescriptor) {
539 //TODO deparenthisise typed
540 JetExpression deparenthesized = JetPsiUtil.deparenthesize(expression);
541 return InlineUtil.isInlineLambdaParameter(valueParameterDescriptor) &&
542 isInlinableParameterExpression(deparenthesized);
543 }
544
545 protected static boolean isInlinableParameterExpression(JetExpression deparenthesized) {
546 return deparenthesized instanceof JetFunctionLiteralExpression ||
547 deparenthesized instanceof JetNamedFunction ||
548 deparenthesized instanceof JetCallableReferenceExpression;
549 }
550
551 public void rememberClosure(JetExpression expression, Type type) {
552 JetExpression lambda = JetPsiUtil.deparenthesize(expression);
553 assert isInlinableParameterExpression(lambda) : "Couldn't find inline expression in " + expression.getText();
554
555 LambdaInfo info = new LambdaInfo(lambda, typeMapper);
556
557 ParameterInfo closureInfo = invocationParamBuilder.addNextParameter(type, true, null);
558 closureInfo.setLambda(info);
559 expressionMap.put(closureInfo.getIndex(), info);
560 }
561
562 @NotNull
563 protected static Set<String> getDeclarationLabels(@Nullable PsiElement lambdaOrFun, @NotNull DeclarationDescriptor descriptor) {
564 Set<String> result = new HashSet<String>();
565
566 if (lambdaOrFun != null) {
567 Name label = LabelResolver.INSTANCE.getLabelNameIfAny(lambdaOrFun);
568 if (label != null) {
569 result.add(label.asString());
570 }
571 }
572
573 if (!isFunctionLiteral(descriptor)) {
574 if (!descriptor.getName().isSpecial()) {
575 result.add(descriptor.getName().asString());
576 }
577 result.add(InlineCodegenUtil.FIRST_FUN_LABEL);
578 }
579 return result;
580 }
581
582 private void putClosureParametersOnStack() {
583 for (LambdaInfo next : expressionMap.values()) {
584 activeLambda = next;
585 codegen.pushClosureOnStack(next.getClassDescriptor(), true, this);
586 }
587 activeLambda = null;
588 }
589
590 public static CodegenContext getContext(DeclarationDescriptor descriptor, GenerationState state) {
591 if (descriptor instanceof PackageFragmentDescriptor) {
592 return new PackageContext((PackageFragmentDescriptor) descriptor, state.getRootContext(), null);
593 }
594
595 CodegenContext parent = getContext(descriptor.getContainingDeclaration(), state);
596
597 if (descriptor instanceof ClassDescriptor) {
598 OwnerKind kind = DescriptorUtils.isTrait(descriptor) ? OwnerKind.TRAIT_IMPL : OwnerKind.IMPLEMENTATION;
599 return parent.intoClass((ClassDescriptor) descriptor, kind, state);
600 }
601 else if (descriptor instanceof ScriptDescriptor) {
602 ClassDescriptor classDescriptorForScript = state.getBindingContext().get(CLASS_FOR_SCRIPT, (ScriptDescriptor) descriptor);
603 assert classDescriptorForScript != null : "Can't find class for script: " + descriptor;
604 List<ScriptDescriptor> earlierScripts = state.getEarlierScriptsForReplInterpreter();
605 return parent.intoScript((ScriptDescriptor) descriptor,
606 earlierScripts == null ? Collections.emptyList() : earlierScripts,
607 classDescriptorForScript);
608 }
609 else if (descriptor instanceof FunctionDescriptor) {
610 return parent.intoFunction((FunctionDescriptor) descriptor);
611 }
612
613 throw new IllegalStateException("Couldn't build context for " + descriptorName(descriptor));
614 }
615
616 private static boolean isStaticMethod(FunctionDescriptor functionDescriptor, MethodContext context) {
617 return (getMethodAsmFlags(functionDescriptor, context.getContextKind()) & Opcodes.ACC_STATIC) != 0;
618 }
619
620 private static String descriptorName(DeclarationDescriptor descriptor) {
621 return DescriptorRenderer.SHORT_NAMES_IN_TYPES.render(descriptor);
622 }
623
624 @Override
625 public void genValueAndPut(
626 @NotNull ValueParameterDescriptor valueParameterDescriptor,
627 @NotNull JetExpression argumentExpression,
628 @NotNull Type parameterType
629 ) {
630 if (isInliningParameter(argumentExpression, valueParameterDescriptor)) {
631 rememberClosure(argumentExpression, parameterType);
632 }
633 else {
634 StackValue value = codegen.gen(argumentExpression);
635 putValueIfNeeded(valueParameterDescriptor, parameterType, value);
636 }
637 }
638
639 @Override
640 public void putValueIfNeeded(@Nullable ValueParameterDescriptor valueParameterDescriptor, @NotNull Type parameterType, @NotNull StackValue value) {
641 if (shouldPutValue(parameterType, value, valueParameterDescriptor)) {
642 value.put(parameterType, codegen.v);
643 }
644 afterParameterPut(parameterType, value, valueParameterDescriptor);
645 }
646
647 @Override
648 public void putCapturedValueOnStack(
649 @NotNull StackValue stackValue, @NotNull Type valueType, int paramIndex
650 ) {
651 if (shouldPutValue(stackValue.type, stackValue, null)) {
652 stackValue.put(stackValue.type, codegen.v);
653 }
654 putCapturedInLocal(stackValue.type, stackValue, null, paramIndex);
655 }
656
657
658 public void generateAndInsertFinallyBlocks(
659 @NotNull MethodNode intoNode,
660 @NotNull List<MethodInliner.PointForExternalFinallyBlocks> insertPoints,
661 int offsetForFinallyLocalVar
662 ) {
663 if (!codegen.hasFinallyBlocks()) return;
664
665 Map<AbstractInsnNode, MethodInliner.PointForExternalFinallyBlocks> extensionPoints =
666 new HashMap<AbstractInsnNode, MethodInliner.PointForExternalFinallyBlocks>();
667 for (MethodInliner.PointForExternalFinallyBlocks insertPoint : insertPoints) {
668 extensionPoints.put(insertPoint.beforeIns, insertPoint);
669 }
670
671 DefaultProcessor processor = new DefaultProcessor(intoNode, offsetForFinallyLocalVar);
672
673 int curFinallyDepth = 0;
674 AbstractInsnNode curInstr = intoNode.instructions.getFirst();
675 while (curInstr != null) {
676 processor.processInstruction(curInstr, true);
677 if (InlineCodegenUtil.isFinallyStart(curInstr)) {
678 //TODO depth index calc could be more precise
679 curFinallyDepth = getConstant(curInstr.getPrevious());
680 }
681
682 MethodInliner.PointForExternalFinallyBlocks extension = extensionPoints.get(curInstr);
683 if (extension != null) {
684 Label start = new Label();
685
686 MethodNode finallyNode = InlineCodegenUtil.createEmptyMethodNode();
687 finallyNode.visitLabel(start);
688
689 ExpressionCodegen finallyCodegen =
690 new ExpressionCodegen(finallyNode, codegen.getFrameMap(), codegen.getReturnType(),
691 codegen.getContext(), codegen.getState(), codegen.getParentCodegen());
692 finallyCodegen.addBlockStackElementsForNonLocalReturns(codegen.getBlockStackElements(), curFinallyDepth);
693
694 FrameMap frameMap = finallyCodegen.getFrameMap();
695 FrameMap.Mark mark = frameMap.mark();
696 while (frameMap.getCurrentSize() < processor.getNextFreeLocalIndex()) {
697 frameMap.enterTemp(Type.INT_TYPE);
698 }
699
700 finallyCodegen.generateFinallyBlocksIfNeeded(extension.returnType, extension.finallyIntervalEnd.getLabel());
701
702 //Exception table for external try/catch/finally blocks will be generated in original codegen after exiting this method
703 InlineCodegenUtil.insertNodeBefore(finallyNode, intoNode, curInstr);
704
705 SimpleInterval splitBy = new SimpleInterval((LabelNode) start.info, extension.finallyIntervalEnd);
706 processor.getTryBlocksMetaInfo().splitCurrentIntervals(splitBy, true);
707
708 processor.getLocalVarsMetaInfo().splitCurrentIntervals(splitBy, true);
709
710 mark.dropTo();
711 }
712
713 curInstr = curInstr.getNext();
714 }
715
716 processor.substituteTryBlockNodes(intoNode);
717
718 //processor.substituteLocalVarTable(intoNode);
719 }
720
721 public void removeFinallyMarkers(@NotNull MethodNode intoNode) {
722 if (InlineCodegenUtil.isFinallyMarkerRequired(codegen.getContext())) return;
723
724 InsnList instructions = intoNode.instructions;
725 AbstractInsnNode curInstr = instructions.getFirst();
726 while (curInstr != null) {
727 if (InlineCodegenUtil.isFinallyMarker(curInstr)) {
728 AbstractInsnNode marker = curInstr;
729 //just to assert
730 getConstant(marker.getPrevious());
731 curInstr = curInstr.getNext();
732 instructions.remove(marker.getPrevious());
733 instructions.remove(marker);
734 continue;
735 }
736 curInstr = curInstr.getNext();
737 }
738 }
739
740 private SourceMapper createNestedSourceMapper(@NotNull SMAPAndMethodNode nodeAndSmap) {
741 return new NestedSourceMapper(sourceMapper, nodeAndSmap.getRanges(), nodeAndSmap.getClassSMAP().getSourceInfo());
742 }
743
744 private void reportIncrementalInfo(
745 @NotNull FunctionDescriptor sourceDescriptor,
746 @NotNull FunctionDescriptor targetDescriptor
747 ) {
748 IncrementalCompilationComponents incrementalCompilationComponents = state.getIncrementalCompilationComponents();
749 TargetId targetId = state.getTargetId();
750
751 if (incrementalCompilationComponents == null || targetId == null) return;
752
753 IncrementalCache incrementalCache = incrementalCompilationComponents.getIncrementalCache(targetId);
754 String sourceFile = getClassFilePath(sourceDescriptor, incrementalCache);
755 String targetFile = getSourceFilePath(targetDescriptor);
756 incrementalCache.registerInline(sourceFile, jvmSignature.toString(), targetFile);
757 }
758 }