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