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;
018    
019    import com.google.common.collect.Lists;
020    import com.google.common.collect.Maps;
021    import com.intellij.openapi.progress.ProcessCanceledException;
022    import com.intellij.psi.PsiElement;
023    import com.intellij.psi.tree.IElementType;
024    import com.intellij.util.ArrayUtil;
025    import com.intellij.util.Function;
026    import com.intellij.util.containers.Stack;
027    import kotlin.Pair;
028    import kotlin.Unit;
029    import kotlin.collections.CollectionsKt;
030    import kotlin.jvm.functions.Function1;
031    import org.jetbrains.annotations.NotNull;
032    import org.jetbrains.annotations.Nullable;
033    import org.jetbrains.kotlin.backend.common.CodegenUtil;
034    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
035    import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
036    import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
037    import org.jetbrains.kotlin.codegen.context.*;
038    import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension;
039    import org.jetbrains.kotlin.codegen.inline.*;
040    import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicCallable;
041    import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethod;
042    import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods;
043    import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicPropertyGetter;
044    import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsnsKt;
045    import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter;
046    import org.jetbrains.kotlin.codegen.state.GenerationState;
047    import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
048    import org.jetbrains.kotlin.codegen.when.SwitchCodegen;
049    import org.jetbrains.kotlin.codegen.when.SwitchCodegenUtil;
050    import org.jetbrains.kotlin.descriptors.*;
051    import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor;
052    import org.jetbrains.kotlin.diagnostics.DiagnosticUtils;
053    import org.jetbrains.kotlin.diagnostics.Errors;
054    import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
055    import org.jetbrains.kotlin.jvm.RuntimeAssertionInfo;
056    import org.jetbrains.kotlin.jvm.bindingContextSlices.BindingContextSlicesKt;
057    import org.jetbrains.kotlin.lexer.KtTokens;
058    import org.jetbrains.kotlin.load.java.JvmAbi;
059    import org.jetbrains.kotlin.load.java.descriptors.SamConstructorDescriptor;
060    import org.jetbrains.kotlin.name.Name;
061    import org.jetbrains.kotlin.psi.*;
062    import org.jetbrains.kotlin.resolve.BindingContext;
063    import org.jetbrains.kotlin.resolve.BindingContextUtils;
064    import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
065    import org.jetbrains.kotlin.resolve.DescriptorUtils;
066    import org.jetbrains.kotlin.resolve.annotations.AnnotationUtilKt;
067    import org.jetbrains.kotlin.resolve.calls.callResolverUtil.CallResolverUtilKt;
068    import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
069    import org.jetbrains.kotlin.resolve.calls.model.*;
070    import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
071    import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject;
072    import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
073    import org.jetbrains.kotlin.resolve.constants.ConstantValue;
074    import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
075    import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
076    import org.jetbrains.kotlin.resolve.inline.InlineUtil;
077    import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
078    import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind;
079    import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature;
080    import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
081    import org.jetbrains.kotlin.resolve.scopes.receivers.*;
082    import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor;
083    import org.jetbrains.kotlin.types.KotlinType;
084    import org.jetbrains.kotlin.types.TypeProjection;
085    import org.jetbrains.kotlin.types.TypeUtils;
086    import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
087    import org.jetbrains.kotlin.types.typesApproximation.CapturedTypeApproximationKt;
088    import org.jetbrains.org.objectweb.asm.Label;
089    import org.jetbrains.org.objectweb.asm.MethodVisitor;
090    import org.jetbrains.org.objectweb.asm.Opcodes;
091    import org.jetbrains.org.objectweb.asm.Type;
092    import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
093    
094    import java.util.*;
095    
096    import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isInt;
097    import static org.jetbrains.kotlin.codegen.AsmUtil.*;
098    import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*;
099    import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*;
100    import static org.jetbrains.kotlin.resolve.BindingContext.*;
101    import static org.jetbrains.kotlin.resolve.BindingContextUtils.*;
102    import static org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry;
103    import static org.jetbrains.kotlin.resolve.DescriptorUtils.isObject;
104    import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*;
105    import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionExpression;
106    import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral;
107    import static org.jetbrains.org.objectweb.asm.Opcodes.*;
108    
109    public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> implements LocalLookup {
110        private final GenerationState state;
111        final JetTypeMapper typeMapper;
112        private final BindingContext bindingContext;
113    
114        public final InstructionAdapter v;
115        public final FrameMap myFrameMap;
116        private final MethodContext context;
117        private final Type returnType;
118    
119        private final CodegenStatementVisitor statementVisitor = new CodegenStatementVisitor(this);
120        private final MemberCodegen<?> parentCodegen;
121        private final TailRecursionCodegen tailRecursionCodegen;
122        public final CallGenerator defaultCallGenerator = new CallGenerator.DefaultCallGenerator(this);
123    
124        private final Stack<BlockStackElement> blockStackElements = new Stack<BlockStackElement>();
125    
126        /*
127         * When we create a temporary variable to hold some value not to compute it many times
128         * we put it into this map to emit access to that variable instead of evaluating the whole expression
129         */
130        public final Map<KtElement, StackValue> tempVariables = Maps.newHashMap();
131    
132        private int myLastLineNumber = -1;
133        private boolean shouldMarkLineNumbers = true;
134        private int finallyDepth = 0;
135    
136        public ExpressionCodegen(
137                @NotNull MethodVisitor mv,
138                @NotNull FrameMap frameMap,
139                @NotNull Type returnType,
140                @NotNull MethodContext context,
141                @NotNull GenerationState state,
142                @NotNull MemberCodegen<?> parentCodegen
143        ) {
144            this.state = state;
145            this.typeMapper = state.getTypeMapper();
146            this.bindingContext = state.getBindingContext();
147            this.v = new InstructionAdapter(mv);
148            this.myFrameMap = frameMap;
149            this.context = context;
150            this.returnType = returnType;
151            this.parentCodegen = parentCodegen;
152            this.tailRecursionCodegen = new TailRecursionCodegen(context, this, this.v, state);
153        }
154    
155        static class BlockStackElement {
156        }
157    
158        static class LoopBlockStackElement extends BlockStackElement {
159            final Label continueLabel;
160            final Label breakLabel;
161            public final KtSimpleNameExpression targetLabel;
162    
163            LoopBlockStackElement(Label breakLabel, Label continueLabel, KtSimpleNameExpression targetLabel) {
164                this.breakLabel = breakLabel;
165                this.continueLabel = continueLabel;
166                this.targetLabel = targetLabel;
167            }
168        }
169    
170        static class FinallyBlockStackElement extends BlockStackElement {
171            List<Label> gaps = new ArrayList<Label>();
172    
173            final KtTryExpression expression;
174    
175            FinallyBlockStackElement(KtTryExpression expression) {
176                this.expression = expression;
177            }
178    
179            private void addGapLabel(Label label){
180                gaps.add(label);
181            }
182        }
183    
184        @NotNull
185        public GenerationState getState() {
186            return state;
187        }
188    
189        @NotNull
190        public BindingContext getBindingContext() {
191            return bindingContext;
192        }
193    
194        @NotNull
195        public MemberCodegen<?> getParentCodegen() {
196            return parentCodegen;
197        }
198    
199        @NotNull
200        public ObjectLiteralResult generateObjectLiteral(@NotNull KtObjectLiteralExpression literal) {
201            KtObjectDeclaration objectDeclaration = literal.getObjectDeclaration();
202    
203            ClassDescriptor classDescriptor = bindingContext.get(CLASS, objectDeclaration);
204            assert classDescriptor != null;
205    
206            Type asmType = asmTypeForAnonymousClass(bindingContext, objectDeclaration);
207            ClassBuilder classBuilder = state.getFactory().newVisitor(
208                    JvmDeclarationOriginKt.OtherOrigin(objectDeclaration, classDescriptor),
209                    asmType,
210                    literal.getContainingFile()
211            );
212    
213            ClassContext objectContext = context.intoAnonymousClass(classDescriptor, this, OwnerKind.IMPLEMENTATION);
214    
215            MemberCodegen literalCodegen = new ImplementationBodyCodegen(
216                    objectDeclaration, objectContext, classBuilder, state, getParentCodegen(),
217                    /* isLocal = */ true);
218            literalCodegen.generate();
219    
220            addReifiedParametersFromSignature(literalCodegen, classDescriptor);
221            propagateChildReifiedTypeParametersUsages(literalCodegen.getReifiedTypeParametersUsages());
222    
223            return new ObjectLiteralResult(
224                    literalCodegen.getReifiedTypeParametersUsages().wereUsedReifiedParameters(),
225                    classDescriptor
226            );
227        }
228    
229        private static void addReifiedParametersFromSignature(@NotNull MemberCodegen member, @NotNull ClassDescriptor descriptor) {
230            for (KotlinType type : descriptor.getTypeConstructor().getSupertypes()) {
231                for (TypeProjection supertypeArgument : type.getArguments()) {
232                    TypeParameterDescriptor parameterDescriptor = TypeUtils.getTypeParameterDescriptorOrNull(supertypeArgument.getType());
233                    if (parameterDescriptor != null && parameterDescriptor.isReified()) {
234                        member.getReifiedTypeParametersUsages().addUsedReifiedParameter(parameterDescriptor.getName().asString());
235                    }
236                }
237            }
238        }
239    
240        private static class ObjectLiteralResult {
241            private final boolean wereReifiedMarkers;
242            private final ClassDescriptor classDescriptor;
243    
244            public ObjectLiteralResult(boolean wereReifiedMarkers, @NotNull ClassDescriptor classDescriptor) {
245                this.wereReifiedMarkers = wereReifiedMarkers;
246                this.classDescriptor = classDescriptor;
247            }
248        }
249    
250        @NotNull
251        private StackValue castToRequiredTypeOfInterfaceIfNeeded(
252                StackValue inner,
253                @NotNull ClassDescriptor provided,
254                @NotNull ClassDescriptor required
255        ) {
256            if (!isJvmInterface(provided) && isJvmInterface(required)) {
257                return StackValue.coercion(inner, asmType(required.getDefaultType()));
258            }
259    
260            return inner;
261        }
262    
263        public StackValue genQualified(StackValue receiver, KtElement selector) {
264            return genQualified(receiver, selector, this);
265        }
266    
267        private StackValue genQualified(StackValue receiver, KtElement selector, KtVisitor<StackValue, StackValue> visitor) {
268            if (tempVariables.containsKey(selector)) {
269                throw new IllegalStateException("Inconsistent state: expression saved to a temporary variable is a selector");
270            }
271            if (!(selector instanceof KtBlockExpression)) {
272                markStartLineNumber(selector);
273            }
274            try {
275                if (selector instanceof KtExpression) {
276                    StackValue samValue = genSamInterfaceValue((KtExpression) selector, visitor);
277                    if (samValue != null) {
278                        return samValue;
279                    }
280                }
281    
282                StackValue stackValue = selector.accept(visitor, receiver);
283    
284                RuntimeAssertionInfo runtimeAssertionInfo = null;
285                if (selector instanceof KtExpression) {
286                    runtimeAssertionInfo = bindingContext.get(BindingContextSlicesKt.getRUNTIME_ASSERTION_INFO(), (KtExpression) selector);
287                }
288    
289                if (BuiltinSpecialBridgesKt.isValueArgumentForCallToMethodWithTypeCheckBarrier(selector, bindingContext)) return stackValue;
290    
291                return genNotNullAssertions(state, stackValue, runtimeAssertionInfo);
292            }
293            catch (ProcessCanceledException e) {
294                throw e;
295            }
296            catch (CompilationException e) {
297                throw e;
298            }
299            catch (Throwable error) {
300                String message = error.getMessage();
301                throw new CompilationException(message != null ? message : "null", error, selector);
302            }
303        }
304    
305        public StackValue gen(KtElement expr) {
306            StackValue tempVar = tempVariables.get(expr);
307            return tempVar != null ? tempVar : genQualified(StackValue.none(), expr);
308        }
309    
310        public void gen(KtElement expr, Type type) {
311            StackValue value = Type.VOID_TYPE.equals(type) ? genStatement(expr) : gen(expr);
312            // for repl store the result of the last line into special field
313            if (value.type != Type.VOID_TYPE && state.getReplSpecific().getShouldGenerateScriptResultValue()) {
314                ScriptContext context = getScriptContext();
315                if (expr == context.getLastStatement()) {
316                    StackValue.Field resultValue = StackValue.field(context.getResultFieldInfo(), StackValue.LOCAL_0);
317                    resultValue.store(value, v);
318                    state.getReplSpecific().setHasResult(true);
319                    return;
320                }
321            }
322    
323            value.put(type, v);
324        }
325    
326        @NotNull
327        private ScriptContext getScriptContext() {
328            CodegenContext context = getContext();
329            while (!(context instanceof ScriptContext)) {
330                context = context.getParentContext();
331            }
332            return (ScriptContext) context;
333        }
334    
335        public StackValue genLazy(KtElement expr, Type type) {
336            StackValue value = gen(expr);
337            return StackValue.coercion(value, type);
338        }
339    
340        private StackValue genStatement(KtElement statement) {
341            return genQualified(StackValue.none(), statement, statementVisitor);
342        }
343    
344        @Override
345        public StackValue visitClass(@NotNull KtClass klass, StackValue data) {
346            return visitClassOrObject(klass);
347        }
348    
349        private StackValue visitClassOrObject(KtClassOrObject declaration) {
350            ClassDescriptor descriptor = bindingContext.get(CLASS, declaration);
351            assert descriptor != null;
352    
353            Type asmType = asmTypeForAnonymousClass(bindingContext, declaration);
354            ClassBuilder classBuilder = state.getFactory().newVisitor(JvmDeclarationOriginKt.OtherOrigin(declaration, descriptor), asmType, declaration.getContainingFile());
355    
356            ClassContext objectContext = context.intoAnonymousClass(descriptor, this, OwnerKind.IMPLEMENTATION);
357            new ImplementationBodyCodegen(declaration, objectContext, classBuilder, state, getParentCodegen(), /* isLocal = */ true).generate();
358    
359            if (declaration instanceof KtClass && ((KtClass) declaration).isInterface()) {
360                Type traitImplType = state.getTypeMapper().mapDefaultImpls(descriptor);
361                ClassBuilder traitImplBuilder = state.getFactory().newVisitor(JvmDeclarationOriginKt.TraitImpl(declaration, descriptor), traitImplType, declaration.getContainingFile());
362                ClassContext traitImplContext = context.intoAnonymousClass(descriptor, this, OwnerKind.DEFAULT_IMPLS);
363                new InterfaceImplBodyCodegen(declaration, traitImplContext, traitImplBuilder, state, parentCodegen).generate();
364            }
365    
366            return StackValue.none();
367        }
368    
369        @Override
370        public StackValue visitObjectDeclaration(@NotNull KtObjectDeclaration declaration, StackValue data) {
371            return visitClassOrObject(declaration);
372        }
373    
374        @Override
375        public StackValue visitExpression(@NotNull KtExpression expression, StackValue receiver) {
376            throw new UnsupportedOperationException("Codegen for " + expression + " is not yet implemented");
377        }
378    
379        @Override
380        public StackValue visitSuperExpression(@NotNull KtSuperExpression expression, StackValue data) {
381            return StackValue.thisOrOuter(this, getSuperCallLabelTarget(context, expression), true, false);
382        }
383    
384        @NotNull
385        public static ClassDescriptor getSuperCallLabelTarget(
386                @NotNull CodegenContext<?> context,
387                @NotNull KtSuperExpression expression
388        ) {
389            BindingContext bindingContext = context.getState().getBindingContext();
390            PsiElement labelPsi = bindingContext.get(LABEL_TARGET, expression.getTargetLabel());
391            ClassDescriptor labelTarget = (ClassDescriptor) bindingContext.get(DECLARATION_TO_DESCRIPTOR, labelPsi);
392            DeclarationDescriptor descriptor = bindingContext.get(REFERENCE_TARGET, expression.getInstanceReference());
393            // "super<descriptor>@labelTarget"
394            if (labelTarget != null) {
395                return labelTarget;
396            }
397            assert descriptor instanceof ClassDescriptor : "Don't know how to generate super-call to not a class";
398            CodegenContext result = getParentContextSubclassOf((ClassDescriptor) descriptor, context);
399            assert result != null : "Can't find parent context for " + descriptor;
400            return result.getThisDescriptor();
401        }
402    
403        @NotNull
404        private Type asmType(@NotNull KotlinType type) {
405            return typeMapper.mapType(type);
406        }
407    
408        @NotNull
409        public Type expressionType(@Nullable KtExpression expression) {
410            KotlinType type = expressionJetType(expression);
411            return type == null ? Type.VOID_TYPE : asmType(type);
412        }
413    
414        @Nullable
415        public KotlinType expressionJetType(@Nullable KtExpression expression) {
416            return expression != null ? bindingContext.getType(expression) : null;
417        }
418    
419        @Override
420        public StackValue visitParenthesizedExpression(@NotNull KtParenthesizedExpression expression, StackValue receiver) {
421            return genQualified(receiver, expression.getExpression());
422        }
423    
424        @Override
425        public StackValue visitAnnotatedExpression(@NotNull KtAnnotatedExpression expression, StackValue receiver) {
426            return genQualified(receiver, expression.getBaseExpression());
427        }
428    
429        private static boolean isEmptyExpression(@Nullable KtElement expr) {
430            if (expr == null) {
431                return true;
432            }
433            if (expr instanceof KtBlockExpression) {
434                KtBlockExpression blockExpression = (KtBlockExpression) expr;
435                List<KtExpression> statements = blockExpression.getStatements();
436                if (statements.size() == 0 || statements.size() == 1 && isEmptyExpression(statements.get(0))) {
437                    return true;
438                }
439            }
440            return false;
441        }
442    
443        @Override
444        public StackValue visitIfExpression(@NotNull KtIfExpression expression, StackValue receiver) {
445            return generateIfExpression(expression, false);
446        }
447    
448        /* package */ StackValue generateIfExpression(@NotNull final KtIfExpression expression, final boolean isStatement) {
449            final Type asmType = isStatement ? Type.VOID_TYPE : expressionType(expression);
450            final StackValue condition = gen(expression.getCondition());
451    
452            final KtExpression thenExpression = expression.getThen();
453            final KtExpression elseExpression = expression.getElse();
454    
455            if (isEmptyExpression(thenExpression)) {
456                if (isEmptyExpression(elseExpression)) {
457                    return StackValue.coercion(condition, asmType);
458                }
459                return generateSingleBranchIf(condition, expression, elseExpression, false, isStatement);
460            }
461            else {
462                if (isEmptyExpression(elseExpression)) {
463                    return generateSingleBranchIf(condition, expression, thenExpression, true, isStatement);
464                }
465            }
466    
467            return StackValue.operation(asmType, new Function1<InstructionAdapter, Unit>() {
468                @Override
469                public Unit invoke(InstructionAdapter v) {
470                    Label elseLabel = new Label();
471                    BranchedValue.Companion.condJump(condition, elseLabel, true, v);
472    
473                    Label end = new Label();
474    
475                    gen(thenExpression, asmType);
476    
477                    v.goTo(end);
478                    v.mark(elseLabel);
479    
480                    gen(elseExpression, asmType);
481    
482                    markLineNumber(expression, isStatement);
483                    v.mark(end);
484                    return Unit.INSTANCE;
485                }
486            });
487        }
488    
489        @Override
490        public StackValue visitWhileExpression(@NotNull KtWhileExpression expression, StackValue receiver) {
491            Label condition = new Label();
492            v.mark(condition);
493    
494            Label end = new Label();
495            blockStackElements.push(new LoopBlockStackElement(end, condition, targetLabel(expression)));
496    
497            StackValue conditionValue = gen(expression.getCondition());
498            BranchedValue.Companion.loopJump(conditionValue, end, true, v);
499    
500            generateLoopBody(expression.getBody());
501    
502            v.goTo(condition);
503    
504            v.mark(end);
505    
506            blockStackElements.pop();
507    
508            return StackValue.onStack(Type.VOID_TYPE);
509        }
510    
511    
512        @Override
513        public StackValue visitDoWhileExpression(@NotNull KtDoWhileExpression expression, StackValue receiver) {
514            Label beginLoopLabel = new Label();
515            v.mark(beginLoopLabel);
516    
517            Label breakLabel = new Label();
518            Label continueLabel = new Label();
519    
520            blockStackElements.push(new LoopBlockStackElement(breakLabel, continueLabel, targetLabel(expression)));
521    
522            PseudoInsnsKt.fakeAlwaysFalseIfeq(v, continueLabel);
523    
524            KtExpression body = expression.getBody();
525            KtExpression condition = expression.getCondition();
526            StackValue conditionValue;
527    
528            if (body instanceof KtBlockExpression) {
529                // If body's a block, it can contain variable declarations which may be used in the condition of a do-while loop.
530                // We handle this case separately because otherwise such variable will be out of the frame map after the block ends
531                List<KtExpression> doWhileStatements = ((KtBlockExpression) body).getStatements();
532    
533                List<KtExpression> statements = new ArrayList<KtExpression>(doWhileStatements.size() + 1);
534                statements.addAll(doWhileStatements);
535                statements.add(condition);
536    
537                conditionValue = generateBlock(statements, false, continueLabel, null);
538            }
539            else {
540                if (body != null) {
541                    gen(body, Type.VOID_TYPE);
542                }
543                v.mark(continueLabel);
544                conditionValue = gen(condition);
545            }
546    
547            BranchedValue.Companion.loopJump(conditionValue, beginLoopLabel, false, v);
548            v.mark(breakLabel);
549    
550            blockStackElements.pop();
551            return StackValue.none();
552        }
553    
554        @Override
555        public StackValue visitForExpression(@NotNull KtForExpression forExpression, StackValue receiver) {
556            // Is it a "1..2" or so
557            RangeCodegenUtil.BinaryCall binaryCall = RangeCodegenUtil.getRangeAsBinaryCall(forExpression);
558            if (binaryCall != null) {
559                ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCall(binaryCall.op, bindingContext);
560                if (resolvedCall != null) {
561                    if (RangeCodegenUtil.isOptimizableRangeTo(resolvedCall.getResultingDescriptor())) {
562                        generateForLoop(new ForInRangeLiteralLoopGenerator(forExpression, binaryCall));
563                        return StackValue.none();
564                    }
565                }
566            }
567    
568            KtExpression loopRange = forExpression.getLoopRange();
569            assert loopRange != null;
570            KotlinType loopRangeType = bindingContext.getType(loopRange);
571            assert loopRangeType != null;
572            Type asmLoopRangeType = asmType(loopRangeType);
573            if (asmLoopRangeType.getSort() == Type.ARRAY) {
574                generateForLoop(new ForInArrayLoopGenerator(forExpression));
575                return StackValue.none();
576            }
577    
578            if (RangeCodegenUtil.isRange(loopRangeType)) {
579                generateForLoop(new ForInRangeInstanceLoopGenerator(forExpression));
580                return StackValue.none();
581            }
582    
583            if (RangeCodegenUtil.isProgression(loopRangeType)) {
584                generateForLoop(new ForInProgressionExpressionLoopGenerator(forExpression));
585                return StackValue.none();
586            }
587    
588            generateForLoop(new IteratorForLoopGenerator(forExpression));
589            return StackValue.none();
590        }
591    
592        private OwnerKind contextKind() {
593            return context.getContextKind();
594        }
595    
596        private void generateForLoop(AbstractForLoopGenerator generator) {
597            Label loopExit = new Label();
598            Label loopEntry = new Label();
599            Label continueLabel = new Label();
600    
601            generator.beforeLoop();
602            generator.checkEmptyLoop(loopExit);
603    
604            v.mark(loopEntry);
605            generator.checkPreCondition(loopExit);
606    
607            // Some forms of for-loop can be optimized as post-condition loops.
608            PseudoInsnsKt.fakeAlwaysFalseIfeq(v, continueLabel);
609    
610            generator.beforeBody();
611            blockStackElements.push(new LoopBlockStackElement(loopExit, continueLabel, targetLabel(generator.forExpression)));
612            generator.body();
613            blockStackElements.pop();
614            v.mark(continueLabel);
615            generator.afterBody(loopExit);
616    
617            v.goTo(loopEntry);
618    
619            v.mark(loopExit);
620            generator.afterLoop();
621        }
622    
623        private abstract class AbstractForLoopGenerator {
624    
625            // for (e : E in c) {...}
626            protected final KtForExpression forExpression;
627            private final Label bodyStart = new Label();
628            private final Label bodyEnd = new Label();
629            private final List<Runnable> leaveVariableTasks = Lists.newArrayList();
630    
631            protected final KotlinType elementType;
632            protected final Type asmElementType;
633    
634            protected int loopParameterVar;
635    
636            private AbstractForLoopGenerator(@NotNull KtForExpression forExpression) {
637                this.forExpression = forExpression;
638                this.elementType = getElementType(forExpression);
639                this.asmElementType = asmType(elementType);
640            }
641    
642            @NotNull
643            private KotlinType getElementType(KtForExpression forExpression) {
644                KtExpression loopRange = forExpression.getLoopRange();
645                assert loopRange != null;
646                ResolvedCall<FunctionDescriptor> nextCall = getNotNull(bindingContext,
647                                                                       LOOP_RANGE_NEXT_RESOLVED_CALL, loopRange,
648                                                                       "No next() function " + DiagnosticUtils.atLocation(loopRange));
649                //noinspection ConstantConditions
650                return nextCall.getResultingDescriptor().getReturnType();
651            }
652    
653            public void beforeLoop() {
654                KtParameter loopParameter = forExpression.getLoopParameter();
655                if (loopParameter != null) {
656                    // E e = tmp<iterator>.next()
657                    final VariableDescriptor parameterDescriptor = bindingContext.get(VALUE_PARAMETER, loopParameter);
658                    @SuppressWarnings("ConstantConditions") final Type asmTypeForParameter = asmType(parameterDescriptor.getType());
659                    loopParameterVar = myFrameMap.enter(parameterDescriptor, asmTypeForParameter);
660                    scheduleLeaveVariable(new Runnable() {
661                        @Override
662                        public void run() {
663                            myFrameMap.leave(parameterDescriptor);
664                            v.visitLocalVariable(parameterDescriptor.getName().asString(),
665                                                 asmTypeForParameter.getDescriptor(), null,
666                                                 bodyStart, bodyEnd,
667                                                 loopParameterVar);
668                        }
669                    });
670                }
671                else {
672                    KtDestructuringDeclaration multiParameter = forExpression.getDestructuringParameter();
673                    assert multiParameter != null;
674    
675                    // E tmp<e> = tmp<iterator>.next()
676                    loopParameterVar = createLoopTempVariable(asmElementType);
677                }
678            }
679    
680            public abstract void checkEmptyLoop(@NotNull Label loopExit);
681    
682            public abstract void checkPreCondition(@NotNull Label loopExit);
683    
684            public void beforeBody() {
685                v.mark(bodyStart);
686    
687                assignToLoopParameter();
688    
689                if (forExpression.getLoopParameter() == null) {
690                    KtDestructuringDeclaration multiParameter = forExpression.getDestructuringParameter();
691                    assert multiParameter != null;
692    
693                    generateMultiVariables(multiParameter.getEntries());
694                }
695            }
696    
697            private void generateMultiVariables(List<KtDestructuringDeclarationEntry> entries) {
698                for (KtDestructuringDeclarationEntry variableDeclaration : entries) {
699                    final VariableDescriptor componentDescriptor = bindingContext.get(VARIABLE, variableDeclaration);
700    
701                    @SuppressWarnings("ConstantConditions") final Type componentAsmType = asmType(componentDescriptor.getReturnType());
702                    final int componentVarIndex = myFrameMap.enter(componentDescriptor, componentAsmType);
703                    final Label variableStartLabel = new Label();
704                    scheduleLeaveVariable(new Runnable() {
705                        @Override
706                        public void run() {
707                            myFrameMap.leave(componentDescriptor);
708                            v.visitLocalVariable(componentDescriptor.getName().asString(),
709                                                 componentAsmType.getDescriptor(), null,
710                                                 variableStartLabel, bodyEnd,
711                                                 componentVarIndex);
712                        }
713                    });
714    
715                    ResolvedCall<FunctionDescriptor> resolvedCall = bindingContext.get(COMPONENT_RESOLVED_CALL, variableDeclaration);
716                    assert resolvedCall != null : "Resolved call is null for " + variableDeclaration.getText();
717                    Call call = makeFakeCall(new TransientReceiver(elementType));
718    
719                    StackValue value = invokeFunction(call, resolvedCall, StackValue.local(loopParameterVar, asmElementType));
720                    StackValue.local(componentVarIndex, componentAsmType).store(value, v);
721                    v.visitLabel(variableStartLabel);
722                }
723            }
724    
725            protected abstract void assignToLoopParameter();
726    
727            protected abstract void increment(@NotNull Label loopExit);
728    
729            public void body() {
730                generateLoopBody(forExpression.getBody());
731            }
732    
733            private void scheduleLeaveVariable(Runnable runnable) {
734                leaveVariableTasks.add(runnable);
735            }
736    
737            protected int createLoopTempVariable(final Type type) {
738                int varIndex = myFrameMap.enterTemp(type);
739                scheduleLeaveVariable(new Runnable() {
740                    @Override
741                    public void run() {
742                        myFrameMap.leaveTemp(type);
743                    }
744                });
745                return varIndex;
746            }
747    
748            public void afterBody(@NotNull Label loopExit) {
749                markStartLineNumber(forExpression);
750    
751                increment(loopExit);
752    
753                v.mark(bodyEnd);
754            }
755    
756            public void afterLoop() {
757                for (Runnable task : Lists.reverse(leaveVariableTasks)) {
758                    task.run();
759                }
760            }
761    
762            // This method consumes range/progression from stack
763            // The result is stored to local variable
764            protected void generateRangeOrProgressionProperty(Type loopRangeType, String getterName, Type elementType, int varToStore) {
765                v.invokevirtual(loopRangeType.getInternalName(), getterName, "()" + elementType.getDescriptor(), false);
766                v.store(varToStore, elementType);
767            }
768        }
769    
770        private void generateLoopBody(@Nullable KtExpression body) {
771            if (body != null) {
772                gen(body, Type.VOID_TYPE);
773            }
774        }
775    
776        private class IteratorForLoopGenerator extends AbstractForLoopGenerator {
777    
778            private int iteratorVarIndex;
779            private final ResolvedCall<FunctionDescriptor> iteratorCall;
780            private final ResolvedCall<FunctionDescriptor> nextCall;
781            private final Type asmTypeForIterator;
782    
783            private IteratorForLoopGenerator(@NotNull KtForExpression forExpression) {
784                super(forExpression);
785    
786                KtExpression loopRange = forExpression.getLoopRange();
787                assert loopRange != null;
788                this.iteratorCall = getNotNull(bindingContext,
789                                               LOOP_RANGE_ITERATOR_RESOLVED_CALL, loopRange,
790                                               "No .iterator() function " + DiagnosticUtils.atLocation(loopRange));
791    
792                KotlinType iteratorType = iteratorCall.getResultingDescriptor().getReturnType();
793                assert iteratorType != null;
794                this.asmTypeForIterator = asmType(iteratorType);
795    
796                this.nextCall = getNotNull(bindingContext,
797                                           LOOP_RANGE_NEXT_RESOLVED_CALL, loopRange,
798                                           "No next() function " + DiagnosticUtils.atLocation(loopRange));
799            }
800    
801            @Override
802            public void beforeLoop() {
803                super.beforeLoop();
804    
805                // Iterator<E> tmp<iterator> = c.iterator()
806    
807                iteratorVarIndex = createLoopTempVariable(asmTypeForIterator);
808    
809                StackValue.local(iteratorVarIndex, asmTypeForIterator).store(invokeFunction(iteratorCall, StackValue.none()), v);
810            }
811    
812            @Override
813            public void checkEmptyLoop(@NotNull Label loopExit) {
814            }
815    
816            @Override
817            public void checkPreCondition(@NotNull Label loopExit) {
818                // tmp<iterator>.hasNext()
819    
820                KtExpression loopRange = forExpression.getLoopRange();
821                @SuppressWarnings("ConstantConditions") ResolvedCall<FunctionDescriptor> hasNextCall = getNotNull(bindingContext,
822                                                                          LOOP_RANGE_HAS_NEXT_RESOLVED_CALL, loopRange,
823                                                                          "No hasNext() function " + DiagnosticUtils.atLocation(loopRange));
824                @SuppressWarnings("ConstantConditions") Call fakeCall = makeFakeCall(new TransientReceiver(iteratorCall.getResultingDescriptor().getReturnType()));
825                StackValue result = invokeFunction(fakeCall, hasNextCall, StackValue.local(iteratorVarIndex, asmTypeForIterator));
826                result.put(result.type, v);
827    
828                FunctionDescriptor hasNext = hasNextCall.getResultingDescriptor();
829                KotlinType type = hasNext.getReturnType();
830                assert type != null && KotlinTypeChecker.DEFAULT.isSubtypeOf(type, DescriptorUtilsKt.getBuiltIns(hasNext).getBooleanType());
831    
832                Type asmType = asmType(type);
833                StackValue.coerce(asmType, Type.BOOLEAN_TYPE, v);
834                v.ifeq(loopExit);
835            }
836    
837            @Override
838            protected void assignToLoopParameter() {
839                @SuppressWarnings("ConstantConditions") Call fakeCall =
840                        makeFakeCall(new TransientReceiver(iteratorCall.getResultingDescriptor().getReturnType()));
841                StackValue value = invokeFunction(fakeCall, nextCall, StackValue.local(iteratorVarIndex, asmTypeForIterator));
842                //noinspection ConstantConditions
843                StackValue.local(loopParameterVar, asmType(nextCall.getResultingDescriptor().getReturnType())).store(value, v);
844            }
845    
846            @Override
847            protected void increment(@NotNull Label loopExit) {
848            }
849        }
850    
851        private class ForInArrayLoopGenerator extends AbstractForLoopGenerator {
852            private int indexVar;
853            private int arrayVar;
854            private final KotlinType loopRangeType;
855    
856            private ForInArrayLoopGenerator(@NotNull KtForExpression forExpression) {
857                super(forExpression);
858                loopRangeType = bindingContext.getType(forExpression.getLoopRange());
859            }
860    
861            @Override
862            public void beforeLoop() {
863                super.beforeLoop();
864    
865                indexVar = createLoopTempVariable(Type.INT_TYPE);
866    
867                KtExpression loopRange = forExpression.getLoopRange();
868                StackValue value = gen(loopRange);
869                Type asmLoopRangeType = asmType(loopRangeType);
870                if (value instanceof StackValue.Local && value.type.equals(asmLoopRangeType)) {
871                    arrayVar = ((StackValue.Local) value).index; // no need to copy local variable into another variable
872                }
873                else {
874                    arrayVar = createLoopTempVariable(OBJECT_TYPE);
875                    value.put(asmLoopRangeType, v);
876                    v.store(arrayVar, OBJECT_TYPE);
877                }
878    
879                v.iconst(0);
880                v.store(indexVar, Type.INT_TYPE);
881            }
882    
883            @Override
884            public void checkEmptyLoop(@NotNull Label loopExit) {
885            }
886    
887            @Override
888            public void checkPreCondition(@NotNull Label loopExit) {
889                v.load(indexVar, Type.INT_TYPE);
890                v.load(arrayVar, OBJECT_TYPE);
891                v.arraylength();
892                v.ificmpge(loopExit);
893            }
894    
895            @Override
896            protected void assignToLoopParameter() {
897                Type arrayElParamType;
898                if (KotlinBuiltIns.isArray(loopRangeType)) {
899                    arrayElParamType = boxType(asmElementType);
900                }
901                else {
902                    arrayElParamType = asmElementType;
903                }
904    
905                v.load(arrayVar, OBJECT_TYPE);
906                v.load(indexVar, Type.INT_TYPE);
907                v.aload(arrayElParamType);
908                StackValue.onStack(arrayElParamType).put(asmElementType, v);
909                v.store(loopParameterVar, asmElementType);
910            }
911    
912            @Override
913            protected void increment(@NotNull Label loopExit) {
914                v.iinc(indexVar, 1);
915            }
916        }
917    
918        private abstract class AbstractForInProgressionOrRangeLoopGenerator extends AbstractForLoopGenerator {
919            protected int endVar;
920    
921            private AbstractForInProgressionOrRangeLoopGenerator(@NotNull KtForExpression forExpression) {
922                super(forExpression);
923    
924                switch (asmElementType.getSort()) {
925                    case Type.INT:
926                    case Type.BYTE:
927                    case Type.SHORT:
928                    case Type.CHAR:
929                    case Type.LONG:
930                        break;
931    
932                    default:
933                        throw new IllegalStateException("Unexpected range element type: " + asmElementType);
934                }
935            }
936    
937            @Override
938            public void beforeLoop() {
939                super.beforeLoop();
940    
941                endVar = createLoopTempVariable(asmElementType);
942            }
943    
944            protected void checkPostCondition(@NotNull Label loopExit) {
945                assert endVar != -1 :
946                        "endVar must be allocated, endVar = " + endVar;
947    
948                v.load(loopParameterVar, asmElementType);
949                v.load(endVar, asmElementType);
950                if (asmElementType.getSort() == Type.LONG) {
951                    v.lcmp();
952                    v.ifeq(loopExit);
953                }
954                else {
955                    v.ificmpeq(loopExit);
956                }
957            }
958    
959            @Override
960            public void checkPreCondition(@NotNull Label loopExit) {
961            }
962        }
963    
964        private abstract class AbstractForInRangeLoopGenerator extends AbstractForInProgressionOrRangeLoopGenerator {
965            private AbstractForInRangeLoopGenerator(@NotNull KtForExpression forExpression) {
966                super(forExpression);
967            }
968    
969            @Override
970            public void beforeLoop() {
971                super.beforeLoop();
972    
973                storeRangeStartAndEnd();
974            }
975    
976            protected abstract void storeRangeStartAndEnd();
977    
978            @Override
979            public void checkEmptyLoop(@NotNull Label loopExit) {
980    
981                v.load(loopParameterVar, asmElementType);
982                v.load(endVar, asmElementType);
983                if (asmElementType.getSort() == Type.LONG) {
984                    v.lcmp();
985                    v.ifgt(loopExit);
986                }
987                else {
988                    v.ificmpgt(loopExit);
989                }
990            }
991    
992            @Override
993            protected void assignToLoopParameter() {
994            }
995    
996            @Override
997            protected void increment(@NotNull Label loopExit) {
998                checkPostCondition(loopExit);
999    
1000                if (asmElementType == Type.INT_TYPE) {
1001                    v.iinc(loopParameterVar, 1);
1002                }
1003                else {
1004                    v.load(loopParameterVar, asmElementType);
1005                    genIncrement(asmElementType, 1, v);
1006                    v.store(loopParameterVar, asmElementType);
1007                }
1008            }
1009        }
1010    
1011        private class ForInRangeLiteralLoopGenerator extends AbstractForInRangeLoopGenerator {
1012            private final RangeCodegenUtil.BinaryCall rangeCall;
1013    
1014            private ForInRangeLiteralLoopGenerator(
1015                    @NotNull KtForExpression forExpression,
1016                    @NotNull RangeCodegenUtil.BinaryCall rangeCall
1017            ) {
1018                super(forExpression);
1019                this.rangeCall = rangeCall;
1020            }
1021    
1022            @Override
1023            protected void storeRangeStartAndEnd() {
1024                gen(rangeCall.left, asmElementType);
1025                v.store(loopParameterVar, asmElementType);
1026    
1027                gen(rangeCall.right, asmElementType);
1028                v.store(endVar, asmElementType);
1029            }
1030        }
1031    
1032        private class ForInRangeInstanceLoopGenerator extends AbstractForInRangeLoopGenerator {
1033            private ForInRangeInstanceLoopGenerator(@NotNull KtForExpression forExpression) {
1034                super(forExpression);
1035            }
1036    
1037            @Override
1038            protected void storeRangeStartAndEnd() {
1039                KotlinType loopRangeType = bindingContext.getType(forExpression.getLoopRange());
1040                assert loopRangeType != null;
1041                Type asmLoopRangeType = asmType(loopRangeType);
1042                gen(forExpression.getLoopRange(), asmLoopRangeType);
1043                v.dup();
1044    
1045                // ranges inherit first and last from corresponding progressions
1046                generateRangeOrProgressionProperty(asmLoopRangeType, "getFirst", asmElementType, loopParameterVar);
1047                generateRangeOrProgressionProperty(asmLoopRangeType, "getLast", asmElementType, endVar);
1048            }
1049        }
1050    
1051        private class ForInProgressionExpressionLoopGenerator extends AbstractForInProgressionOrRangeLoopGenerator {
1052            private int incrementVar;
1053            private Type incrementType;
1054    
1055            private ForInProgressionExpressionLoopGenerator(@NotNull KtForExpression forExpression) {
1056                super(forExpression);
1057            }
1058    
1059            @Override
1060            public void beforeLoop() {
1061                super.beforeLoop();
1062    
1063                incrementVar = createLoopTempVariable(asmElementType);
1064    
1065                KotlinType loopRangeType = bindingContext.getType(forExpression.getLoopRange());
1066                assert loopRangeType != null;
1067                Type asmLoopRangeType = asmType(loopRangeType);
1068    
1069                Collection<PropertyDescriptor> incrementProp =
1070                        loopRangeType.getMemberScope().getContributedVariables(Name.identifier("step"), NoLookupLocation.FROM_BACKEND);
1071                assert incrementProp.size() == 1 : loopRangeType + " " + incrementProp.size();
1072                incrementType = asmType(incrementProp.iterator().next().getType());
1073    
1074                gen(forExpression.getLoopRange(), asmLoopRangeType);
1075                v.dup();
1076                v.dup();
1077    
1078                generateRangeOrProgressionProperty(asmLoopRangeType, "getFirst", asmElementType, loopParameterVar);
1079                generateRangeOrProgressionProperty(asmLoopRangeType, "getLast", asmElementType, endVar);
1080                generateRangeOrProgressionProperty(asmLoopRangeType, "getStep", incrementType, incrementVar);
1081            }
1082    
1083            @Override
1084            public void checkEmptyLoop(@NotNull Label loopExit) {
1085    
1086                v.load(loopParameterVar, asmElementType);
1087                v.load(endVar, asmElementType);
1088                v.load(incrementVar, incrementType);
1089    
1090                Label negativeIncrement = new Label();
1091                Label afterIf = new Label();
1092    
1093                if (asmElementType.getSort() == Type.LONG) {
1094                    v.lconst(0L);
1095                    v.lcmp();
1096                    v.ifle(negativeIncrement); // if increment < 0, jump
1097    
1098                    // increment > 0
1099                    v.lcmp();
1100                    v.ifgt(loopExit);
1101                    v.goTo(afterIf);
1102    
1103                    // increment < 0
1104                    v.mark(negativeIncrement);
1105                    v.lcmp();
1106                    v.iflt(loopExit);
1107                    v.mark(afterIf);
1108                }
1109                else {
1110                    v.ifle(negativeIncrement); // if increment < 0, jump
1111    
1112                    // increment > 0
1113                    v.ificmpgt(loopExit);
1114                    v.goTo(afterIf);
1115    
1116                    // increment < 0
1117                    v.mark(negativeIncrement);
1118                    v.ificmplt(loopExit);
1119                    v.mark(afterIf);
1120                }
1121            }
1122    
1123            @Override
1124            protected void assignToLoopParameter() {
1125            }
1126    
1127            @Override
1128            protected void increment(@NotNull Label loopExit) {
1129                checkPostCondition(loopExit);
1130    
1131                v.load(loopParameterVar, asmElementType);
1132                v.load(incrementVar, asmElementType);
1133                v.add(asmElementType);
1134    
1135                if (asmElementType == Type.BYTE_TYPE || asmElementType == Type.SHORT_TYPE || asmElementType == Type.CHAR_TYPE) {
1136                    StackValue.coerce(Type.INT_TYPE, asmElementType, v);
1137                }
1138    
1139                v.store(loopParameterVar, asmElementType);
1140            }
1141        }
1142    
1143    
1144        @Override
1145        public StackValue visitBreakExpression(@NotNull KtBreakExpression expression, StackValue receiver) {
1146            return generateBreakOrContinueExpression(expression, true, new Label());
1147        }
1148    
1149        @Override
1150        public StackValue visitContinueExpression(@NotNull KtContinueExpression expression, StackValue receiver) {
1151            return generateBreakOrContinueExpression(expression, false, new Label());
1152        }
1153    
1154        @NotNull
1155        private StackValue generateBreakOrContinueExpression(
1156                @NotNull KtExpressionWithLabel expression,
1157                boolean isBreak,
1158                final @NotNull Label afterBreakContinueLabel
1159        ) {
1160            assert expression instanceof KtContinueExpression || expression instanceof KtBreakExpression;
1161    
1162            if (!blockStackElements.isEmpty()) {
1163                BlockStackElement stackElement = blockStackElements.peek();
1164    
1165                if (stackElement instanceof FinallyBlockStackElement) {
1166                    FinallyBlockStackElement finallyBlockStackElement = (FinallyBlockStackElement) stackElement;
1167                    //noinspection ConstantConditions
1168                    genFinallyBlockOrGoto(finallyBlockStackElement, null, afterBreakContinueLabel);
1169                }
1170                else if (stackElement instanceof LoopBlockStackElement) {
1171                    LoopBlockStackElement loopBlockStackElement = (LoopBlockStackElement) stackElement;
1172                    KtSimpleNameExpression labelElement = expression.getTargetLabel();
1173                    //noinspection ConstantConditions
1174                    if (labelElement == null ||
1175                        loopBlockStackElement.targetLabel != null &&
1176                        labelElement.getReferencedName().equals(loopBlockStackElement.targetLabel.getReferencedName())) {
1177                        final Label label = isBreak ? loopBlockStackElement.breakLabel : loopBlockStackElement.continueLabel;
1178                        return StackValue.operation(Type.VOID_TYPE, new Function1<InstructionAdapter, Unit>() {
1179                                                        @Override
1180                                                        public Unit invoke(InstructionAdapter adapter) {
1181                                                            PseudoInsnsKt.fixStackAndJump(v, label);
1182                                                            v.mark(afterBreakContinueLabel);
1183                                                            return Unit.INSTANCE;
1184                                                        }
1185                                                    }
1186                        );
1187                    }
1188                }
1189                else {
1190                    throw new UnsupportedOperationException("Wrong BlockStackElement in processing stack");
1191                }
1192    
1193                blockStackElements.pop();
1194                StackValue result = generateBreakOrContinueExpression(expression, isBreak, afterBreakContinueLabel);
1195                blockStackElements.push(stackElement);
1196                return result;
1197            }
1198    
1199            throw new UnsupportedOperationException("Target label for break/continue not found");
1200        }
1201    
1202        private StackValue generateSingleBranchIf(
1203                final StackValue condition,
1204                final KtIfExpression ifExpression,
1205                final KtExpression expression,
1206                final boolean inverse,
1207                final boolean isStatement
1208        ) {
1209            Type targetType = isStatement ? Type.VOID_TYPE : expressionType(ifExpression);
1210            return StackValue.operation(targetType, new Function1<InstructionAdapter, Unit>() {
1211                @Override
1212                public Unit invoke(InstructionAdapter v) {
1213                    Label elseLabel = new Label();
1214                    BranchedValue.Companion.condJump(condition, elseLabel, inverse, v);
1215    
1216                    if (isStatement) {
1217                        gen(expression, Type.VOID_TYPE);
1218                        v.mark(elseLabel);
1219                    }
1220                    else {
1221                        Type targetType = expressionType(ifExpression);
1222                        gen(expression, targetType);
1223                        Label end = new Label();
1224                        v.goTo(end);
1225    
1226                        v.mark(elseLabel);
1227                        StackValue.putUnitInstance(v);
1228    
1229                        markStartLineNumber(ifExpression);
1230                        v.mark(end);
1231                    }
1232                    return null;
1233                }
1234            });
1235        }
1236    
1237        @Override
1238        public StackValue visitConstantExpression(@NotNull KtConstantExpression expression, StackValue receiver) {
1239            ConstantValue<?> compileTimeValue = getCompileTimeConstant(expression, bindingContext);
1240            assert compileTimeValue != null;
1241            return StackValue.constant(compileTimeValue.getValue(), expressionType(expression));
1242        }
1243    
1244        @Nullable
1245        public static ConstantValue<?> getCompileTimeConstant(@NotNull KtExpression expression, @NotNull BindingContext bindingContext) {
1246            return getCompileTimeConstant(expression, bindingContext, false);
1247        }
1248    
1249        @Nullable
1250        public static ConstantValue<?> getCompileTimeConstant(
1251                @NotNull KtExpression expression,
1252                @NotNull BindingContext bindingContext,
1253                boolean checkPure
1254        ) {
1255            CompileTimeConstant<?> compileTimeValue = ConstantExpressionEvaluator.getConstant(expression, bindingContext);
1256            if (compileTimeValue == null) {
1257                return null;
1258            }
1259    
1260            if (compileTimeValue.getUsesNonConstValAsConstant() || (checkPure && !compileTimeValue.getParameters().isPure())) return null;
1261    
1262            KotlinType expectedType = bindingContext.getType(expression);
1263            return compileTimeValue.toConstantValue(expectedType);
1264        }
1265    
1266        @Override
1267        public StackValue visitStringTemplateExpression(@NotNull KtStringTemplateExpression expression, StackValue receiver) {
1268            StringBuilder constantValue = new StringBuilder("");
1269            final KtStringTemplateEntry[] entries = expression.getEntries();
1270    
1271            if (entries.length == 1 && entries[0] instanceof KtStringTemplateEntryWithExpression) {
1272                KtExpression expr = entries[0].getExpression();
1273                return genToString(gen(expr), expressionType(expr));
1274            }
1275    
1276            for (KtStringTemplateEntry entry : entries) {
1277                if (entry instanceof KtLiteralStringTemplateEntry) {
1278                    constantValue.append(entry.getText());
1279                }
1280                else if (entry instanceof KtEscapeStringTemplateEntry) {
1281                    constantValue.append(((KtEscapeStringTemplateEntry) entry).getUnescapedValue());
1282                }
1283                else {
1284                    constantValue = null;
1285                    break;
1286                }
1287            }
1288            if (constantValue != null) {
1289                Type type = expressionType(expression);
1290                return StackValue.constant(constantValue.toString(), type);
1291            }
1292            else {
1293                return StackValue.operation(JAVA_STRING_TYPE, new Function1<InstructionAdapter, Unit>() {
1294                    @Override
1295                    public Unit invoke(InstructionAdapter v) {
1296                        genStringBuilderConstructor(v);
1297                        for (KtStringTemplateEntry entry : entries) {
1298                            if (entry instanceof KtStringTemplateEntryWithExpression) {
1299                                invokeAppend(entry.getExpression());
1300                            }
1301                            else {
1302                                String text = entry instanceof KtEscapeStringTemplateEntry
1303                                              ? ((KtEscapeStringTemplateEntry) entry).getUnescapedValue()
1304                                              : entry.getText();
1305                                v.aconst(text);
1306                                genInvokeAppendMethod(v, JAVA_STRING_TYPE);
1307                            }
1308                        }
1309                        v.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
1310                        return Unit.INSTANCE;
1311                    }
1312                });
1313            }
1314        }
1315    
1316        @Override
1317        public StackValue visitBlockExpression(@NotNull KtBlockExpression expression, StackValue receiver) {
1318            return generateBlock(expression, false);
1319        }
1320    
1321        @Override
1322        public StackValue visitNamedFunction(@NotNull KtNamedFunction function, StackValue data) {
1323            return visitNamedFunction(function, data, false);
1324        }
1325    
1326        public StackValue visitNamedFunction(@NotNull KtNamedFunction function, StackValue data, boolean isStatement) {
1327            assert data == StackValue.none();
1328    
1329            if (KtPsiUtil.isScriptDeclaration(function)) {
1330                return StackValue.none();
1331            }
1332    
1333            StackValue closure = genClosure(function, null);
1334            if (isStatement) {
1335                DeclarationDescriptor descriptor = bindingContext.get(DECLARATION_TO_DESCRIPTOR, function);
1336                int index = lookupLocalIndex(descriptor);
1337                closure.put(OBJECT_TYPE, v);
1338                v.store(index, OBJECT_TYPE);
1339                return StackValue.none();
1340            }
1341            else {
1342                return closure;
1343            }
1344        }
1345    
1346        @Override
1347        public StackValue visitLambdaExpression(@NotNull KtLambdaExpression expression, StackValue receiver) {
1348            if (Boolean.TRUE.equals(bindingContext.get(BLOCK, expression))) {
1349                return gen(expression.getFunctionLiteral().getBodyExpression());
1350            }
1351            else {
1352                return genClosure(expression.getFunctionLiteral(), null);
1353            }
1354        }
1355    
1356        @NotNull
1357        private StackValue genClosure(KtDeclarationWithBody declaration, @Nullable SamType samType) {
1358            FunctionDescriptor descriptor = bindingContext.get(FUNCTION, declaration);
1359            assert descriptor != null : "Function is not resolved to descriptor: " + declaration.getText();
1360    
1361            return genClosure(
1362                    declaration, descriptor, new FunctionGenerationStrategy.FunctionDefault(state, descriptor, declaration), samType, null
1363            );
1364        }
1365    
1366        @NotNull
1367        private StackValue genClosure(
1368                @NotNull KtElement declaration,
1369                @NotNull FunctionDescriptor descriptor,
1370                @NotNull FunctionGenerationStrategy strategy,
1371                @Nullable SamType samType,
1372                @Nullable FunctionDescriptor functionReferenceTarget
1373        ) {
1374            ClassBuilder cv = state.getFactory().newVisitor(
1375                    JvmDeclarationOriginKt.OtherOrigin(declaration, descriptor),
1376                    asmTypeForAnonymousClass(bindingContext, descriptor),
1377                    declaration.getContainingFile()
1378            );
1379    
1380            ClosureCodegen closureCodegen = new ClosureCodegen(
1381                    state, declaration, samType, context.intoClosure(descriptor, this, typeMapper),
1382                    functionReferenceTarget, strategy, parentCodegen, cv
1383            );
1384    
1385            closureCodegen.generate();
1386    
1387            if (closureCodegen.getReifiedTypeParametersUsages().wereUsedReifiedParameters()) {
1388                ReifiedTypeInliner.putNeedClassReificationMarker(v);
1389                propagateChildReifiedTypeParametersUsages(closureCodegen.getReifiedTypeParametersUsages());
1390            }
1391    
1392            return closureCodegen.putInstanceOnStack(this);
1393        }
1394    
1395        @Override
1396        public StackValue visitObjectLiteralExpression(@NotNull KtObjectLiteralExpression expression, StackValue receiver) {
1397            final ObjectLiteralResult objectLiteralResult = generateObjectLiteral(expression);
1398            final ClassDescriptor classDescriptor = objectLiteralResult.classDescriptor;
1399            final Type type = typeMapper.mapType(classDescriptor);
1400    
1401            return StackValue.operation(type, new Function1<InstructionAdapter, Unit>() {
1402                @Override
1403                public Unit invoke(InstructionAdapter v) {
1404                    if (objectLiteralResult.wereReifiedMarkers) {
1405                        ReifiedTypeInliner.putNeedClassReificationMarker(v);
1406                    }
1407                    v.anew(type);
1408                    v.dup();
1409    
1410                    pushClosureOnStack(classDescriptor, true, defaultCallGenerator);
1411    
1412                    ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor();
1413                    assert primaryConstructor != null : "There should be primary constructor for object literal";
1414                    ResolvedCall<ConstructorDescriptor> superCall = getDelegationConstructorCall(bindingContext, primaryConstructor);
1415                    if (superCall != null) {
1416                        // For an anonymous object, we should also generate all non-default arguments that it captures for its super call
1417                        ConstructorDescriptor superConstructor = superCall.getResultingDescriptor();
1418                        ConstructorDescriptor constructorToCall = SamCodegenUtil.resolveSamAdapter(superConstructor);
1419                        List<ValueParameterDescriptor> superValueParameters = superConstructor.getValueParameters();
1420                        int params = superValueParameters.size();
1421                        List<Type> superMappedTypes = typeMapper.mapToCallableMethod(constructorToCall, false).getValueParameterTypes();
1422                        assert superMappedTypes.size() >= params : String
1423                                .format("Incorrect number of mapped parameters vs arguments: %d < %d for %s",
1424                                        superMappedTypes.size(), params, classDescriptor);
1425    
1426                        List<ResolvedValueArgument> valueArguments = new ArrayList<ResolvedValueArgument>(params);
1427                        List<ValueParameterDescriptor> valueParameters = new ArrayList<ValueParameterDescriptor>(params);
1428                        List<Type> mappedTypes = new ArrayList<Type>(params);
1429                        for (ValueParameterDescriptor parameter : superValueParameters) {
1430                            ResolvedValueArgument argument = superCall.getValueArguments().get(parameter);
1431                            if (!(argument instanceof DefaultValueArgument)) {
1432                                valueArguments.add(argument);
1433                                valueParameters.add(parameter);
1434                                mappedTypes.add(superMappedTypes.get(parameter.getIndex()));
1435                            }
1436                        }
1437                        ArgumentGenerator argumentGenerator =
1438                                new CallBasedArgumentGenerator(ExpressionCodegen.this, defaultCallGenerator, valueParameters, mappedTypes);
1439    
1440                        argumentGenerator.generate(valueArguments, valueArguments);
1441                    }
1442    
1443                    Collection<ConstructorDescriptor> constructors = classDescriptor.getConstructors();
1444                    assert constructors.size() == 1 : "Unexpected number of constructors for class: " + classDescriptor + " " + constructors;
1445                    ConstructorDescriptor constructorDescriptor = CollectionsKt.single(constructors);
1446    
1447                    JvmMethodSignature constructor = typeMapper.mapSignature(SamCodegenUtil.resolveSamAdapter(constructorDescriptor));
1448                    v.invokespecial(type.getInternalName(), "<init>", constructor.getAsmMethod().getDescriptor(), false);
1449                    return Unit.INSTANCE;
1450                }
1451            });
1452        }
1453    
1454        public void pushClosureOnStack(@NotNull ClassDescriptor classDescriptor, boolean putThis, @NotNull CallGenerator callGenerator) {
1455            CalculatedClosure closure = bindingContext.get(CLOSURE, classDescriptor);
1456            if (closure == null) return;
1457    
1458            int paramIndex = 0;
1459    
1460            if (putThis) {
1461                ClassDescriptor captureThis = closure.getCaptureThis();
1462                if (captureThis != null) {
1463                    StackValue thisOrOuter = generateThisOrOuter(captureThis, false);
1464                    assert !isPrimitive(thisOrOuter.type) : "This or outer should be non primitive: " + thisOrOuter.type;
1465                    callGenerator.putCapturedValueOnStack(thisOrOuter, thisOrOuter.type, paramIndex++);
1466                }
1467            }
1468    
1469            KotlinType captureReceiver = closure.getCaptureReceiverType();
1470            if (captureReceiver != null) {
1471                Type asmType = typeMapper.mapType(captureReceiver);
1472                StackValue.Local capturedReceiver = StackValue.local(AsmUtil.getReceiverIndex(context, context.getContextDescriptor()), asmType);
1473                callGenerator.putCapturedValueOnStack(capturedReceiver, capturedReceiver.type, paramIndex++);
1474            }
1475    
1476            for (Map.Entry<DeclarationDescriptor, EnclosedValueDescriptor> entry : closure.getCaptureVariables().entrySet()) {
1477                Type sharedVarType = typeMapper.getSharedVarType(entry.getKey());
1478                if (sharedVarType == null) {
1479                    sharedVarType = typeMapper.mapType((VariableDescriptor) entry.getKey());
1480                }
1481                StackValue capturedVar = lookupOuterValue(entry.getValue());
1482                callGenerator.putCapturedValueOnStack(capturedVar, sharedVarType, paramIndex++);
1483            }
1484    
1485    
1486            ClassDescriptor superClass = DescriptorUtilsKt.getSuperClassNotAny(classDescriptor);
1487            if (superClass != null) {
1488                pushClosureOnStack(
1489                        superClass,
1490                        putThis && closure.getCaptureThis() == null,
1491                        callGenerator
1492                );
1493            }
1494        }
1495    
1496        /* package */ StackValue generateBlock(@NotNull KtBlockExpression expression, boolean isStatement) {
1497            if (expression.getParent() instanceof KtNamedFunction) {
1498                // For functions end of block should be end of function label
1499                return generateBlock(expression.getStatements(), isStatement, null, context.getMethodEndLabel());
1500            }
1501            return generateBlock(expression.getStatements(), isStatement, null, null);
1502        }
1503    
1504        @NotNull
1505        public StackValue lookupOuterValue(EnclosedValueDescriptor d) {
1506            DeclarationDescriptor descriptor = d.getDescriptor();
1507            for (LocalLookup.LocalLookupCase aCase : LocalLookup.LocalLookupCase.values()) {
1508                if (aCase.isCase(descriptor)) {
1509                    return aCase.outerValue(d, this);
1510                }
1511            }
1512            throw new IllegalStateException("Can't get outer value in " + this + " for " + d);
1513        }
1514    
1515        private StackValue generateBlock(
1516                List<KtExpression> statements,
1517                boolean isStatement,
1518                Label labelBeforeLastExpression,
1519                @Nullable final Label labelBlockEnd
1520        ) {
1521            final Label blockEnd = labelBlockEnd != null ? labelBlockEnd : new Label();
1522    
1523            final List<Function<StackValue, Void>> leaveTasks = Lists.newArrayList();
1524    
1525            StackValue answer = StackValue.none();
1526    
1527            for (Iterator<KtExpression> iterator = statements.iterator(); iterator.hasNext(); ) {
1528                KtExpression possiblyLabeledStatement = iterator.next();
1529    
1530                KtElement statement = KtPsiUtil.safeDeparenthesize(possiblyLabeledStatement);
1531    
1532                if (statement instanceof KtNamedDeclaration) {
1533                    KtNamedDeclaration declaration = (KtNamedDeclaration) statement;
1534                    if (KtPsiUtil.isScriptDeclaration(declaration)) {
1535                        continue;
1536                    }
1537                }
1538    
1539                putDescriptorIntoFrameMap(statement);
1540    
1541                boolean isExpression = !iterator.hasNext() && !isStatement;
1542                if (isExpression && labelBeforeLastExpression != null) {
1543                    v.mark(labelBeforeLastExpression);
1544                }
1545    
1546                StackValue result = isExpression ? gen(possiblyLabeledStatement) : genStatement(possiblyLabeledStatement);
1547    
1548                if (!iterator.hasNext()) {
1549                    answer = result;
1550                }
1551                else {
1552                    result.put(Type.VOID_TYPE, v);
1553                }
1554    
1555                addLeaveTaskToRemoveDescriptorFromFrameMap(statement, blockEnd, leaveTasks);
1556            }
1557    
1558            return new StackValueWithLeaveTask(answer, new Function1<StackValue, Unit>() {
1559                @Override
1560                public Unit invoke(StackValue value) {
1561                    if (labelBlockEnd == null) {
1562                        v.mark(blockEnd);
1563                    }
1564                    for (Function<StackValue, Void> task : Lists.reverse(leaveTasks)) {
1565                        task.fun(value);
1566                    }
1567                    return Unit.INSTANCE;
1568                }
1569            });
1570        }
1571    
1572        @NotNull
1573        private Type getVariableType(@NotNull VariableDescriptor variableDescriptor) {
1574            Type sharedVarType = typeMapper.getSharedVarType(variableDescriptor);
1575            return sharedVarType != null ? sharedVarType : asmType(variableDescriptor.getType());
1576        }
1577    
1578        private static boolean isSharedVarType(@NotNull Type type) {
1579            return type.getSort() == Type.OBJECT && type.getInternalName().startsWith(REF_TYPE_PREFIX);
1580        }
1581    
1582    
1583        private void putDescriptorIntoFrameMap(@NotNull KtElement statement) {
1584            if (statement instanceof KtDestructuringDeclaration) {
1585                KtDestructuringDeclaration multiDeclaration = (KtDestructuringDeclaration) statement;
1586                for (KtDestructuringDeclarationEntry entry : multiDeclaration.getEntries()) {
1587                    putLocalVariableIntoFrameMap(entry);
1588                }
1589            }
1590    
1591            if (statement instanceof KtVariableDeclaration) {
1592                putLocalVariableIntoFrameMap((KtVariableDeclaration) statement);
1593            }
1594    
1595            if (statement instanceof KtNamedFunction) {
1596                DeclarationDescriptor descriptor = bindingContext.get(DECLARATION_TO_DESCRIPTOR, statement);
1597                assert descriptor instanceof FunctionDescriptor : "Couldn't find function declaration in binding context " + statement.getText();
1598                Type type = asmTypeForAnonymousClass(bindingContext, (FunctionDescriptor) descriptor);
1599                myFrameMap.enter(descriptor, type);
1600            }
1601        }
1602    
1603        private void putLocalVariableIntoFrameMap(@NotNull KtVariableDeclaration statement) {
1604            VariableDescriptor variableDescriptor = bindingContext.get(VARIABLE, statement);
1605            assert variableDescriptor != null : "Couldn't find variable declaration in binding context " + statement.getText();
1606    
1607            Type type = getVariableType(variableDescriptor);
1608            int index = myFrameMap.enter(variableDescriptor, type);
1609    
1610            if (isSharedVarType(type)) {
1611                markLineNumber(statement, false);
1612                v.anew(type);
1613                v.dup();
1614                v.invokespecial(type.getInternalName(), "<init>", "()V", false);
1615                v.store(index, OBJECT_TYPE);
1616            }
1617        }
1618    
1619        private void addLeaveTaskToRemoveDescriptorFromFrameMap(
1620                @NotNull KtElement statement,
1621                @NotNull Label blockEnd,
1622                @NotNull List<Function<StackValue, Void>> leaveTasks
1623        ) {
1624            if (statement instanceof KtDestructuringDeclaration) {
1625                KtDestructuringDeclaration multiDeclaration = (KtDestructuringDeclaration) statement;
1626                for (KtDestructuringDeclarationEntry entry : multiDeclaration.getEntries()) {
1627                    addLeaveTaskToRemoveLocalVariableFromFrameMap(entry, blockEnd, leaveTasks);
1628                }
1629            }
1630    
1631            if (statement instanceof KtVariableDeclaration) {
1632                addLeaveTaskToRemoveLocalVariableFromFrameMap((KtVariableDeclaration) statement, blockEnd, leaveTasks);
1633            }
1634    
1635            if (statement instanceof KtNamedFunction) {
1636                addLeaveTaskToRemoveNamedFunctionFromFrameMap((KtNamedFunction) statement, blockEnd, leaveTasks);
1637            }
1638        }
1639    
1640        private void addLeaveTaskToRemoveLocalVariableFromFrameMap(
1641                @NotNull KtVariableDeclaration statement,
1642                final Label blockEnd,
1643                @NotNull List<Function<StackValue, Void>> leaveTasks
1644        ) {
1645            final VariableDescriptor variableDescriptor = bindingContext.get(VARIABLE, statement);
1646            assert variableDescriptor != null;
1647    
1648            final Type type = getVariableType(variableDescriptor);
1649    
1650            final Label scopeStart = new Label();
1651            v.mark(scopeStart);
1652    
1653            leaveTasks.add(new Function<StackValue, Void>() {
1654                @Override
1655                public Void fun(StackValue answer) {
1656                    int index = myFrameMap.leave(variableDescriptor);
1657    
1658                    if (isSharedVarType(type)) {
1659                        v.aconst(null);
1660                        v.store(index, OBJECT_TYPE);
1661                    }
1662                    v.visitLocalVariable(variableDescriptor.getName().asString(), type.getDescriptor(), null, scopeStart, blockEnd, index);
1663                    return null;
1664                }
1665            });
1666        }
1667    
1668        private void addLeaveTaskToRemoveNamedFunctionFromFrameMap(
1669                @NotNull final KtNamedFunction statement,
1670                final Label blockEnd,
1671                @NotNull List<Function<StackValue, Void>> leaveTasks
1672        ) {
1673            final FunctionDescriptor functionDescriptor = (FunctionDescriptor) bindingContext.get(DECLARATION_TO_DESCRIPTOR, statement);
1674            assert functionDescriptor != null;
1675    
1676            final Type type = asmTypeForAnonymousClass(bindingContext, functionDescriptor);
1677    
1678            final Label scopeStart = new Label();
1679            v.mark(scopeStart);
1680    
1681            leaveTasks.add(new Function<StackValue, Void>() {
1682                @Override
1683                public Void fun(StackValue answer) {
1684                    int index = myFrameMap.leave(functionDescriptor);
1685    
1686                    assert !functionDescriptor.getName().isSpecial() : "Local variable should be generated only for function with name: " + statement.getText();
1687                    v.visitLocalVariable(functionDescriptor.getName().asString() + "$", type.getDescriptor(), null, scopeStart, blockEnd, index);
1688                    return null;
1689                }
1690            });
1691        }
1692    
1693        public boolean isShouldMarkLineNumbers() {
1694            return shouldMarkLineNumbers;
1695        }
1696    
1697        public void setShouldMarkLineNumbers(boolean shouldMarkLineNumbers) {
1698            this.shouldMarkLineNumbers = shouldMarkLineNumbers;
1699        }
1700    
1701        public void markStartLineNumber(@NotNull KtElement element) {
1702            markLineNumber(element, false);
1703        }
1704    
1705        public void markLineNumber(@NotNull KtElement statement, boolean markEndOffset) {
1706            if (!shouldMarkLineNumbers) return;
1707    
1708            Integer lineNumber = CodegenUtil.getLineNumberForElement(statement, markEndOffset);
1709            if (lineNumber == null || lineNumber == myLastLineNumber) {
1710                return;
1711            }
1712            myLastLineNumber = lineNumber;
1713    
1714            Label label = new Label();
1715            v.visitLabel(label);
1716            v.visitLineNumber(lineNumber, label);
1717        }
1718    
1719        //we should generate additional linenumber info after inline call only if it used as argument
1720        public void markLineNumberAfterInlineIfNeeded() {
1721            if (!shouldMarkLineNumbers) {
1722                //if it used as general argument
1723                if (myLastLineNumber > -1) {
1724                    Label label = new Label();
1725                    v.visitLabel(label);
1726                    v.visitLineNumber(myLastLineNumber, label);
1727                }
1728            } else {
1729                //if it used as argument of infix call (in this case lineNumber for simple inlineCall also would be reset)
1730                myLastLineNumber = -1;
1731            }
1732        }
1733    
1734        private void doFinallyOnReturn(@NotNull Label afterReturnLabel) {
1735            if(!blockStackElements.isEmpty()) {
1736                BlockStackElement stackElement = blockStackElements.peek();
1737                if (stackElement instanceof FinallyBlockStackElement) {
1738                    FinallyBlockStackElement finallyBlockStackElement = (FinallyBlockStackElement) stackElement;
1739                    genFinallyBlockOrGoto(finallyBlockStackElement, null, afterReturnLabel);
1740                }
1741                else if (stackElement instanceof LoopBlockStackElement) {
1742    
1743                } else {
1744                    throw new UnsupportedOperationException("Wrong BlockStackElement in processing stack");
1745                }
1746    
1747                blockStackElements.pop();
1748                doFinallyOnReturn(afterReturnLabel);
1749                blockStackElements.push(stackElement);
1750            }
1751        }
1752    
1753        public boolean hasFinallyBlocks() {
1754            for (BlockStackElement element : blockStackElements) {
1755                if (element instanceof FinallyBlockStackElement) {
1756                    return true;
1757                }
1758            }
1759            return false;
1760        }
1761    
1762        private void genFinallyBlockOrGoto(
1763                @Nullable FinallyBlockStackElement finallyBlockStackElement,
1764                @Nullable Label tryCatchBlockEnd,
1765                @Nullable Label afterJumpLabel
1766        ) {
1767            if (finallyBlockStackElement != null) {
1768                finallyDepth++;
1769                assert finallyBlockStackElement.gaps.size() % 2 == 0 : "Finally block gaps are inconsistent";
1770    
1771                BlockStackElement topOfStack = blockStackElements.pop();
1772                assert topOfStack == finallyBlockStackElement : "Top element of stack doesn't equals processing finally block";
1773    
1774                KtTryExpression jetTryExpression = finallyBlockStackElement.expression;
1775                Label finallyStart = new Label();
1776                v.mark(finallyStart);
1777                finallyBlockStackElement.addGapLabel(finallyStart);
1778                if (InlineCodegenUtil.isFinallyMarkerRequired(context)) {
1779                    InlineCodegenUtil.generateFinallyMarker(v, finallyDepth, true);
1780                }
1781                //noinspection ConstantConditions
1782                gen(jetTryExpression.getFinallyBlock().getFinalExpression(), Type.VOID_TYPE);
1783    
1784                if (InlineCodegenUtil.isFinallyMarkerRequired(context)) {
1785                    InlineCodegenUtil.generateFinallyMarker(v, finallyDepth, false);
1786                }
1787            }
1788    
1789            if (tryCatchBlockEnd != null) {
1790                v.goTo(tryCatchBlockEnd);
1791            }
1792    
1793            if (finallyBlockStackElement != null) {
1794                finallyDepth--;
1795                Label finallyEnd = afterJumpLabel != null ? afterJumpLabel : new Label();
1796                if (afterJumpLabel == null) {
1797                    v.mark(finallyEnd);
1798                }
1799                finallyBlockStackElement.addGapLabel(finallyEnd);
1800    
1801                blockStackElements.push(finallyBlockStackElement);
1802            }
1803        }
1804    
1805        @Override
1806        public StackValue visitReturnExpression(@NotNull final KtReturnExpression expression, StackValue receiver) {
1807            return StackValue.operation(Type.VOID_TYPE, new Function1<InstructionAdapter, Unit>() {
1808                @Override
1809                public Unit invoke(InstructionAdapter adapter) {
1810                    KtExpression returnedExpression = expression.getReturnedExpression();
1811                    CallableMemberDescriptor descriptor = getContext().getContextDescriptor();
1812                    NonLocalReturnInfo nonLocalReturn = getNonLocalReturnInfo(descriptor, expression);
1813                    boolean isNonLocalReturn = nonLocalReturn != null;
1814                    if (isNonLocalReturn && !state.isInlineEnabled()) {
1815                        state.getDiagnostics().report(Errors.NON_LOCAL_RETURN_IN_DISABLED_INLINE.on(expression));
1816                        genThrow(v, "java/lang/UnsupportedOperationException",
1817                                 "Non-local returns are not allowed with inlining disabled");
1818                        return Unit.INSTANCE;
1819                    }
1820    
1821                    Type returnType = isNonLocalReturn ? nonLocalReturn.returnType : ExpressionCodegen.this.returnType;
1822                    if (returnedExpression != null) {
1823                        gen(returnedExpression, returnType);
1824                    }
1825    
1826                    Label afterReturnLabel = new Label();
1827                    generateFinallyBlocksIfNeeded(returnType, afterReturnLabel);
1828    
1829                    if (isNonLocalReturn) {
1830                        InlineCodegenUtil.generateGlobalReturnFlag(v, nonLocalReturn.labelName);
1831                    }
1832                    v.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
1833                    v.mark(afterReturnLabel);
1834                    return Unit.INSTANCE;
1835                }
1836            });
1837        }
1838    
1839        public void generateFinallyBlocksIfNeeded(Type returnType, @NotNull Label afterReturnLabel) {
1840            if (hasFinallyBlocks()) {
1841                if (!Type.VOID_TYPE.equals(returnType)) {
1842                    int returnValIndex = myFrameMap.enterTemp(returnType);
1843                    StackValue.Local localForReturnValue = StackValue.local(returnValIndex, returnType);
1844                    localForReturnValue.store(StackValue.onStack(returnType), v);
1845                    doFinallyOnReturn(afterReturnLabel);
1846                    localForReturnValue.put(returnType, v);
1847                    myFrameMap.leaveTemp(returnType);
1848                }
1849                else {
1850                    doFinallyOnReturn(afterReturnLabel);
1851                }
1852            }
1853        }
1854    
1855        @Nullable
1856        private NonLocalReturnInfo getNonLocalReturnInfo(@NotNull CallableMemberDescriptor descriptor, @NotNull KtReturnExpression expression) {
1857            //call inside lambda
1858            if (isFunctionLiteral(descriptor) || isFunctionExpression(descriptor)) {
1859                if (expression.getLabelName() == null) {
1860                    if (isFunctionLiteral(descriptor)) {
1861                        //non labeled return couldn't be local in lambda
1862                        FunctionDescriptor containingFunction =
1863                                BindingContextUtils.getContainingFunctionSkipFunctionLiterals(descriptor, true).getFirst();
1864                        //FIRST_FUN_LABEL to prevent clashing with existing labels
1865                        return new NonLocalReturnInfo(typeMapper.mapReturnType(containingFunction), InlineCodegenUtil.FIRST_FUN_LABEL);
1866                    } else {
1867                        //local
1868                        return null;
1869                    }
1870                }
1871    
1872                PsiElement element = bindingContext.get(LABEL_TARGET, expression.getTargetLabel());
1873                if (element != DescriptorToSourceUtils.getSourceFromDescriptor(context.getContextDescriptor())) {
1874                    DeclarationDescriptor elementDescriptor = typeMapper.getBindingContext().get(DECLARATION_TO_DESCRIPTOR, element);
1875                    assert element != null : "Expression should be not null " + expression.getText();
1876                    assert elementDescriptor != null : "Descriptor should be not null: " + element.getText();
1877                    return new NonLocalReturnInfo(typeMapper.mapReturnType((CallableDescriptor) elementDescriptor), expression.getLabelName());
1878                }
1879            }
1880            return null;
1881        }
1882    
1883        public void returnExpression(KtExpression expr) {
1884            boolean isBlockedNamedFunction = expr instanceof KtBlockExpression && expr.getParent() instanceof KtNamedFunction;
1885    
1886            // If generating body for named block-bodied function, generate it as sequence of statements
1887            gen(expr, isBlockedNamedFunction ? Type.VOID_TYPE : returnType);
1888    
1889            // If it does not end with return we should return something
1890            // because if we don't there can be VerifyError (specific cases with Nothing-typed expressions)
1891            if (!endsWithReturn(expr)) {
1892                markLineNumber(expr, true);
1893    
1894                if (isLambdaVoidBody(expr, returnType)) {
1895                    markLineNumber((KtFunctionLiteral) expr.getParent(), true);
1896                }
1897    
1898                if (isBlockedNamedFunction && !Type.VOID_TYPE.equals(expressionType(expr))) {
1899                    StackValue.none().put(returnType, v);
1900                }
1901    
1902                v.areturn(returnType);
1903            }
1904        }
1905    
1906        private static boolean endsWithReturn(KtElement bodyExpression) {
1907            if (bodyExpression instanceof KtBlockExpression) {
1908                List<KtExpression> statements = ((KtBlockExpression) bodyExpression).getStatements();
1909                return statements.size() > 0 && statements.get(statements.size() - 1) instanceof KtReturnExpression;
1910            }
1911    
1912            return bodyExpression instanceof KtReturnExpression;
1913        }
1914    
1915        private static boolean isLambdaVoidBody(KtElement bodyExpression, Type returnType) {
1916            if (bodyExpression instanceof KtBlockExpression) {
1917                PsiElement parent = bodyExpression.getParent();
1918                if (parent instanceof KtFunctionLiteral) {
1919                    return Type.VOID_TYPE.equals(returnType);
1920                }
1921            }
1922    
1923            return false;
1924        }
1925    
1926        @Override
1927        public StackValue visitSimpleNameExpression(@NotNull KtSimpleNameExpression expression, @NotNull StackValue receiver) {
1928            ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCall(expression, bindingContext);
1929    
1930            DeclarationDescriptor descriptor;
1931            if (resolvedCall == null) {
1932                descriptor = bindingContext.get(REFERENCE_TARGET, expression);
1933            }
1934            else {
1935                if (resolvedCall instanceof VariableAsFunctionResolvedCall) {
1936                    VariableAsFunctionResolvedCall call = (VariableAsFunctionResolvedCall) resolvedCall;
1937                    resolvedCall = call.getVariableCall();
1938                }
1939                receiver = StackValue.receiver(resolvedCall, receiver, this, null);
1940                descriptor = resolvedCall.getResultingDescriptor();
1941                if (descriptor instanceof FakeCallableDescriptorForObject) {
1942                    descriptor = ((FakeCallableDescriptorForObject) descriptor).getReferencedDescriptor();
1943                }
1944            }
1945    
1946            assert descriptor != null : "Couldn't find descriptor for '" + expression.getText() + "'";
1947            descriptor = descriptor.getOriginal();
1948    
1949            boolean isSyntheticField = descriptor instanceof SyntheticFieldDescriptor;
1950            if (isSyntheticField) {
1951                descriptor = ((SyntheticFieldDescriptor) descriptor).getPropertyDescriptor();
1952            }
1953            if (descriptor instanceof CallableMemberDescriptor) {
1954                CallableMemberDescriptor memberDescriptor = DescriptorUtils.unwrapFakeOverride((CallableMemberDescriptor) descriptor);
1955    
1956                IntrinsicMethod intrinsic = state.getIntrinsics().getIntrinsic(memberDescriptor);
1957                if (intrinsic instanceof IntrinsicPropertyGetter) {
1958                    //TODO: intrinsic properties (see intermediateValueForProperty)
1959                    Type returnType = typeMapper.mapType(memberDescriptor);
1960                    StackValue intrinsicResult = ((IntrinsicPropertyGetter) intrinsic).generate(resolvedCall, this, returnType, receiver);
1961                    if (intrinsicResult != null) return intrinsicResult;
1962                }
1963            }
1964    
1965            if (descriptor instanceof PropertyDescriptor) {
1966                PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
1967    
1968                Collection<ExpressionCodegenExtension> codegenExtensions = ExpressionCodegenExtension.Companion.getInstances(state.getProject());
1969                if (!codegenExtensions.isEmpty() && resolvedCall != null) {
1970                    ExpressionCodegenExtension.Context context = new ExpressionCodegenExtension.Context(typeMapper, v);
1971                    KotlinType returnType = propertyDescriptor.getReturnType();
1972                    for (ExpressionCodegenExtension extension : codegenExtensions) {
1973                        if (returnType != null) {
1974                            StackValue value = extension.applyProperty(receiver, resolvedCall, context);
1975                            if (value != null) return value;
1976                        }
1977                    }
1978                }
1979    
1980                boolean directToField = isSyntheticField && contextKind() != OwnerKind.DEFAULT_IMPLS;
1981                ClassDescriptor superCallTarget = resolvedCall == null ? null : getSuperCallTarget(resolvedCall.getCall());
1982    
1983                if (directToField) {
1984                    receiver = StackValue.receiverWithoutReceiverArgument(receiver);
1985                }
1986    
1987                return intermediateValueForProperty(propertyDescriptor, directToField, directToField, superCallTarget, false, receiver);
1988            }
1989    
1990            if (descriptor instanceof ClassDescriptor) {
1991                ClassDescriptor classDescriptor = (ClassDescriptor) descriptor;
1992                if (isObject(classDescriptor)) {
1993                    return StackValue.singleton(classDescriptor, typeMapper);
1994                }
1995                if (isEnumEntry(classDescriptor)) {
1996                    return StackValue.enumEntry(classDescriptor, typeMapper);
1997                }
1998                ClassDescriptor companionObjectDescriptor = classDescriptor.getCompanionObjectDescriptor();
1999                if (companionObjectDescriptor != null) {
2000                    return StackValue.singleton(companionObjectDescriptor, typeMapper);
2001                }
2002                return StackValue.none();
2003            }
2004    
2005            StackValue localOrCaptured = findLocalOrCapturedValue(descriptor);
2006            if (localOrCaptured != null) {
2007                return localOrCaptured;
2008            }
2009            throw new UnsupportedOperationException("don't know how to generate reference " + descriptor);
2010        }
2011    
2012        @Nullable
2013        private ClassDescriptor getSuperCallTarget(@NotNull Call call) {
2014            KtSuperExpression superExpression = CallResolverUtilKt.getSuperCallExpression(call);
2015            return superExpression == null ? null : getSuperCallLabelTarget(context, superExpression);
2016        }
2017    
2018        @Nullable
2019        public StackValue findLocalOrCapturedValue(@NotNull DeclarationDescriptor descriptor) {
2020            int index = lookupLocalIndex(descriptor);
2021            if (index >= 0) {
2022                return stackValueForLocal(descriptor, index);
2023            }
2024    
2025            return findCapturedValue(descriptor);
2026        }
2027    
2028        @Nullable
2029        public StackValue findCapturedValue(@NotNull DeclarationDescriptor descriptor) {
2030            if (context instanceof ConstructorContext) {
2031                return lookupCapturedValueInConstructorParameters(descriptor);
2032            }
2033    
2034            return context.lookupInContext(descriptor, StackValue.LOCAL_0, state, false);
2035        }
2036    
2037        @Nullable
2038        private StackValue lookupCapturedValueInConstructorParameters(@NotNull DeclarationDescriptor descriptor) {
2039            StackValue parentResult = context.lookupInContext(descriptor, StackValue.LOCAL_0, state, false);
2040            if (context.closure == null || parentResult == null) return parentResult;
2041    
2042            int parameterOffsetInConstructor = context.closure.getCapturedParameterOffsetInConstructor(descriptor);
2043            // when captured parameter is singleton
2044            // see compiler/testData/codegen/box/objects/objectInLocalAnonymousObject.kt (fun local() captured in A)
2045            if (parameterOffsetInConstructor == -1) return parentResult;
2046    
2047            assert parentResult instanceof StackValue.Field || parentResult instanceof StackValue.FieldForSharedVar
2048                    : "Part of closure should be either Field or FieldForSharedVar";
2049    
2050            if (parentResult instanceof StackValue.FieldForSharedVar) {
2051                return StackValue.shared(parameterOffsetInConstructor, parentResult.type);
2052            }
2053    
2054            return StackValue.local(parameterOffsetInConstructor, parentResult.type);
2055        }
2056    
2057        private StackValue stackValueForLocal(DeclarationDescriptor descriptor, int index) {
2058            if (descriptor instanceof VariableDescriptor) {
2059                Type sharedVarType = typeMapper.getSharedVarType(descriptor);
2060                KotlinType outType = ((VariableDescriptor) descriptor).getType();
2061                if (sharedVarType != null) {
2062                    return StackValue.shared(index, asmType(outType));
2063                }
2064                else {
2065                    return StackValue.local(index, asmType(outType));
2066                }
2067            }
2068            else {
2069                return StackValue.local(index, OBJECT_TYPE);
2070            }
2071        }
2072    
2073        @Override
2074        public boolean lookupLocal(DeclarationDescriptor descriptor) {
2075            return lookupLocalIndex(descriptor) != -1;
2076        }
2077    
2078        public int lookupLocalIndex(DeclarationDescriptor descriptor) {
2079            return myFrameMap.getIndex(descriptor);
2080        }
2081    
2082        @Nullable
2083        private static KotlinType getPropertyDelegateType(@NotNull PropertyDescriptor descriptor, @NotNull BindingContext bindingContext) {
2084            PropertyGetterDescriptor getter = descriptor.getGetter();
2085            if (getter != null) {
2086                Call call = bindingContext.get(DELEGATED_PROPERTY_CALL, getter);
2087                if (call != null) {
2088                    assert call.getExplicitReceiver() != null : "No explicit receiver for call:" + call;
2089                    return ((ReceiverValue) call.getExplicitReceiver()).getType();
2090                }
2091            }
2092            return null;
2093        }
2094    
2095        @NotNull
2096        public StackValue.Property intermediateValueForProperty(
2097                @NotNull PropertyDescriptor propertyDescriptor,
2098                boolean forceField,
2099                @Nullable ClassDescriptor superCallTarget,
2100                @NotNull StackValue receiver
2101        ) {
2102            return intermediateValueForProperty(propertyDescriptor, forceField, false, superCallTarget, false, receiver);
2103        }
2104    
2105        private CodegenContext getBackingFieldContext(
2106                @NotNull FieldAccessorKind accessorKind,
2107                @NotNull DeclarationDescriptor containingDeclaration
2108        ) {
2109            switch (accessorKind) {
2110                case NORMAL: return context.getParentContext();
2111                // For companion object property, backing field lives in object containing class
2112                // Otherwise, it lives in its containing declaration
2113                case IN_CLASS_COMPANION: return context.findParentContextWithDescriptor(containingDeclaration.getContainingDeclaration());
2114                case FIELD_FROM_LOCAL: return context.findParentContextWithDescriptor(containingDeclaration);
2115                default: throw new IllegalStateException();
2116            }
2117        }
2118    
2119        public StackValue.Property intermediateValueForProperty(
2120                @NotNull PropertyDescriptor propertyDescriptor,
2121                boolean forceField,
2122                boolean syntheticBackingField,
2123                @Nullable ClassDescriptor superCallTarget,
2124                boolean skipAccessorsForPrivateFieldInOuterClass,
2125                StackValue receiver
2126        ) {
2127            if (propertyDescriptor instanceof SyntheticJavaPropertyDescriptor) {
2128                return intermediateValueForSyntheticExtensionProperty((SyntheticJavaPropertyDescriptor) propertyDescriptor, receiver);
2129            }
2130    
2131            DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration();
2132    
2133            FieldAccessorKind fieldAccessorKind = FieldAccessorKind.NORMAL;
2134            boolean isBackingFieldInClassCompanion = JvmAbi.isPropertyWithBackingFieldInOuterClass(propertyDescriptor);
2135            if (isBackingFieldInClassCompanion && (forceField || propertyDescriptor.isConst() && Visibilities.isPrivate(propertyDescriptor.getVisibility()))) {
2136                fieldAccessorKind = FieldAccessorKind.IN_CLASS_COMPANION;
2137            }
2138            else if (syntheticBackingField && context.getFirstCrossInlineOrNonInlineContext().getParentContext().getContextDescriptor() != containingDeclaration) {
2139                fieldAccessorKind = FieldAccessorKind.FIELD_FROM_LOCAL;
2140            }
2141            boolean isStaticBackingField = DescriptorUtils.isStaticDeclaration(propertyDescriptor) ||
2142                                           AsmUtil.isInstancePropertyWithStaticBackingField(propertyDescriptor);
2143            boolean isSuper = superCallTarget != null;
2144            boolean isExtensionProperty = propertyDescriptor.getExtensionReceiverParameter() != null;
2145    
2146            KotlinType delegateType = getPropertyDelegateType(propertyDescriptor, bindingContext);
2147            boolean isDelegatedProperty = delegateType != null;
2148    
2149            CallableMethod callableGetter = null;
2150            CallableMethod callableSetter = null;
2151    
2152            CodegenContext backingFieldContext = getBackingFieldContext(fieldAccessorKind, containingDeclaration);
2153            DeclarationDescriptor ownerDescriptor = containingDeclaration;
2154            boolean skipPropertyAccessors;
2155    
2156            PropertyDescriptor originalPropertyDescriptor = DescriptorUtils.unwrapFakeOverride(propertyDescriptor);
2157            if (fieldAccessorKind != FieldAccessorKind.NORMAL) {
2158                int flags = AsmUtil.getVisibilityForBackingField(propertyDescriptor, isDelegatedProperty);
2159                skipPropertyAccessors = (flags & ACC_PRIVATE) == 0 || skipAccessorsForPrivateFieldInOuterClass;
2160                if (!skipPropertyAccessors) {
2161                    //noinspection ConstantConditions
2162                    propertyDescriptor = (PropertyDescriptor) backingFieldContext.getAccessor(
2163                            propertyDescriptor, fieldAccessorKind, delegateType, superCallTarget
2164                    );
2165                    assert propertyDescriptor instanceof AccessorForPropertyBackingField :
2166                            "Unexpected accessor descriptor: " + propertyDescriptor;
2167                    ownerDescriptor = propertyDescriptor;
2168                }
2169            }
2170            else {
2171                if (!isBackingFieldInClassCompanion) {
2172                    ownerDescriptor = propertyDescriptor;
2173                }
2174                skipPropertyAccessors = forceField;
2175            }
2176    
2177            if (!skipPropertyAccessors) {
2178                if (!couldUseDirectAccessToProperty(propertyDescriptor, true, isDelegatedProperty, context)) {
2179                    propertyDescriptor = context.getAccessorForSuperCallIfNeeded(propertyDescriptor, superCallTarget);
2180    
2181                    propertyDescriptor = context.accessibleDescriptor(propertyDescriptor, superCallTarget);
2182    
2183                    PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
2184                    if (getter != null && !isConstOrHasJvmFieldAnnotation(propertyDescriptor)) {
2185                        callableGetter = typeMapper.mapToCallableMethod(getter, isSuper);
2186                    }
2187                }
2188    
2189                if (propertyDescriptor.isVar()) {
2190                    PropertySetterDescriptor setter = propertyDescriptor.getSetter();
2191                    if (setter != null &&
2192                        !couldUseDirectAccessToProperty(propertyDescriptor, false, isDelegatedProperty, context) &&
2193                        !isConstOrHasJvmFieldAnnotation(propertyDescriptor)) {
2194                        callableSetter = typeMapper.mapToCallableMethod(setter, isSuper);
2195                    }
2196                }
2197            }
2198    
2199            if (!isStaticBackingField) {
2200                propertyDescriptor = DescriptorUtils.unwrapFakeOverride(propertyDescriptor);
2201            }
2202    
2203            Type backingFieldOwner = typeMapper.mapOwner(ownerDescriptor);
2204    
2205            String fieldName;
2206            if (isExtensionProperty && !isDelegatedProperty) {
2207                fieldName = null;
2208            }
2209            else if (originalPropertyDescriptor.getContainingDeclaration() == backingFieldContext.getContextDescriptor()) {
2210                assert backingFieldContext instanceof FieldOwnerContext
2211                        : "Actual context is " + backingFieldContext + " but should be instance of FieldOwnerContext";
2212                fieldName = ((FieldOwnerContext) backingFieldContext).getFieldName(propertyDescriptor, isDelegatedProperty);
2213            }
2214            else {
2215                fieldName = JetTypeMapper.mapDefaultFieldName(propertyDescriptor, isDelegatedProperty);
2216            }
2217    
2218            return StackValue.property(propertyDescriptor, backingFieldOwner,
2219                                       typeMapper.mapType(
2220                                               isDelegatedProperty && forceField ? delegateType : propertyDescriptor.getOriginal().getType()),
2221                                       isStaticBackingField, fieldName, callableGetter, callableSetter, state, receiver);
2222        }
2223    
2224        @NotNull
2225        private StackValue.Property intermediateValueForSyntheticExtensionProperty(
2226                @NotNull SyntheticJavaPropertyDescriptor propertyDescriptor,
2227                StackValue receiver
2228        ) {
2229            Type type = typeMapper.mapType(propertyDescriptor.getOriginal().getType());
2230            CallableMethod callableGetter =
2231                    typeMapper.mapToCallableMethod(context.accessibleDescriptor(propertyDescriptor.getGetMethod(), null), false);
2232            FunctionDescriptor setMethod = propertyDescriptor.getSetMethod();
2233            CallableMethod callableSetter =
2234                    setMethod != null ? typeMapper.mapToCallableMethod(context.accessibleDescriptor(setMethod, null), false) : null;
2235            return StackValue.property(propertyDescriptor, null, type, false, null, callableGetter, callableSetter, state, receiver);
2236        }
2237    
2238        @Override
2239        public StackValue visitCallExpression(@NotNull KtCallExpression expression, StackValue receiver) {
2240            ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCallWithAssert(expression, bindingContext);
2241            FunctionDescriptor descriptor = accessibleFunctionDescriptor(resolvedCall);
2242    
2243            if (descriptor instanceof ConstructorDescriptor) {
2244                return generateNewCall(expression, resolvedCall);
2245            }
2246    
2247            if (descriptor.getOriginal() instanceof SamConstructorDescriptor) {
2248                KtExpression argumentExpression = bindingContext.get(SAM_CONSTRUCTOR_TO_ARGUMENT, expression);
2249                assert argumentExpression != null : "Argument expression is not saved for a SAM constructor: " + descriptor;
2250                return genSamInterfaceValue(argumentExpression, this);
2251            }
2252    
2253            return invokeFunction(resolvedCall, receiver);
2254        }
2255    
2256        @Nullable
2257        private StackValue genSamInterfaceValue(
2258                @NotNull KtExpression probablyParenthesizedExpression,
2259                @NotNull final KtVisitor<StackValue, StackValue> visitor
2260        ) {
2261            final KtExpression expression = KtPsiUtil.deparenthesize(probablyParenthesizedExpression);
2262            final SamType samType = bindingContext.get(SAM_VALUE, probablyParenthesizedExpression);
2263            if (samType == null || expression == null) return null;
2264    
2265            if (expression instanceof KtLambdaExpression) {
2266                return genClosure(((KtLambdaExpression) expression).getFunctionLiteral(), samType);
2267            }
2268    
2269            if (expression instanceof KtNamedFunction) {
2270                return genClosure((KtNamedFunction) expression, samType);
2271            }
2272    
2273            final Type asmType =
2274                    state.getSamWrapperClasses().getSamWrapperClass(samType, expression.getContainingKtFile(), getParentCodegen());
2275    
2276            return StackValue.operation(asmType, new Function1<InstructionAdapter, Unit>() {
2277                @Override
2278                public Unit invoke(InstructionAdapter v) {
2279                    v.anew(asmType);
2280                    v.dup();
2281    
2282                    Type functionType = typeMapper.mapType(samType.getKotlinFunctionType());
2283                    expression.accept(visitor, StackValue.none()).put(functionType, v);
2284    
2285                    Label ifNonNull = new Label();
2286                    Label afterAll = new Label();
2287    
2288                    v.dup();
2289                    v.ifnonnull(ifNonNull);
2290    
2291                    // if null: pop function value and wrapper objects, put null
2292                    v.pop();
2293                    v.pop2();
2294                    v.aconst(null);
2295                    v.goTo(afterAll);
2296    
2297                    v.mark(ifNonNull);
2298                    v.invokespecial(asmType.getInternalName(), "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, functionType), false);
2299    
2300                    v.mark(afterAll);
2301                    return null;
2302                }
2303            });
2304        }
2305    
2306        @NotNull
2307        protected FunctionDescriptor accessibleFunctionDescriptor(@NotNull ResolvedCall<?> resolvedCall) {
2308            FunctionDescriptor descriptor = (FunctionDescriptor) resolvedCall.getResultingDescriptor();
2309            FunctionDescriptor originalIfSamAdapter = SamCodegenUtil.getOriginalIfSamAdapter(descriptor);
2310            if (originalIfSamAdapter != null) {
2311                descriptor = originalIfSamAdapter;
2312            }
2313            // $default method is not private, so you need no accessor to call it
2314            return CallUtilKt.usesDefaultArguments(resolvedCall)
2315                   ? descriptor
2316                   : context.accessibleDescriptor(descriptor, getSuperCallTarget(resolvedCall.getCall()));
2317        }
2318    
2319        @NotNull
2320        public StackValue invokeFunction(@NotNull ResolvedCall<?> resolvedCall, @NotNull StackValue receiver) {
2321            return invokeFunction(resolvedCall.getCall(), resolvedCall, receiver);
2322        }
2323    
2324        @NotNull
2325        public StackValue invokeFunction(@NotNull Call call, @NotNull ResolvedCall<?> resolvedCall, @NotNull StackValue receiver) {
2326            FunctionDescriptor fd = accessibleFunctionDescriptor(resolvedCall);
2327            ClassDescriptor superCallTarget = getSuperCallTarget(call);
2328    
2329            fd = context.getAccessorForSuperCallIfNeeded(fd, superCallTarget);
2330    
2331            Collection<ExpressionCodegenExtension> codegenExtensions = ExpressionCodegenExtension.Companion.getInstances(state.getProject());
2332            if (!codegenExtensions.isEmpty()) {
2333                ExpressionCodegenExtension.Context context = new ExpressionCodegenExtension.Context(typeMapper, v);
2334                for (ExpressionCodegenExtension extension : codegenExtensions) {
2335                    StackValue stackValue = extension.applyFunction(receiver, resolvedCall, context);
2336                    if (stackValue != null) return stackValue;
2337                }
2338            }
2339    
2340            Callable callable = resolveToCallable(fd, superCallTarget != null, resolvedCall);
2341    
2342            return callable.invokeMethodWithArguments(resolvedCall, receiver, this);
2343        }
2344    
2345        @Nullable
2346        // Find the first parent of the current context which corresponds to a subclass of a given class
2347        public static CodegenContext getParentContextSubclassOf(ClassDescriptor descriptor, CodegenContext context) {
2348            CodegenContext c = context;
2349            while (c != null) {
2350                if (c instanceof ClassContext && DescriptorUtils.isSubclass(c.getThisDescriptor(), descriptor)) {
2351                    return c;
2352                }
2353                c = c.getParentContext();
2354            }
2355            return null;
2356        }
2357    
2358        @NotNull
2359        Callable resolveToCallable(@NotNull FunctionDescriptor fd, boolean superCall, @NotNull ResolvedCall resolvedCall) {
2360            IntrinsicMethod intrinsic = state.getIntrinsics().getIntrinsic(fd);
2361            if (intrinsic != null) {
2362                return intrinsic.toCallable(fd, superCall, resolvedCall, this);
2363            }
2364    
2365            return resolveToCallableMethod(fd, superCall);
2366        }
2367    
2368        @NotNull
2369        private CallableMethod resolveToCallableMethod(@NotNull FunctionDescriptor fd, boolean superCall) {
2370            return typeMapper.mapToCallableMethod(SamCodegenUtil.resolveSamAdapter(fd), superCall);
2371        }
2372    
2373        public void invokeMethodWithArguments(
2374                @NotNull Callable callableMethod,
2375                @NotNull ResolvedCall<?> resolvedCall,
2376                @NotNull StackValue receiver
2377        ) {
2378            CallGenerator callGenerator = getOrCreateCallGenerator(resolvedCall);
2379            CallableDescriptor descriptor = resolvedCall.getResultingDescriptor();
2380    
2381            assert callGenerator == defaultCallGenerator || !tailRecursionCodegen.isTailRecursion(resolvedCall) :
2382                    "Tail recursive method can't be inlined: " + descriptor;
2383    
2384            ArgumentGenerator argumentGenerator = new CallBasedArgumentGenerator(this, callGenerator, descriptor.getValueParameters(),
2385                                                                                 callableMethod.getValueParameterTypes());
2386    
2387            invokeMethodWithArguments(callableMethod, resolvedCall, receiver, callGenerator, argumentGenerator);
2388        }
2389    
2390        public void invokeMethodWithArguments(
2391                @NotNull Callable callableMethod,
2392                @NotNull ResolvedCall<?> resolvedCall,
2393                @NotNull StackValue receiver,
2394                @NotNull CallGenerator callGenerator,
2395                @NotNull ArgumentGenerator argumentGenerator
2396        ) {
2397            boolean isConstructor = resolvedCall.getResultingDescriptor() instanceof ConstructorDescriptor;
2398            if (!isConstructor) { // otherwise already
2399                receiver = StackValue.receiver(resolvedCall, receiver, this, callableMethod);
2400                receiver.put(receiver.type, v);
2401                callableMethod.afterReceiverGeneration(v);
2402            }
2403    
2404            callGenerator.putHiddenParams();
2405    
2406            List<ResolvedValueArgument> valueArguments = resolvedCall.getValueArgumentsByIndex();
2407            assert valueArguments != null : "Failed to arrange value arguments by index: " + resolvedCall.getResultingDescriptor();
2408    
2409            DefaultCallArgs defaultArgs =
2410                    argumentGenerator.generate(valueArguments, new ArrayList<ResolvedValueArgument>(resolvedCall.getValueArguments().values()));
2411    
2412            if (tailRecursionCodegen.isTailRecursion(resolvedCall)) {
2413                tailRecursionCodegen.generateTailRecursion(resolvedCall);
2414                return;
2415            }
2416    
2417            boolean defaultMaskWasGenerated = defaultArgs.generateOnStackIfNeeded(callGenerator, isConstructor);
2418    
2419            // Extra constructor marker argument
2420            if (callableMethod instanceof CallableMethod) {
2421                List<JvmMethodParameterSignature> callableParameters = ((CallableMethod) callableMethod).getValueParameters();
2422                for (JvmMethodParameterSignature parameter: callableParameters) {
2423                    if (parameter.getKind() == JvmMethodParameterKind.CONSTRUCTOR_MARKER) {
2424                        callGenerator.putValueIfNeeded(parameter.getAsmType(), StackValue.constant(null, parameter.getAsmType()));
2425                    }
2426                }
2427            }
2428    
2429            callGenerator.genCall(callableMethod, resolvedCall, defaultMaskWasGenerated, this);
2430    
2431            KotlinType returnType = resolvedCall.getResultingDescriptor().getReturnType();
2432            if (returnType != null && KotlinBuiltIns.isNothing(returnType)) {
2433                v.aconst(null);
2434                v.athrow();
2435            }
2436        }
2437    
2438        @NotNull
2439        private CallGenerator getOrCreateCallGenerator(
2440                @NotNull CallableDescriptor descriptor,
2441                @Nullable KtElement callElement,
2442                @Nullable TypeParameterMappings typeParameterMappings,
2443                boolean isDefaultCompilation
2444        ) {
2445            if (callElement == null) return defaultCallGenerator;
2446    
2447            // We should inline callable containing reified type parameters even if inline is disabled
2448            // because they may contain something to reify and straight call will probably fail at runtime
2449            boolean isInline = (state.isInlineEnabled() || InlineUtil.containsReifiedTypeParameters(descriptor)) &&
2450                               (InlineUtil.isInline(descriptor) || InlineUtil.isArrayConstructorWithLambda(descriptor));
2451    
2452            if (!isInline) return defaultCallGenerator;
2453    
2454            FunctionDescriptor original = DescriptorUtils.unwrapFakeOverride((FunctionDescriptor) descriptor.getOriginal());
2455            return new InlineCodegen(this, state, original, callElement, typeParameterMappings, isDefaultCompilation);
2456        }
2457    
2458        @NotNull
2459        protected CallGenerator getOrCreateCallGeneratorForDefaultImplBody(@NotNull FunctionDescriptor descriptor, @Nullable KtNamedFunction function) {
2460            return getOrCreateCallGenerator(descriptor, function, null, true);
2461        }
2462    
2463        @NotNull
2464        CallGenerator getOrCreateCallGenerator(@NotNull ResolvedCall<?> resolvedCall) {
2465            Map<TypeParameterDescriptor, KotlinType> typeArguments = resolvedCall.getTypeArguments();
2466            TypeParameterMappings mappings = new TypeParameterMappings();
2467            for (Map.Entry<TypeParameterDescriptor, KotlinType> entry : typeArguments.entrySet()) {
2468                TypeParameterDescriptor key = entry.getKey();
2469                KotlinType type = entry.getValue();
2470    
2471                boolean isReified = key.isReified() || InlineUtil.isArrayConstructorWithLambda(resolvedCall.getResultingDescriptor());
2472    
2473                Pair<TypeParameterDescriptor, ReificationArgument> typeParameterAndReificationArgument = extractReificationArgument(type);
2474                if (typeParameterAndReificationArgument == null) {
2475                    KotlinType approximatedType = CapturedTypeApproximationKt.approximateCapturedTypes(entry.getValue()).getUpper();
2476                    // type is not generic
2477                    BothSignatureWriter signatureWriter = new BothSignatureWriter(BothSignatureWriter.Mode.TYPE);
2478                    Type asmType = typeMapper.mapTypeParameter(approximatedType, signatureWriter);
2479    
2480                    mappings.addParameterMappingToType(
2481                            key.getName().getIdentifier(), approximatedType, asmType, signatureWriter.toString(), isReified
2482                    );
2483                }
2484                else {
2485                    mappings.addParameterMappingForFurtherReification(
2486                            key.getName().getIdentifier(), type, typeParameterAndReificationArgument.getSecond(), isReified
2487                    );
2488                }
2489            }
2490            return getOrCreateCallGenerator(
2491                    resolvedCall.getResultingDescriptor(), resolvedCall.getCall().getCallElement(), mappings, false);
2492        }
2493    
2494    
2495        @Nullable
2496        private static Pair<TypeParameterDescriptor, ReificationArgument> extractReificationArgument(@NotNull KotlinType type) {
2497            int arrayDepth = 0;
2498            boolean isNullable = type.isMarkedNullable();
2499            while (KotlinBuiltIns.isArray(type)) {
2500                arrayDepth++;
2501                type = type.getArguments().get(0).getType();
2502            }
2503    
2504            TypeParameterDescriptor parameterDescriptor = TypeUtils.getTypeParameterDescriptorOrNull(type);
2505            if (parameterDescriptor == null) return null;
2506    
2507            return new Pair<TypeParameterDescriptor, ReificationArgument>(
2508                    parameterDescriptor,
2509                    new ReificationArgument(parameterDescriptor.getName().asString(), isNullable, arrayDepth));
2510        }
2511    
2512        @NotNull
2513        public StackValue generateReceiverValue(@Nullable ReceiverValue receiverValue, boolean isSuper) {
2514            if (receiverValue instanceof ImplicitClassReceiver) {
2515                ClassDescriptor receiverDescriptor = ((ImplicitClassReceiver) receiverValue).getDeclarationDescriptor();
2516                if (DescriptorUtils.isCompanionObject(receiverDescriptor)) {
2517                    CallableMemberDescriptor contextDescriptor = context.getContextDescriptor();
2518                    if (contextDescriptor instanceof FunctionDescriptor && receiverDescriptor == contextDescriptor.getContainingDeclaration()) {
2519                        return StackValue.LOCAL_0;
2520                    }
2521                    else {
2522                        return StackValue.singleton(receiverDescriptor, typeMapper);
2523                    }
2524                }
2525                else if (receiverDescriptor instanceof ScriptDescriptor) {
2526                    return generateScriptReceiver
2527                            ((ScriptDescriptor) receiverDescriptor);
2528                }
2529                else {
2530                    return StackValue.thisOrOuter(this, receiverDescriptor, isSuper,
2531                                                  receiverValue instanceof CastImplicitClassReceiver || isEnumEntry(receiverDescriptor));
2532                }
2533            }
2534            else if (receiverValue instanceof ExtensionReceiver) {
2535                return generateReceiver(((ExtensionReceiver) receiverValue).getDeclarationDescriptor());
2536            }
2537            else if (receiverValue instanceof ExpressionReceiver) {
2538                return gen(((ExpressionReceiver) receiverValue).getExpression());
2539            }
2540            else {
2541                throw new UnsupportedOperationException("Unsupported receiver value: " + receiverValue);
2542            }
2543        }
2544    
2545        @NotNull
2546        private StackValue generateReceiver(@NotNull CallableDescriptor descriptor) {
2547            return context.generateReceiver(descriptor, state, false);
2548        }
2549    
2550        @NotNull
2551        private StackValue generateScriptReceiver(@NotNull ScriptDescriptor receiver) {
2552            CodegenContext cur = context;
2553            StackValue result = StackValue.LOCAL_0;
2554            boolean inStartConstructorContext = cur instanceof ConstructorContext;
2555            while (cur != null) {
2556                if (!inStartConstructorContext) {
2557                    cur = getNotNullParentContextForMethod(cur);
2558                }
2559    
2560                if (cur instanceof ScriptContext) {
2561                    ScriptContext scriptContext = (ScriptContext) cur;
2562    
2563                    if (scriptContext.getScriptDescriptor() == receiver) {
2564                        //TODO lazy
2565                        return result;
2566                    }
2567                    Type currentScriptType = typeMapper.mapType(scriptContext.getScriptDescriptor());
2568                    Type classType = typeMapper.mapType(receiver);
2569                    String fieldName = scriptContext.getScriptFieldName(receiver);
2570                    return StackValue.field(classType, currentScriptType, fieldName, false, result, receiver);
2571                }
2572    
2573                result = cur.getOuterExpression(result, false);
2574    
2575                if (inStartConstructorContext) {
2576                    cur = getNotNullParentContextForMethod(cur);
2577                    inStartConstructorContext = false;
2578                }
2579    
2580                cur = cur.getParentContext();
2581            }
2582    
2583            throw new UnsupportedOperationException();
2584        }
2585    
2586        @NotNull
2587        public StackValue generateThisOrOuter(@NotNull ClassDescriptor calleeContainingClass, boolean isSuper) {
2588            return generateThisOrOuter(calleeContainingClass, isSuper, false);
2589        }
2590    
2591        @NotNull
2592        public StackValue generateThisOrOuter(@NotNull ClassDescriptor calleeContainingClass, boolean isSuper, boolean forceOuter) {
2593            boolean isSingleton = calleeContainingClass.getKind().isSingleton();
2594            if (isSingleton) {
2595                if (calleeContainingClass.equals(context.getThisDescriptor()) &&
2596                    !AnnotationUtilKt.isPlatformStaticInObjectOrClass(context.getContextDescriptor())) {
2597                    return StackValue.local(0, typeMapper.mapType(calleeContainingClass));
2598                }
2599                else if (isEnumEntry(calleeContainingClass)) {
2600                    return StackValue.enumEntry(calleeContainingClass, typeMapper);
2601                }
2602                else {
2603                    return StackValue.singleton(calleeContainingClass, typeMapper);
2604                }
2605            }
2606    
2607            CodegenContext cur = context;
2608            Type type = asmType(calleeContainingClass.getDefaultType());
2609            StackValue result = StackValue.local(0, type);
2610            boolean inStartConstructorContext = cur instanceof ConstructorContext;
2611            while (cur != null) {
2612                ClassDescriptor thisDescriptor = cur.getThisDescriptor();
2613    
2614                if (!isSuper && thisDescriptor == calleeContainingClass) {
2615                    return result;
2616                }
2617    
2618                if (!forceOuter && isSuper && DescriptorUtils.isSubclass(thisDescriptor, calleeContainingClass)) {
2619                    return castToRequiredTypeOfInterfaceIfNeeded(result, thisDescriptor, calleeContainingClass);
2620                }
2621    
2622                forceOuter = false;
2623    
2624                //for constructor super call we should access to outer instance through parameter in locals, in other cases through field for captured outer
2625                if (inStartConstructorContext) {
2626                    result = cur.getOuterExpression(result, false);
2627                    cur = getNotNullParentContextForMethod(cur);
2628                    inStartConstructorContext = false;
2629                }
2630                else {
2631                    cur = getNotNullParentContextForMethod(cur);
2632                    result = cur.getOuterExpression(result, false);
2633                }
2634    
2635                cur = cur.getParentContext();
2636            }
2637    
2638            throw new UnsupportedOperationException();
2639        }
2640    
2641        @NotNull
2642        private static CodegenContext getNotNullParentContextForMethod(CodegenContext cur) {
2643            if (cur instanceof MethodContext) {
2644                cur = cur.getParentContext();
2645            }
2646            assert cur != null;
2647            return cur;
2648        }
2649    
2650    
2651        public void genVarargs(@NotNull VarargValueArgument valueArgument, @NotNull KotlinType outType) {
2652            Type type = asmType(outType);
2653            assert type.getSort() == Type.ARRAY;
2654            Type elementType = correctElementType(type);
2655            List<ValueArgument> arguments = valueArgument.getArguments();
2656            int size = arguments.size();
2657    
2658            boolean hasSpread = false;
2659            for (int i = 0; i != size; ++i) {
2660                if (arguments.get(i).getSpreadElement() != null) {
2661                    hasSpread = true;
2662                    break;
2663                }
2664            }
2665    
2666            if (hasSpread) {
2667                boolean arrayOfReferences = KotlinBuiltIns.isArray(outType);
2668                if (size == 1) {
2669                    // Arrays.copyOf(array, newLength)
2670                    ValueArgument argument = arguments.get(0);
2671                    Type arrayType = arrayOfReferences ? Type.getType("[Ljava/lang/Object;")
2672                                                       : Type.getType("[" + elementType.getDescriptor());
2673                    gen(argument.getArgumentExpression(), type);
2674                    v.dup();
2675                    v.arraylength();
2676                    v.invokestatic("java/util/Arrays", "copyOf", Type.getMethodDescriptor(arrayType, arrayType, Type.INT_TYPE), false);
2677                    if (arrayOfReferences) {
2678                        v.checkcast(type);
2679                    }
2680                }
2681                else {
2682                    String owner;
2683                    String addDescriptor;
2684                    String toArrayDescriptor;
2685                    if (arrayOfReferences) {
2686                        owner = "kotlin/jvm/internal/SpreadBuilder";
2687                        addDescriptor = "(Ljava/lang/Object;)V";
2688                        toArrayDescriptor = "([Ljava/lang/Object;)[Ljava/lang/Object;";
2689                    }
2690                    else {
2691                        String spreadBuilderClassName = AsmUtil.asmPrimitiveTypeToLangPrimitiveType(elementType).getTypeName().getIdentifier() + "SpreadBuilder";
2692                        owner = "kotlin/jvm/internal/" + spreadBuilderClassName;
2693                        addDescriptor = "(" + elementType.getDescriptor() + ")V";
2694                        toArrayDescriptor = "()" + type.getDescriptor();
2695                    }
2696                    v.anew(Type.getObjectType(owner));
2697                    v.dup();
2698                    v.iconst(size);
2699                    v.invokespecial(owner, "<init>", "(I)V", false);
2700                    for (int i = 0; i != size; ++i) {
2701                        v.dup();
2702                        ValueArgument argument = arguments.get(i);
2703                        if (argument.getSpreadElement() != null) {
2704                            gen(argument.getArgumentExpression(), OBJECT_TYPE);
2705                            v.invokevirtual(owner, "addSpread", "(Ljava/lang/Object;)V", false);
2706                        }
2707                        else {
2708                            gen(argument.getArgumentExpression(), elementType);
2709                            v.invokevirtual(owner, "add", addDescriptor, false);
2710                        }
2711                    }
2712                    if (arrayOfReferences) {
2713                        v.dup();
2714                        v.invokevirtual(owner, "size", "()I", false);
2715                        newArrayInstruction(outType);
2716                        v.invokevirtual(owner, "toArray", toArrayDescriptor, false);
2717                        v.checkcast(type);
2718                    }
2719                    else {
2720                        v.invokevirtual(owner, "toArray", toArrayDescriptor, false);
2721                    }
2722                }
2723            }
2724            else {
2725                v.iconst(arguments.size());
2726                newArrayInstruction(outType);
2727                for (int i = 0; i != size; ++i) {
2728                    v.dup();
2729                    StackValue rightSide = gen(arguments.get(i).getArgumentExpression());
2730                    StackValue.arrayElement(elementType, StackValue.onStack(type), StackValue.constant(i, Type.INT_TYPE)).store(rightSide, v);
2731                }
2732            }
2733        }
2734    
2735        public int indexOfLocal(KtReferenceExpression lhs) {
2736            DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, lhs);
2737            if (isVarCapturedInClosure(bindingContext, declarationDescriptor)) {
2738                return -1;
2739            }
2740            return lookupLocalIndex(declarationDescriptor);
2741        }
2742    
2743        @Override
2744        public StackValue visitClassLiteralExpression(@NotNull KtClassLiteralExpression expression, StackValue data) {
2745            KotlinType type = bindingContext.getType(expression);
2746            assert type != null;
2747    
2748            assert state.getReflectionTypes().getKClass().getTypeConstructor().equals(type.getConstructor())
2749                    : "::class expression should be type checked to a KClass: " + type;
2750    
2751            return generateClassLiteralReference(typeMapper, CollectionsKt.single(type.getArguments()).getType(), this);
2752        }
2753    
2754        @Override
2755        public StackValue visitCallableReferenceExpression(@NotNull KtCallableReferenceExpression expression, StackValue data) {
2756            ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCallWithAssert(expression.getCallableReference(), bindingContext);
2757            FunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression);
2758            if (functionDescriptor != null) {
2759                FunctionReferenceGenerationStrategy strategy = new FunctionReferenceGenerationStrategy(state, functionDescriptor, resolvedCall);
2760                return genClosure(expression, functionDescriptor, strategy, null, (FunctionDescriptor) resolvedCall.getResultingDescriptor());
2761            }
2762    
2763            VariableDescriptor variableDescriptor = bindingContext.get(VARIABLE, expression);
2764            if (variableDescriptor != null) {
2765                return generatePropertyReference(expression, variableDescriptor, resolvedCall);
2766            }
2767    
2768            throw new UnsupportedOperationException("Unsupported callable reference expression: " + expression.getText());
2769        }
2770    
2771        @NotNull
2772        private StackValue generatePropertyReference(
2773                @NotNull KtElement element,
2774                @NotNull VariableDescriptor variableDescriptor,
2775                @NotNull ResolvedCall<?> resolvedCall
2776        ) {
2777            ClassDescriptor classDescriptor = CodegenBinding.anonymousClassForCallable(bindingContext, variableDescriptor);
2778    
2779            ClassBuilder classBuilder = state.getFactory().newVisitor(
2780                    JvmDeclarationOriginKt.OtherOrigin(element),
2781                    typeMapper.mapClass(classDescriptor),
2782                    element.getContainingFile()
2783            );
2784    
2785            PropertyReferenceCodegen codegen = new PropertyReferenceCodegen(
2786                    state, parentCodegen, context.intoAnonymousClass(classDescriptor, this, OwnerKind.IMPLEMENTATION),
2787                    element, classBuilder, resolvedCall
2788            );
2789            codegen.generate();
2790    
2791            return codegen.putInstanceOnStack();
2792        }
2793    
2794        @NotNull
2795        public static StackValue generateClassLiteralReference(@NotNull JetTypeMapper typeMapper, @NotNull KotlinType type) {
2796            return generateClassLiteralReference(typeMapper, type, null);
2797        }
2798    
2799        @NotNull
2800        private static StackValue generateClassLiteralReference(@NotNull final JetTypeMapper typeMapper, @NotNull final KotlinType type, @Nullable final ExpressionCodegen codegen) {
2801            return StackValue.operation(K_CLASS_TYPE, new Function1<InstructionAdapter, Unit>() {
2802                @Override
2803                public Unit invoke(InstructionAdapter v) {
2804                    Type classAsmType = typeMapper.mapType(type);
2805                    ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
2806                    if (descriptor instanceof TypeParameterDescriptor) {
2807                        TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) descriptor;
2808                        assert typeParameterDescriptor.isReified() :
2809                                "Non-reified type parameter under ::class should be rejected by type checker: " + typeParameterDescriptor;
2810                        assert codegen != null :
2811                                "Reference to member of reified type should be rejected by type checker " + typeParameterDescriptor;
2812                        codegen.putReifiedOperationMarkerIfTypeIsReifiedParameter(type, ReifiedTypeInliner.OperationKind.JAVA_CLASS);
2813                    }
2814    
2815                    putJavaLangClassInstance(v, classAsmType);
2816                    wrapJavaClassIntoKClass(v);
2817    
2818                    return Unit.INSTANCE;
2819                }
2820            });
2821        }
2822    
2823        @Override
2824        public StackValue visitDotQualifiedExpression(@NotNull KtDotQualifiedExpression expression, StackValue receiver) {
2825            StackValue receiverValue = StackValue.none(); //gen(expression.getReceiverExpression())
2826            return genQualified(receiverValue, expression.getSelectorExpression());
2827        }
2828    
2829        private StackValue generateExpressionWithNullFallback(@NotNull KtExpression expression, @NotNull Label ifnull) {
2830            KtExpression deparenthesized = KtPsiUtil.deparenthesize(expression);
2831            assert deparenthesized != null : "Unexpected empty expression";
2832    
2833            expression = deparenthesized;
2834            Type type = expressionType(expression);
2835    
2836            if (expression instanceof KtSafeQualifiedExpression && !isPrimitive(type)) {
2837                return StackValue.coercion(generateSafeQualifiedExpression((KtSafeQualifiedExpression) expression, ifnull), type);
2838            }
2839            else {
2840                return genLazy(expression, type);
2841            }
2842        }
2843    
2844        private StackValue generateSafeQualifiedExpression(@NotNull KtSafeQualifiedExpression expression, @NotNull Label ifNull) {
2845            KtExpression receiver = expression.getReceiverExpression();
2846            KtExpression selector = expression.getSelectorExpression();
2847    
2848            Type receiverType = expressionType(receiver);
2849            StackValue receiverValue = generateExpressionWithNullFallback(receiver, ifNull);
2850    
2851            //Do not optimize for primitives cause in case of safe call extension receiver should be generated before dispatch one
2852            StackValue newReceiver = new StackValue.SafeCall(receiverType, receiverValue, isPrimitive(receiverType) ? null : ifNull);
2853            return genQualified(newReceiver, selector);
2854        }
2855    
2856        @Override
2857        public StackValue visitSafeQualifiedExpression(@NotNull KtSafeQualifiedExpression expression, StackValue unused) {
2858            Label ifnull = new Label();
2859            Type type = boxType(expressionType(expression));
2860    
2861            StackValue value = generateSafeQualifiedExpression(expression, ifnull);
2862            StackValue newReceiver = StackValue.coercion(value, type);
2863            StackValue result;
2864    
2865            if (!isPrimitive(expressionType(expression.getReceiverExpression()))) {
2866                result = new StackValue.SafeFallback(type, ifnull, newReceiver);
2867            } else {
2868                result = newReceiver;
2869            }
2870    
2871            return result;
2872        }
2873    
2874        @Override
2875        public StackValue visitBinaryExpression(@NotNull KtBinaryExpression expression, @NotNull StackValue receiver) {
2876            KtSimpleNameExpression reference = expression.getOperationReference();
2877            IElementType opToken = reference.getReferencedNameElementType();
2878            if (opToken == KtTokens.EQ) {
2879                return generateAssignmentExpression(expression);
2880            }
2881            else if (KtTokens.AUGMENTED_ASSIGNMENTS.contains(opToken)) {
2882                return generateAugmentedAssignment(expression);
2883            }
2884            else if (opToken == KtTokens.ANDAND) {
2885                return generateBooleanAnd(expression);
2886            }
2887            else if (opToken == KtTokens.OROR) {
2888                return generateBooleanOr(expression);
2889            }
2890            else if (opToken == KtTokens.EQEQ || opToken == KtTokens.EXCLEQ ||
2891                     opToken == KtTokens.EQEQEQ || opToken == KtTokens.EXCLEQEQEQ) {
2892                return generateEquals(expression.getLeft(), expression.getRight(), opToken);
2893            }
2894            else if (opToken == KtTokens.LT || opToken == KtTokens.LTEQ ||
2895                     opToken == KtTokens.GT || opToken == KtTokens.GTEQ) {
2896                return generateComparison(expression, receiver);
2897            }
2898            else if (opToken == KtTokens.ELVIS) {
2899                return generateElvis(expression);
2900            }
2901            else if (opToken == KtTokens.IN_KEYWORD || opToken == KtTokens.NOT_IN) {
2902                return generateIn(StackValue.expression(expressionType(expression.getLeft()), expression.getLeft(), this),
2903                                  expression.getRight(), reference);
2904            }
2905            else {
2906                ConstantValue<?> compileTimeConstant = getCompileTimeConstant(expression, bindingContext, true);
2907                if (compileTimeConstant != null) {
2908                    return StackValue.constant(compileTimeConstant.getValue(), expressionType(expression));
2909                }
2910    
2911                ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCallWithAssert(expression, bindingContext);
2912                FunctionDescriptor descriptor = (FunctionDescriptor) resolvedCall.getResultingDescriptor();
2913    
2914                if (descriptor instanceof ConstructorDescriptor) {
2915                    return generateConstructorCall(resolvedCall, expressionType(expression));
2916                }
2917    
2918                return invokeFunction(resolvedCall, receiver);
2919            }
2920        }
2921    
2922        private StackValue generateIn(final StackValue leftValue, KtExpression rangeExpression, final KtSimpleNameExpression operationReference) {
2923            final KtExpression deparenthesized = KtPsiUtil.deparenthesize(rangeExpression);
2924    
2925            assert deparenthesized != null : "For with empty range expression";
2926    
2927            return StackValue.operation(Type.BOOLEAN_TYPE, new Function1<InstructionAdapter, Unit>() {
2928                @Override
2929                public Unit invoke(InstructionAdapter v) {
2930                    if (isIntRangeExpr(deparenthesized) && AsmUtil.isIntPrimitive(leftValue.type)) {
2931                        genInIntRange(leftValue, (KtBinaryExpression) deparenthesized);
2932                    }
2933                    else {
2934                        ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilKt
2935                                .getResolvedCallWithAssert(operationReference, bindingContext);
2936                        StackValue result = invokeFunction(resolvedCall.getCall(), resolvedCall, StackValue.none());
2937                        result.put(result.type, v);
2938                    }
2939                    if (operationReference.getReferencedNameElementType() == KtTokens.NOT_IN) {
2940                        genInvertBoolean(v);
2941                    }
2942                    return null;
2943                }
2944            });
2945        }
2946    
2947        private void genInIntRange(StackValue leftValue, KtBinaryExpression rangeExpression) {
2948            v.iconst(1);
2949            // 1
2950            leftValue.put(Type.INT_TYPE, v);
2951            // 1 l
2952            v.dup2();
2953            // 1 l 1 l
2954    
2955            //noinspection ConstantConditions
2956            gen(rangeExpression.getLeft(), Type.INT_TYPE);
2957            // 1 l 1 l r
2958            Label lok = new Label();
2959            v.ificmpge(lok);
2960            // 1 l 1
2961            v.pop();
2962            v.iconst(0);
2963            v.mark(lok);
2964            // 1 l c
2965            v.dupX2();
2966            // c 1 l c
2967            v.pop();
2968            // c 1 l
2969    
2970            gen(rangeExpression.getRight(), Type.INT_TYPE);
2971            // c 1 l r
2972            Label rok = new Label();
2973            v.ificmple(rok);
2974            // c 1
2975            v.pop();
2976            v.iconst(0);
2977            v.mark(rok);
2978            // c c
2979    
2980            v.and(Type.INT_TYPE);
2981        }
2982    
2983        private StackValue generateBooleanAnd(KtBinaryExpression expression) {
2984            return StackValue.and(gen(expression.getLeft()), gen(expression.getRight()));
2985        }
2986    
2987        private StackValue generateBooleanOr(KtBinaryExpression expression) {
2988            return StackValue.or(gen(expression.getLeft()), gen(expression.getRight()));
2989        }
2990    
2991        private StackValue generateEquals(KtExpression left, KtExpression right, IElementType opToken) {
2992            Type leftType = expressionType(left);
2993            Type rightType = expressionType(right);
2994    
2995            if (KtPsiUtil.isNullConstant(left)) {
2996                return genCmpWithNull(right, opToken);
2997            }
2998    
2999            if (KtPsiUtil.isNullConstant(right)) {
3000                return genCmpWithNull(left, opToken);
3001            }
3002    
3003            if (isIntZero(left, leftType) && isIntPrimitive(rightType)) {
3004                return genCmpWithZero(right, opToken);
3005            }
3006    
3007            if (isIntZero(right, rightType) && isIntPrimitive(leftType)) {
3008                return genCmpWithZero(left, opToken);
3009            }
3010    
3011            if (isPrimitive(leftType) != isPrimitive(rightType)) {
3012                leftType = boxType(leftType);
3013                rightType = boxType(rightType);
3014            }
3015    
3016            StackValue leftValue = genLazy(left, leftType);
3017            StackValue rightValue = genLazy(right, rightType);
3018    
3019            if (opToken == KtTokens.EQEQEQ || opToken == KtTokens.EXCLEQEQEQ) {
3020                // TODO: always casting to the type of the left operand in case of primitives looks wrong
3021                Type operandType = isPrimitive(leftType) ? leftType : OBJECT_TYPE;
3022                return StackValue.cmp(opToken, operandType, leftValue, rightValue);
3023            }
3024    
3025            return genEqualsForExpressionsOnStack(opToken, leftValue, rightValue);
3026        }
3027    
3028        private boolean isIntZero(KtExpression expr, Type exprType) {
3029            ConstantValue<?> exprValue = getCompileTimeConstant(expr, bindingContext);
3030            return isIntPrimitive(exprType) && exprValue != null && Integer.valueOf(0).equals(exprValue.getValue());
3031        }
3032    
3033        private StackValue genCmpWithZero(KtExpression exp, IElementType opToken) {
3034            return StackValue.compareIntWithZero(gen(exp), (KtTokens.EQEQ == opToken || KtTokens.EQEQEQ == opToken) ? IFNE : IFEQ);
3035        }
3036    
3037        private StackValue genCmpWithNull(KtExpression exp, IElementType opToken) {
3038            return StackValue.compareWithNull(gen(exp), (KtTokens.EQEQ == opToken || KtTokens.EQEQEQ == opToken) ? IFNONNULL : IFNULL);
3039        }
3040    
3041        private StackValue generateElvis(@NotNull final KtBinaryExpression expression) {
3042            KtExpression left = expression.getLeft();
3043    
3044            final Type exprType = expressionType(expression);
3045            final Type leftType = expressionType(left);
3046    
3047            final Label ifNull = new Label();
3048    
3049    
3050            assert left != null : "left expression in elvis should be not null: " + expression.getText();
3051            final StackValue value = generateExpressionWithNullFallback(left, ifNull);
3052    
3053            if (isPrimitive(leftType)) {
3054                return value;
3055            }
3056    
3057            return StackValue.operation(exprType, new Function1<InstructionAdapter, Unit>() {
3058                @Override
3059                public Unit invoke(InstructionAdapter v) {
3060                    value.put(value.type, v);
3061                    v.dup();
3062    
3063                    v.ifnull(ifNull);
3064                    StackValue.onStack(leftType).put(exprType, v);
3065    
3066                    Label end = new Label();
3067                    v.goTo(end);
3068    
3069                    v.mark(ifNull);
3070                    v.pop();
3071                    gen(expression.getRight(), exprType);
3072                    v.mark(end);
3073                    return null;
3074                }
3075            });
3076        }
3077    
3078        private StackValue generateComparison(KtBinaryExpression expression, StackValue receiver) {
3079            ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCallWithAssert(expression, bindingContext);
3080    
3081            KtExpression left = expression.getLeft();
3082            KtExpression right = expression.getRight();
3083    
3084            Type type;
3085            StackValue leftValue;
3086            StackValue rightValue;
3087            Type leftType = expressionType(left);
3088            Type rightType = expressionType(right);
3089            Callable callable = resolveToCallable((FunctionDescriptor) resolvedCall.getResultingDescriptor(), false, resolvedCall);
3090            if (callable instanceof IntrinsicCallable) {
3091                type = comparisonOperandType(leftType, rightType);
3092                leftValue = gen(left);
3093                rightValue = gen(right);
3094            }
3095            else {
3096                type = Type.INT_TYPE;
3097                leftValue = invokeFunction(resolvedCall, receiver);
3098                rightValue = StackValue.constant(0, type);
3099            }
3100            return StackValue.cmp(expression.getOperationToken(), type, leftValue, rightValue);
3101        }
3102    
3103        private StackValue generateAssignmentExpression(KtBinaryExpression expression) {
3104            StackValue stackValue = gen(expression.getLeft());
3105            KtExpression right = expression.getRight();
3106            assert right != null : expression.getText();
3107            stackValue.store(gen(right), v);
3108            return StackValue.none();
3109        }
3110    
3111        private StackValue generateAugmentedAssignment(KtBinaryExpression expression) {
3112            ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCallWithAssert(expression, bindingContext);
3113            FunctionDescriptor descriptor = accessibleFunctionDescriptor(resolvedCall);
3114            Callable callable = resolveToCallable(descriptor, false, resolvedCall);
3115            KtExpression lhs = expression.getLeft();
3116            Type lhsType = expressionType(lhs);
3117    
3118            boolean keepReturnValue = Boolean.TRUE.equals(bindingContext.get(VARIABLE_REASSIGNMENT, expression))
3119                    || !KotlinBuiltIns.isUnit(descriptor.getReturnType());
3120    
3121            callAugAssignMethod(expression, resolvedCall, callable, lhsType, keepReturnValue);
3122    
3123            return StackValue.none();
3124        }
3125    
3126        private void callAugAssignMethod(
3127                @NotNull KtBinaryExpression expression,
3128                @NotNull ResolvedCall<?> resolvedCall,
3129                @NotNull Callable callable,
3130                @NotNull Type lhsType,
3131                boolean keepReturnValue
3132        ) {
3133            StackValue value = gen(expression.getLeft());
3134            if (keepReturnValue) {
3135                value = StackValue.complexWriteReadReceiver(value);
3136            }
3137            value.put(lhsType, v);
3138            StackValue receiver = StackValue.onStack(lhsType);
3139    
3140            callable.invokeMethodWithArguments(resolvedCall, receiver, this).put(callable.getReturnType(), v);
3141    
3142            if (keepReturnValue) {
3143                value.store(StackValue.onStack(callable.getReturnType()), v, true);
3144            }
3145        }
3146    
3147        public void invokeAppend(KtExpression expr) {
3148            if (expr instanceof KtBinaryExpression) {
3149                KtBinaryExpression binaryExpression = (KtBinaryExpression) expr;
3150                if (binaryExpression.getOperationToken() == KtTokens.PLUS) {
3151                    KtExpression left = binaryExpression.getLeft();
3152                    KtExpression right = binaryExpression.getRight();
3153                    Type leftType = expressionType(left);
3154    
3155                    if (leftType.equals(JAVA_STRING_TYPE)) {
3156                        invokeAppend(left);
3157                        invokeAppend(right);
3158                        return;
3159                    }
3160                }
3161            }
3162            Type exprType = expressionType(expr);
3163            gen(expr, exprType);
3164            genInvokeAppendMethod(v, exprType.getSort() == Type.ARRAY ? OBJECT_TYPE : exprType);
3165        }
3166    
3167        @Nullable
3168        private static KtSimpleNameExpression targetLabel(KtExpression expression) {
3169            if (expression.getParent() instanceof KtLabeledExpression) {
3170                return ((KtLabeledExpression) expression.getParent()).getTargetLabel();
3171            }
3172            return null;
3173        }
3174    
3175        @Override
3176        public StackValue visitLabeledExpression(
3177                @NotNull KtLabeledExpression expression, StackValue receiver
3178        ) {
3179            return genQualified(receiver, expression.getBaseExpression());
3180        }
3181    
3182        @Override
3183        public StackValue visitPrefixExpression(@NotNull KtPrefixExpression expression, @NotNull StackValue receiver) {
3184            ConstantValue<?> compileTimeConstant = getCompileTimeConstant(expression, bindingContext, true);
3185            if (compileTimeConstant != null) {
3186                return StackValue.constant(compileTimeConstant.getValue(), expressionType(expression));
3187            }
3188    
3189            DeclarationDescriptor originalOperation = bindingContext.get(REFERENCE_TARGET, expression.getOperationReference());
3190            ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCallWithAssert(expression, bindingContext);
3191            CallableDescriptor op = resolvedCall.getResultingDescriptor();
3192    
3193            assert op instanceof FunctionDescriptor || originalOperation == null : String.valueOf(op);
3194            String operationName = originalOperation == null ? "" : originalOperation.getName().asString();
3195            if (!(operationName.equals("inc") || operationName.equals("dec"))) {
3196                return invokeFunction(resolvedCall, receiver);
3197            }
3198    
3199            int increment = operationName.equals("inc") ? 1 : -1;
3200            Type type = expressionType(expression.getBaseExpression());
3201            StackValue value = gen(expression.getBaseExpression());
3202            return StackValue.preIncrement(type, value, increment, resolvedCall, this);
3203        }
3204    
3205        @Override
3206        public StackValue visitPostfixExpression(@NotNull final KtPostfixExpression expression, StackValue receiver) {
3207            if (expression.getOperationReference().getReferencedNameElementType() == KtTokens.EXCLEXCL) {
3208                final StackValue base = genQualified(receiver, expression.getBaseExpression());
3209                if (isPrimitive(base.type)) {
3210                    return base;
3211                } else {
3212                    return StackValue.operation(base.type, new Function1<InstructionAdapter, Unit>() {
3213                        @Override
3214                        public Unit invoke(InstructionAdapter v) {
3215                            base.put(base.type, v);
3216                            v.dup();
3217                            Label ok = new Label();
3218                            v.ifnonnull(ok);
3219                            v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "throwNpe", "()V", false);
3220                            v.mark(ok);
3221                            return null;
3222                        }
3223                    });
3224                }
3225            }
3226    
3227            DeclarationDescriptor originalOperation = bindingContext.get(REFERENCE_TARGET, expression.getOperationReference());
3228            String originalOperationName = originalOperation != null ? originalOperation.getName().asString() : null;
3229            final ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCallWithAssert(expression, bindingContext);
3230            DeclarationDescriptor op = resolvedCall.getResultingDescriptor();
3231            if (!(op instanceof FunctionDescriptor) || originalOperation == null) {
3232                throw new UnsupportedOperationException("Don't know how to generate this postfix expression: " + originalOperationName + " " + op);
3233            }
3234    
3235    
3236            final Type asmResultType = expressionType(expression);
3237            final Type asmBaseType = expressionType(expression.getBaseExpression());
3238    
3239            DeclarationDescriptor cls = op.getContainingDeclaration();
3240    
3241            final int increment;
3242            if (originalOperationName.equals("inc")) {
3243                increment = 1;
3244            }
3245            else if (originalOperationName.equals("dec")) {
3246                increment = -1;
3247            }
3248            else {
3249                throw new UnsupportedOperationException("Unsupported postfix operation: " + originalOperationName + " " + op);
3250            }
3251    
3252            final boolean isPrimitiveNumberClassDescriptor = isPrimitiveNumberClassDescriptor(cls);
3253            if (isPrimitiveNumberClassDescriptor && AsmUtil.isPrimitive(asmBaseType)) {
3254                KtExpression operand = expression.getBaseExpression();
3255                // Optimization for j = i++, when j and i are Int without any smart cast: we just work with primitive int
3256                if (operand instanceof KtReferenceExpression && asmResultType == Type.INT_TYPE &&
3257                    bindingContext.get(BindingContext.SMARTCAST, operand) == null) {
3258                    int index = indexOfLocal((KtReferenceExpression) operand);
3259                    if (index >= 0) {
3260                        return StackValue.postIncrement(index, increment);
3261                    }
3262                }
3263            }
3264    
3265            return StackValue.operation(asmBaseType, new Function1<InstructionAdapter, Unit>() {
3266                @Override
3267                public Unit invoke(InstructionAdapter v) {
3268                    StackValue value = StackValue.complexWriteReadReceiver(gen(expression.getBaseExpression()));
3269    
3270                    value.put(asmBaseType, v);
3271                    AsmUtil.dup(v, asmBaseType);
3272    
3273                    StackValue previousValue = StackValue.local(myFrameMap.enterTemp(asmBaseType), asmBaseType);
3274                    previousValue.store(StackValue.onStack(asmBaseType), v);
3275    
3276                    Type storeType;
3277                    if (isPrimitiveNumberClassDescriptor && AsmUtil.isPrimitive(asmBaseType)) {
3278                        genIncrement(asmResultType, asmBaseType, increment, v);
3279                        storeType = asmBaseType;
3280                    }
3281                    else {
3282                        StackValue result = invokeFunction(resolvedCall, StackValue.onStack(asmBaseType));
3283                        result.put(result.type, v);
3284                        storeType = result.type;
3285                    }
3286    
3287                    value.store(StackValue.onStack(storeType), v, true);
3288    
3289                    previousValue.put(asmBaseType, v);
3290    
3291                    myFrameMap.leaveTemp(asmBaseType);
3292    
3293                    return Unit.INSTANCE;
3294                }
3295            });
3296        }
3297    
3298        @Override
3299        public StackValue visitProperty(@NotNull KtProperty property, StackValue receiver) {
3300            KtExpression initializer = property.getInitializer();
3301            if (initializer == null) {
3302                return StackValue.none();
3303            }
3304            initializeLocalVariable(property, gen(initializer));
3305            return StackValue.none();
3306        }
3307    
3308        @Override
3309        public StackValue visitDestructuringDeclaration(@NotNull KtDestructuringDeclaration multiDeclaration, StackValue receiver) {
3310            KtExpression initializer = multiDeclaration.getInitializer();
3311            if (initializer == null) return StackValue.none();
3312    
3313            KotlinType initializerType = bindingContext.getType(initializer);
3314            assert initializerType != null;
3315    
3316            Type initializerAsmType = asmType(initializerType);
3317    
3318            TransientReceiver initializerAsReceiver = new TransientReceiver(initializerType);
3319    
3320            int tempVarIndex = myFrameMap.enterTemp(initializerAsmType);
3321    
3322            gen(initializer, initializerAsmType);
3323            v.store(tempVarIndex, initializerAsmType);
3324            StackValue.Local local = StackValue.local(tempVarIndex, initializerAsmType);
3325    
3326            for (KtDestructuringDeclarationEntry variableDeclaration : multiDeclaration.getEntries()) {
3327                ResolvedCall<FunctionDescriptor> resolvedCall = bindingContext.get(COMPONENT_RESOLVED_CALL, variableDeclaration);
3328                assert resolvedCall != null : "Resolved call is null for " + variableDeclaration.getText();
3329                Call call = makeFakeCall(initializerAsReceiver);
3330                initializeLocalVariable(variableDeclaration, invokeFunction(call, resolvedCall, local));
3331            }
3332    
3333            if (initializerAsmType.getSort() == Type.OBJECT || initializerAsmType.getSort() == Type.ARRAY) {
3334                v.aconst(null);
3335                v.store(tempVarIndex, initializerAsmType);
3336            }
3337            myFrameMap.leaveTemp(initializerAsmType);
3338    
3339            return StackValue.none();
3340        }
3341    
3342        private void initializeLocalVariable(
3343                @NotNull KtVariableDeclaration variableDeclaration,
3344                @NotNull StackValue initializer
3345        ) {
3346            VariableDescriptor variableDescriptor = bindingContext.get(VARIABLE, variableDeclaration);
3347    
3348            if (KtPsiUtil.isScriptDeclaration(variableDeclaration)) {
3349                return;
3350            }
3351            int index = lookupLocalIndex(variableDescriptor);
3352    
3353            if (index < 0) {
3354                throw new IllegalStateException("Local variable not found for " + variableDescriptor);
3355            }
3356    
3357            Type sharedVarType = typeMapper.getSharedVarType(variableDescriptor);
3358            assert variableDescriptor != null;
3359    
3360            Type varType = asmType(variableDescriptor.getType());
3361    
3362            StackValue storeTo = sharedVarType == null ? StackValue.local(index, varType) : StackValue.shared(index, varType);
3363    
3364            storeTo.putReceiver(v, false);
3365            initializer.put(initializer.type, v);
3366    
3367            markLineNumber(variableDeclaration, false);
3368    
3369            storeTo.storeSelector(initializer.type, v);
3370        }
3371    
3372        @NotNull
3373        private StackValue generateNewCall(@NotNull KtCallExpression expression, @NotNull ResolvedCall<?> resolvedCall) {
3374            Type type = expressionType(expression);
3375            if (type.getSort() == Type.ARRAY) {
3376                //noinspection ConstantConditions
3377                return generateNewArray(expression, bindingContext.getType(expression), resolvedCall);
3378            }
3379    
3380            return generateConstructorCall(resolvedCall, type);
3381        }
3382    
3383        @NotNull
3384        public ConstructorDescriptor getConstructorDescriptor(@NotNull ResolvedCall<?> resolvedCall) {
3385            FunctionDescriptor accessibleDescriptor = accessibleFunctionDescriptor(resolvedCall);
3386            assert accessibleDescriptor instanceof ConstructorDescriptor :
3387                    "getConstructorDescriptor must be called only for constructors: " + accessibleDescriptor;
3388            return (ConstructorDescriptor) accessibleDescriptor;
3389        }
3390    
3391        @NotNull
3392        public StackValue generateConstructorCall(@NotNull final ResolvedCall<?> resolvedCall, @NotNull final Type objectType) {
3393            return StackValue.functionCall(objectType, new Function1<InstructionAdapter, Unit>() {
3394                @Override
3395                public Unit invoke(InstructionAdapter v) {
3396                    v.anew(objectType);
3397                    v.dup();
3398    
3399                    ConstructorDescriptor constructor = getConstructorDescriptor(resolvedCall);
3400    
3401                    ReceiverParameterDescriptor dispatchReceiver = constructor.getDispatchReceiverParameter();
3402                    ClassDescriptor containingDeclaration = constructor.getContainingDeclaration();
3403                    if (dispatchReceiver != null) {
3404                        Type receiverType = typeMapper.mapType(dispatchReceiver.getType());
3405                        ReceiverValue receiver = resolvedCall.getDispatchReceiver();
3406                        boolean callSuper = containingDeclaration.isInner() && receiver instanceof ImplicitClassReceiver;
3407                        generateReceiverValue(receiver, callSuper).put(receiverType, v);
3408                    }
3409    
3410                    // Resolved call to local class constructor doesn't have dispatchReceiver, so we need to generate closure on stack
3411                    // See StackValue.receiver for more info
3412                    pushClosureOnStack(containingDeclaration, dispatchReceiver == null, defaultCallGenerator);
3413    
3414                    constructor = SamCodegenUtil.resolveSamAdapter(constructor);
3415                    CallableMethod method = typeMapper.mapToCallableMethod(constructor, false);
3416                    invokeMethodWithArguments(method, resolvedCall, StackValue.none());
3417    
3418                    return Unit.INSTANCE;
3419                }
3420            });
3421        }
3422    
3423        public StackValue generateNewArray(
3424                @NotNull KtCallExpression expression, @NotNull final KotlinType arrayType, @NotNull ResolvedCall<?> resolvedCall
3425        ) {
3426            List<KtValueArgument> args = expression.getValueArguments();
3427            assert args.size() == 1 || args.size() == 2 : "Unknown constructor called: " + args.size() + " arguments";
3428    
3429            if (args.size() == 1) {
3430                final KtExpression sizeExpression = args.get(0).getArgumentExpression();
3431                return StackValue.operation(typeMapper.mapType(arrayType), new Function1<InstructionAdapter, Unit>() {
3432                    @Override
3433                    public Unit invoke(InstructionAdapter v) {
3434                        gen(sizeExpression, Type.INT_TYPE);
3435                        newArrayInstruction(arrayType);
3436                        return Unit.INSTANCE;
3437                    }
3438                });
3439            }
3440    
3441            return invokeFunction(resolvedCall, StackValue.none());
3442        }
3443    
3444        public void newArrayInstruction(@NotNull KotlinType arrayType) {
3445            if (KotlinBuiltIns.isArray(arrayType)) {
3446                KotlinType elementJetType = arrayType.getArguments().get(0).getType();
3447                putReifiedOperationMarkerIfTypeIsReifiedParameter(
3448                        elementJetType,
3449                        ReifiedTypeInliner.OperationKind.NEW_ARRAY
3450                );
3451                v.newarray(boxType(asmType(elementJetType)));
3452            }
3453            else {
3454                Type type = typeMapper.mapType(arrayType);
3455                v.newarray(correctElementType(type));
3456            }
3457        }
3458    
3459        @Override
3460        public StackValue visitArrayAccessExpression(@NotNull KtArrayAccessExpression expression, StackValue receiver) {
3461            KtExpression array = expression.getArrayExpression();
3462            KotlinType type = array != null ? bindingContext.getType(array) : null;
3463            Type arrayType = expressionType(array);
3464            List<KtExpression> indices = expression.getIndexExpressions();
3465            FunctionDescriptor operationDescriptor = (FunctionDescriptor) bindingContext.get(REFERENCE_TARGET, expression);
3466            assert operationDescriptor != null;
3467            if (arrayType.getSort() == Type.ARRAY &&
3468                indices.size() == 1 &&
3469                isInt(operationDescriptor.getValueParameters().get(0).getType())) {
3470                assert type != null;
3471                Type elementType;
3472                if (KotlinBuiltIns.isArray(type)) {
3473                    KotlinType jetElementType = type.getArguments().get(0).getType();
3474                    elementType = boxType(asmType(jetElementType));
3475                }
3476                else {
3477                    elementType = correctElementType(arrayType);
3478                }
3479                StackValue arrayValue = genLazy(array, arrayType);
3480                StackValue index = genLazy(indices.get(0), Type.INT_TYPE);
3481    
3482                return StackValue.arrayElement(elementType, arrayValue, index);
3483            }
3484            else {
3485                ResolvedCall<FunctionDescriptor> resolvedSetCall = bindingContext.get(INDEXED_LVALUE_SET, expression);
3486                ResolvedCall<FunctionDescriptor> resolvedGetCall = bindingContext.get(INDEXED_LVALUE_GET, expression);
3487    
3488                boolean isGetter = "get".equals(operationDescriptor.getName().asString());
3489    
3490                Callable callable = resolveToCallable(operationDescriptor, false, isGetter ? resolvedGetCall : resolvedSetCall);
3491                Callable callableMethod = resolveToCallableMethod(operationDescriptor, false);
3492                Type[] argumentTypes = callableMethod.getParameterTypes();
3493    
3494                StackValue.CollectionElementReceiver collectionElementReceiver = createCollectionElementReceiver(
3495                        expression, receiver, operationDescriptor, isGetter, resolvedGetCall, resolvedSetCall, callable
3496                );
3497    
3498                Type elementType = isGetter ? callableMethod.getReturnType() : ArrayUtil.getLastElement(argumentTypes);
3499                return StackValue.collectionElement(collectionElementReceiver, elementType, resolvedGetCall, resolvedSetCall, this);
3500            }
3501        }
3502    
3503        @NotNull
3504        private StackValue.CollectionElementReceiver createCollectionElementReceiver(
3505                @NotNull KtArrayAccessExpression expression,
3506                @NotNull StackValue receiver,
3507                @NotNull FunctionDescriptor operationDescriptor,
3508                boolean isGetter,
3509                ResolvedCall<FunctionDescriptor> resolvedGetCall,
3510                ResolvedCall<FunctionDescriptor> resolvedSetCall,
3511                @NotNull Callable callable
3512        ) {
3513            ResolvedCall<FunctionDescriptor> resolvedCall = isGetter ? resolvedGetCall : resolvedSetCall;
3514            assert resolvedCall != null : "couldn't find resolved call: " + expression.getText();
3515    
3516            List<ResolvedValueArgument> valueArguments = resolvedCall.getValueArgumentsByIndex();
3517            assert valueArguments != null : "Failed to arrange value arguments by index: " + operationDescriptor;
3518    
3519            if (!isGetter) {
3520                assert valueArguments.size() >= 2 : "Setter call should have at least 2 arguments: " + operationDescriptor;
3521                // Skip generation of the right hand side of an indexed assignment, which is the last value argument
3522                valueArguments.remove(valueArguments.size() - 1);
3523            }
3524    
3525            return new StackValue.CollectionElementReceiver(
3526                    callable, receiver, resolvedGetCall, resolvedSetCall, isGetter, this, valueArguments
3527            );
3528        }
3529    
3530        @Override
3531        public StackValue visitThrowExpression(@NotNull final KtThrowExpression expression, StackValue receiver) {
3532            return StackValue.operation(Type.VOID_TYPE, new Function1<InstructionAdapter, Unit>() {
3533                @Override
3534                public Unit invoke(InstructionAdapter adapter) {
3535                    gen(expression.getThrownExpression(), JAVA_THROWABLE_TYPE);
3536                    v.athrow();
3537                    return Unit.INSTANCE;
3538                }
3539            });
3540        }
3541    
3542        @Override
3543        public StackValue visitThisExpression(@NotNull KtThisExpression expression, StackValue receiver) {
3544            DeclarationDescriptor descriptor = bindingContext.get(REFERENCE_TARGET, expression.getInstanceReference());
3545            if (descriptor instanceof ClassDescriptor) {
3546                //TODO rewrite with context.lookupInContext()
3547                return StackValue.thisOrOuter(this, (ClassDescriptor) descriptor, false, true);
3548            }
3549            if (descriptor instanceof CallableDescriptor) {
3550                return generateReceiver((CallableDescriptor) descriptor);
3551            }
3552            throw new UnsupportedOperationException("Neither this nor receiver: " + descriptor);
3553        }
3554    
3555        @Override
3556        public StackValue visitTryExpression(@NotNull KtTryExpression expression, StackValue receiver) {
3557            return generateTryExpression(expression, false);
3558        }
3559    
3560        public StackValue generateTryExpression(final KtTryExpression expression, final boolean isStatement) {
3561            /*
3562    The "returned" value of try expression with no finally is either the last expression in the try block or the last expression in the catch block
3563    (or blocks).
3564             */
3565    
3566            KotlinType jetType = bindingContext.getType(expression);
3567            assert jetType != null;
3568            final Type expectedAsmType = isStatement ? Type.VOID_TYPE : asmType(jetType);
3569    
3570            return StackValue.operation(expectedAsmType, new Function1<InstructionAdapter, Unit>() {
3571                @Override
3572                public Unit invoke(InstructionAdapter v) {
3573                    KtFinallySection finallyBlock = expression.getFinallyBlock();
3574                    FinallyBlockStackElement finallyBlockStackElement = null;
3575                    if (finallyBlock != null) {
3576                        finallyBlockStackElement = new FinallyBlockStackElement(expression);
3577                        blockStackElements.push(finallyBlockStackElement);
3578                    }
3579    
3580                    //PseudoInsnsPackage.saveStackBeforeTryExpr(v);
3581    
3582                    Label tryStart = new Label();
3583                    v.mark(tryStart);
3584                    v.nop(); // prevent verify error on empty try
3585    
3586                    gen(expression.getTryBlock(), expectedAsmType);
3587    
3588                    int savedValue = -1;
3589                    if (!isStatement) {
3590                        savedValue = myFrameMap.enterTemp(expectedAsmType);
3591                        v.store(savedValue, expectedAsmType);
3592                    }
3593    
3594                    Label tryEnd = new Label();
3595                    v.mark(tryEnd);
3596    
3597                    //do it before finally block generation
3598                    List<Label> tryBlockRegions = getCurrentCatchIntervals(finallyBlockStackElement, tryStart, tryEnd);
3599    
3600                    Label end = new Label();
3601    
3602                    genFinallyBlockOrGoto(finallyBlockStackElement, end, null);
3603    
3604                    List<KtCatchClause> clauses = expression.getCatchClauses();
3605                    for (int i = 0, size = clauses.size(); i < size; i++) {
3606                        KtCatchClause clause = clauses.get(i);
3607    
3608                        Label clauseStart = new Label();
3609                        v.mark(clauseStart);
3610    
3611                        VariableDescriptor descriptor = bindingContext.get(VALUE_PARAMETER, clause.getCatchParameter());
3612                        assert descriptor != null;
3613                        Type descriptorType = asmType(descriptor.getType());
3614                        myFrameMap.enter(descriptor, descriptorType);
3615                        int index = lookupLocalIndex(descriptor);
3616                        v.store(index, descriptorType);
3617    
3618                        gen(clause.getCatchBody(), expectedAsmType);
3619    
3620                        if (!isStatement) {
3621                            v.store(savedValue, expectedAsmType);
3622                        }
3623    
3624                        myFrameMap.leave(descriptor);
3625    
3626                        Label clauseEnd = new Label();
3627                        v.mark(clauseEnd);
3628    
3629                        v.visitLocalVariable(descriptor.getName().asString(), descriptorType.getDescriptor(), null, clauseStart, clauseEnd,
3630                                             index);
3631    
3632                        genFinallyBlockOrGoto(finallyBlockStackElement, i != size - 1 || finallyBlock != null ? end : null, null);
3633    
3634                        generateExceptionTable(clauseStart, tryBlockRegions, descriptorType.getInternalName());
3635                    }
3636    
3637    
3638                    //for default catch clause
3639                    if (finallyBlock != null) {
3640                        Label defaultCatchStart = new Label();
3641                        v.mark(defaultCatchStart);
3642                        int savedException = myFrameMap.enterTemp(JAVA_THROWABLE_TYPE);
3643                        v.store(savedException, JAVA_THROWABLE_TYPE);
3644    
3645                        Label defaultCatchEnd = new Label();
3646                        v.mark(defaultCatchEnd);
3647    
3648                        //do it before finally block generation
3649                        //javac also generates entry in exception table for default catch clause too!!!! so defaultCatchEnd as end parameter
3650                        List<Label> defaultCatchRegions = getCurrentCatchIntervals(finallyBlockStackElement, tryStart, defaultCatchEnd);
3651    
3652    
3653                        genFinallyBlockOrGoto(finallyBlockStackElement, null, null);
3654    
3655                        v.load(savedException, JAVA_THROWABLE_TYPE);
3656                        myFrameMap.leaveTemp(JAVA_THROWABLE_TYPE);
3657    
3658                        v.athrow();
3659    
3660                        generateExceptionTable(defaultCatchStart, defaultCatchRegions, null);
3661                    }
3662    
3663                    markLineNumber(expression, isStatement);
3664                    v.mark(end);
3665    
3666                    if (!isStatement) {
3667                        v.load(savedValue, expectedAsmType);
3668                        myFrameMap.leaveTemp(expectedAsmType);
3669                    }
3670    
3671                    if (finallyBlock != null) {
3672                        blockStackElements.pop();
3673                    }
3674                    return Unit.INSTANCE;
3675                }
3676            });
3677        }
3678    
3679        private void generateExceptionTable(@NotNull Label catchStart, @NotNull List<Label> catchedRegions, @Nullable String exception) {
3680            for (int i = 0; i < catchedRegions.size(); i += 2) {
3681                Label startRegion = catchedRegions.get(i);
3682                Label endRegion = catchedRegions.get(i+1);
3683                v.visitTryCatchBlock(startRegion, endRegion, catchStart, exception);
3684            }
3685        }
3686    
3687        @NotNull
3688        private static List<Label> getCurrentCatchIntervals(
3689                @Nullable FinallyBlockStackElement finallyBlockStackElement,
3690                @NotNull Label blockStart,
3691                @NotNull Label blockEnd
3692        ) {
3693            List<Label> gapsInBlock =
3694                    finallyBlockStackElement != null ? new ArrayList<Label>(finallyBlockStackElement.gaps) : Collections.<Label>emptyList();
3695            assert gapsInBlock.size() % 2 == 0;
3696            List<Label> blockRegions = new ArrayList<Label>(gapsInBlock.size() + 2);
3697            blockRegions.add(blockStart);
3698            blockRegions.addAll(gapsInBlock);
3699            blockRegions.add(blockEnd);
3700            return blockRegions;
3701        }
3702    
3703        @Override
3704        public StackValue visitBinaryWithTypeRHSExpression(@NotNull KtBinaryExpressionWithTypeRHS expression, StackValue receiver) {
3705            KtExpression left = expression.getLeft();
3706            final IElementType opToken = expression.getOperationReference().getReferencedNameElementType();
3707    
3708            final KotlinType rightType = bindingContext.get(TYPE, expression.getRight());
3709            assert rightType != null;
3710    
3711            final StackValue value = genQualified(receiver, left);
3712    
3713            return StackValue.operation(boxType(asmType(rightType)), new Function1<InstructionAdapter, Unit>() {
3714                @Override
3715                public Unit invoke(InstructionAdapter v) {
3716                    value.put(boxType(value.type), v);
3717    
3718                    if (value.type == Type.VOID_TYPE) {
3719                        StackValue.putUnitInstance(v);
3720                    }
3721    
3722                    boolean safeAs = opToken == KtTokens.AS_SAFE;
3723                    Type type = boxType(asmType(rightType));
3724                    if (TypeUtils.isReifiedTypeParameter(rightType)) {
3725                        putReifiedOperationMarkerIfTypeIsReifiedParameter(rightType,
3726                                                                          safeAs ? ReifiedTypeInliner.OperationKind.SAFE_AS
3727                                                                                 : ReifiedTypeInliner.OperationKind.AS);
3728                        v.checkcast(type);
3729                        return Unit.INSTANCE;
3730                    }
3731    
3732                    CodegenUtilKt.generateAsCast(v, rightType, type, safeAs);
3733    
3734                    return Unit.INSTANCE;
3735                }
3736            });
3737        }
3738    
3739        @Override
3740        public StackValue visitIsExpression(@NotNull KtIsExpression expression, StackValue receiver) {
3741            StackValue match = StackValue.expression(OBJECT_TYPE, expression.getLeftHandSide(), this);
3742            return generateIsCheck(match, expression.getTypeReference(), expression.isNegated());
3743        }
3744    
3745        private StackValue generateExpressionMatch(StackValue expressionToMatch, KtExpression patternExpression) {
3746            if (expressionToMatch != null) {
3747                Type subjectType = expressionToMatch.type;
3748                markStartLineNumber(patternExpression);
3749                KotlinType condJetType = bindingContext.getType(patternExpression);
3750                Type condType;
3751                if (isNumberPrimitive(subjectType) || subjectType.getSort() == Type.BOOLEAN) {
3752                    assert condJetType != null;
3753                    condType = asmType(condJetType);
3754                    if (!(isNumberPrimitive(condType) || condType.getSort() == Type.BOOLEAN)) {
3755                        subjectType = boxType(subjectType);
3756                    }
3757                }
3758                else {
3759                    condType = OBJECT_TYPE;
3760                }
3761                StackValue condition = genLazy(patternExpression, condType);
3762                return genEqualsForExpressionsOnStack(KtTokens.EQEQ, StackValue.coercion(expressionToMatch, subjectType), condition);
3763            }
3764            else {
3765                return gen(patternExpression);
3766            }
3767        }
3768    
3769        private StackValue generateIsCheck(StackValue expressionToMatch, KtTypeReference typeReference, boolean negated) {
3770            KotlinType jetType = bindingContext.get(TYPE, typeReference);
3771            markStartLineNumber(typeReference);
3772            StackValue value = generateIsCheck(expressionToMatch, jetType, false);
3773            return negated ? StackValue.not(value) : value;
3774        }
3775    
3776        private StackValue generateIsCheck(final StackValue expressionToGen, final KotlinType kotlinType, final boolean leaveExpressionOnStack) {
3777            return StackValue.operation(Type.BOOLEAN_TYPE, new Function1<InstructionAdapter, Unit>() {
3778                @Override
3779                public Unit invoke(InstructionAdapter v) {
3780                    expressionToGen.put(OBJECT_TYPE, v);
3781                    if (leaveExpressionOnStack) {
3782                        v.dup();
3783                    }
3784    
3785                    Type type = boxType(asmType(kotlinType));
3786                    if (TypeUtils.isReifiedTypeParameter(kotlinType)) {
3787                        putReifiedOperationMarkerIfTypeIsReifiedParameter(kotlinType, ReifiedTypeInliner.OperationKind.IS);
3788                        v.instanceOf(type);
3789                        return null;
3790                    }
3791    
3792                    CodegenUtilKt.generateIsCheck(v, kotlinType, type);
3793                    return null;
3794                }
3795            });
3796        }
3797    
3798        public void putReifiedOperationMarkerIfTypeIsReifiedParameter(
3799                @NotNull KotlinType type, @NotNull ReifiedTypeInliner.OperationKind operationKind
3800        ) {
3801            Pair<TypeParameterDescriptor, ReificationArgument> typeParameterAndReificationArgument = extractReificationArgument(type);
3802            if (typeParameterAndReificationArgument != null && typeParameterAndReificationArgument.getFirst().isReified()) {
3803                TypeParameterDescriptor typeParameterDescriptor = typeParameterAndReificationArgument.getFirst();
3804                if (typeParameterDescriptor.getContainingDeclaration() != context.getContextDescriptor()) {
3805                    parentCodegen.getReifiedTypeParametersUsages().
3806                            addUsedReifiedParameter(typeParameterDescriptor.getName().asString());
3807                }
3808                v.iconst(operationKind.getId());
3809                v.visitLdcInsn(typeParameterAndReificationArgument.getSecond().asString());
3810                v.invokestatic(
3811                        IntrinsicMethods.INTRINSICS_CLASS_NAME, ReifiedTypeInliner.REIFIED_OPERATION_MARKER_METHOD_NAME,
3812                        Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE, Type.getType(String.class)), false
3813                );
3814            }
3815        }
3816    
3817        public void propagateChildReifiedTypeParametersUsages(@NotNull ReifiedTypeParametersUsages usages) {
3818            parentCodegen.getReifiedTypeParametersUsages().propagateChildUsagesWithinContext(usages, context);
3819        }
3820    
3821        @Override
3822        public StackValue visitWhenExpression(@NotNull KtWhenExpression expression, StackValue receiver) {
3823            return generateWhenExpression(expression, false);
3824        }
3825    
3826        public StackValue generateWhenExpression(final KtWhenExpression expression, final boolean isStatement) {
3827            final KtExpression expr = expression.getSubjectExpression();
3828            final Type subjectType = expressionType(expr);
3829    
3830            final Type resultType = isStatement ? Type.VOID_TYPE : expressionType(expression);
3831    
3832            return StackValue.operation(resultType, new Function1<InstructionAdapter, Unit>() {
3833                @Override
3834                public Unit invoke(InstructionAdapter v) {
3835                    SwitchCodegen switchCodegen = SwitchCodegenUtil.buildAppropriateSwitchCodegenIfPossible(
3836                            expression, isStatement, isExhaustive(expression, isStatement), ExpressionCodegen.this
3837                    );
3838                    if (switchCodegen != null) {
3839                        switchCodegen.generate();
3840                        return Unit.INSTANCE;
3841                    }
3842    
3843                    int subjectLocal = expr != null ? myFrameMap.enterTemp(subjectType) : -1;
3844                    if (subjectLocal != -1) {
3845                        gen(expr, subjectType);
3846                        tempVariables.put(expr, StackValue.local(subjectLocal, subjectType));
3847                        v.store(subjectLocal, subjectType);
3848                    }
3849    
3850                    Label end = new Label();
3851                    boolean hasElse = KtPsiUtil.checkWhenExpressionHasSingleElse(expression);
3852    
3853                    Label nextCondition = null;
3854                    for (KtWhenEntry whenEntry : expression.getEntries()) {
3855                        if (nextCondition != null) {
3856                            v.mark(nextCondition);
3857                        }
3858                        nextCondition = new Label();
3859                        FrameMap.Mark mark = myFrameMap.mark();
3860                        Label thisEntry = new Label();
3861                        if (!whenEntry.isElse()) {
3862                            KtWhenCondition[] conditions = whenEntry.getConditions();
3863                            for (int i = 0; i < conditions.length; i++) {
3864                                StackValue conditionValue = generateWhenCondition(subjectType, subjectLocal, conditions[i]);
3865                                BranchedValue.Companion.condJump(conditionValue, nextCondition, true, v);
3866                                if (i < conditions.length - 1) {
3867                                    v.goTo(thisEntry);
3868                                    v.mark(nextCondition);
3869                                    nextCondition = new Label();
3870                                }
3871                            }
3872                        }
3873    
3874                        v.visitLabel(thisEntry);
3875                        gen(whenEntry.getExpression(), resultType);
3876                        mark.dropTo();
3877                        if (!whenEntry.isElse()) {
3878                            v.goTo(end);
3879                        }
3880                    }
3881                    if (!hasElse && nextCondition != null) {
3882                        v.mark(nextCondition);
3883                        putUnitInstanceOntoStackForNonExhaustiveWhen(expression, isStatement);
3884                    }
3885    
3886                    markLineNumber(expression, isStatement);
3887                    v.mark(end);
3888    
3889                    myFrameMap.leaveTemp(subjectType);
3890                    tempVariables.remove(expr);
3891                    return null;
3892                }
3893            });
3894        }
3895    
3896        private boolean isExhaustive(@NotNull KtWhenExpression whenExpression, boolean isStatement) {
3897            if (isStatement) {
3898                return Boolean.TRUE.equals(bindingContext.get(BindingContext.IMPLICIT_EXHAUSTIVE_WHEN, whenExpression));
3899            }
3900            else {
3901                return Boolean.TRUE.equals(bindingContext.get(BindingContext.EXHAUSTIVE_WHEN, whenExpression));
3902            }
3903        }
3904    
3905        public void putUnitInstanceOntoStackForNonExhaustiveWhen(
3906                @NotNull KtWhenExpression whenExpression,
3907                boolean isStatement
3908        ) {
3909            if (isExhaustive(whenExpression, isStatement)) {
3910                // when() is supposed to be exhaustive
3911                genThrow(v, "kotlin/NoWhenBranchMatchedException", null);
3912            }
3913            else if (!isStatement) {
3914                // non-exhaustive when() with no else -> Unit must be expected
3915                StackValue.putUnitInstance(v);
3916            }
3917        }
3918    
3919        private StackValue generateWhenCondition(Type subjectType, int subjectLocal, KtWhenCondition condition) {
3920            if (condition instanceof KtWhenConditionInRange) {
3921                KtWhenConditionInRange conditionInRange = (KtWhenConditionInRange) condition;
3922                return generateIn(StackValue.local(subjectLocal, subjectType),
3923                                  conditionInRange.getRangeExpression(),
3924                                  conditionInRange.getOperationReference());
3925            }
3926            StackValue.Local match = subjectLocal == -1 ? null : StackValue.local(subjectLocal, subjectType);
3927            if (condition instanceof KtWhenConditionIsPattern) {
3928                KtWhenConditionIsPattern patternCondition = (KtWhenConditionIsPattern) condition;
3929                return generateIsCheck(match, patternCondition.getTypeReference(), patternCondition.isNegated());
3930            }
3931            else if (condition instanceof KtWhenConditionWithExpression) {
3932                KtExpression patternExpression = ((KtWhenConditionWithExpression) condition).getExpression();
3933                return generateExpressionMatch(match, patternExpression);
3934            }
3935            else {
3936                throw new UnsupportedOperationException("unsupported kind of when condition");
3937            }
3938        }
3939    
3940        private boolean isIntRangeExpr(KtExpression rangeExpression) {
3941            if (rangeExpression instanceof KtBinaryExpression) {
3942                KtBinaryExpression binaryExpression = (KtBinaryExpression) rangeExpression;
3943                if (binaryExpression.getOperationReference().getReferencedNameElementType() == KtTokens.RANGE) {
3944                    KotlinType jetType = bindingContext.getType(rangeExpression);
3945                    assert jetType != null;
3946                    DeclarationDescriptor descriptor = jetType.getConstructor().getDeclarationDescriptor();
3947                    return DescriptorUtilsKt.getBuiltIns(descriptor).getIntegralRanges().contains(descriptor);
3948                }
3949            }
3950            return false;
3951        }
3952    
3953        private Call makeFakeCall(ReceiverValue initializerAsReceiver) {
3954            KtSimpleNameExpression fake = KtPsiFactoryKt.KtPsiFactory(state.getProject()).createSimpleName("fake");
3955            return CallMaker.makeCall(fake, initializerAsReceiver);
3956        }
3957    
3958        @Override
3959        public String toString() {
3960            return context.getContextDescriptor().toString();
3961        }
3962    
3963        @NotNull
3964        public FrameMap getFrameMap() {
3965            return myFrameMap;
3966        }
3967    
3968        @NotNull
3969        public MethodContext getContext() {
3970            return context;
3971        }
3972    
3973        @NotNull
3974        public NameGenerator getInlineNameGenerator() {
3975            NameGenerator nameGenerator = getParentCodegen().getInlineNameGenerator();
3976            Name name = context.getContextDescriptor().getName();
3977            return nameGenerator.subGenerator((name.isSpecial() ? "$special" : name.asString()) + "$$inlined" );
3978        }
3979    
3980        public Type getReturnType() {
3981            return returnType;
3982        }
3983    
3984        public Stack<BlockStackElement> getBlockStackElements() {
3985            return new Stack<BlockStackElement>(blockStackElements);
3986        }
3987    
3988        public void addBlockStackElementsForNonLocalReturns(@NotNull Stack<BlockStackElement> elements, int finallyDepth) {
3989            blockStackElements.addAll(elements);
3990            this.finallyDepth = finallyDepth;
3991        }
3992    
3993        private static class NonLocalReturnInfo {
3994    
3995            final Type returnType;
3996    
3997            final String labelName;
3998    
3999            private NonLocalReturnInfo(Type type, String name) {
4000                returnType = type;
4001                labelName = name;
4002            }
4003        }
4004    }