001    /*
002     * Copyright 2010-2013 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.jet.codegen;
018    
019    import com.intellij.psi.tree.IElementType;
020    import org.jetbrains.annotations.Contract;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.jet.codegen.intrinsics.IntrinsicMethod;
024    import org.jetbrains.jet.codegen.state.GenerationState;
025    import org.jetbrains.jet.codegen.state.JetTypeMapper;
026    import org.jetbrains.jet.lang.descriptors.*;
027    import org.jetbrains.jet.lang.psi.JetExpression;
028    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
029    import org.jetbrains.jet.lang.resolve.java.JvmAbi;
030    import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
031    import org.jetbrains.jet.lexer.JetTokens;
032    import org.jetbrains.org.objectweb.asm.Label;
033    import org.jetbrains.org.objectweb.asm.Type;
034    import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
035    import org.jetbrains.org.objectweb.asm.commons.Method;
036    
037    import java.util.List;
038    
039    import static org.jetbrains.jet.codegen.AsmUtil.*;
040    import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.*;
041    import static org.jetbrains.org.objectweb.asm.Opcodes.*;
042    
043    public abstract class StackValue {
044    
045        private static final String NULLABLE_BYTE_TYPE_NAME = "java/lang/Byte";
046        private static final String NULLABLE_SHORT_TYPE_NAME = "java/lang/Short";
047        private static final String NULLABLE_LONG_TYPE_NAME = "java/lang/Long";
048    
049        @NotNull
050        public final Type type;
051    
052        protected StackValue(@NotNull Type type) {
053            this.type = type;
054        }
055    
056        /**
057         * Put this value to the top of the stack.
058         */
059        public abstract void put(Type type, InstructionAdapter v);
060    
061        /**
062         * This method is called to put the value on the top of the JVM stack if <code>depth</code> other values have been put on the
063         * JVM stack after this value was generated.
064         *
065         * @param type  the type as which the value should be put
066         * @param v     the visitor used to genClassOrObject the instructions
067         * @param depth the number of new values put onto the stack
068         */
069        protected void moveToTopOfStack(Type type, InstructionAdapter v, int depth) {
070            put(type, v);
071        }
072    
073        /**
074         * Set this value from the top of the stack.
075         */
076        public void store(Type topOfStackType, InstructionAdapter v) {
077            throw new UnsupportedOperationException("cannot store to value " + this);
078        }
079    
080        public void dupReceiver(InstructionAdapter v) {
081        }
082    
083        public int receiverSize() {
084            return 0;
085        }
086    
087        public void condJump(Label label, boolean jumpIfFalse, InstructionAdapter v) {
088            put(this.type, v);
089            coerceTo(Type.BOOLEAN_TYPE, v);
090            if (jumpIfFalse) {
091                v.ifeq(label);
092            }
093            else {
094                v.ifne(label);
095            }
096        }
097    
098        public static Local local(int index, Type type) {
099            return new Local(index, type);
100        }
101    
102        public static StackValue shared(int index, Type type) {
103            return new Shared(index, type);
104        }
105    
106        public static StackValue onStack(Type type) {
107            return type == Type.VOID_TYPE ? none() : new OnStack(type);
108        }
109    
110        public static StackValue constant(@Nullable Object value, Type type) {
111            return new Constant(value, type);
112        }
113    
114        public static StackValue cmp(IElementType opToken, Type type) {
115            return type.getSort() == Type.OBJECT ? new ObjectCompare(opToken, type) : new NumberCompare(opToken, type);
116        }
117    
118        public static StackValue not(StackValue stackValue) {
119            return new Invert(stackValue);
120        }
121    
122        @NotNull
123        public static StackValue arrayElement(Type type) {
124            return new ArrayElement(type);
125        }
126    
127        @NotNull
128        public static StackValue collectionElement(
129                Type type,
130                ResolvedCall<FunctionDescriptor> getter,
131                ResolvedCall<FunctionDescriptor> setter,
132                ExpressionCodegen codegen,
133                GenerationState state
134        ) {
135            return new CollectionElement(type, getter, setter, codegen, state);
136        }
137    
138        @NotNull
139        public static Field field(@NotNull Type type, @NotNull Type owner, @NotNull String name, boolean isStatic) {
140            return new Field(type, owner, name, isStatic);
141        }
142    
143        @NotNull
144        public static Property property(
145                @NotNull PropertyDescriptor descriptor,
146                @NotNull Type methodOwner,
147                @NotNull Type type,
148                boolean isStatic,
149                @Nullable String fieldName,
150                @Nullable CallableMethod getter,
151                @Nullable CallableMethod setter,
152                GenerationState state
153        ) {
154            return new Property(descriptor, methodOwner, getter, setter, isStatic, fieldName, type, state);
155        }
156    
157        @NotNull
158        public static StackValue expression(Type type, JetExpression expression, ExpressionCodegen generator) {
159            return new Expression(type, expression, generator);
160        }
161    
162        private static void box(Type type, Type toType, InstructionAdapter v) {
163            if (type == Type.BYTE_TYPE || toType.getInternalName().equals(NULLABLE_BYTE_TYPE_NAME) && type == Type.INT_TYPE) {
164                v.cast(type, Type.BYTE_TYPE);
165                v.invokestatic(NULLABLE_BYTE_TYPE_NAME, "valueOf", "(B)L" + NULLABLE_BYTE_TYPE_NAME + ";", false);
166            }
167            else if (type == Type.SHORT_TYPE || toType.getInternalName().equals(NULLABLE_SHORT_TYPE_NAME) && type == Type.INT_TYPE) {
168                v.cast(type, Type.SHORT_TYPE);
169                v.invokestatic(NULLABLE_SHORT_TYPE_NAME, "valueOf", "(S)L" + NULLABLE_SHORT_TYPE_NAME + ";", false);
170            }
171            else if (type == Type.LONG_TYPE || toType.getInternalName().equals(NULLABLE_LONG_TYPE_NAME) && type == Type.INT_TYPE) {
172                v.cast(type, Type.LONG_TYPE);
173                v.invokestatic(NULLABLE_LONG_TYPE_NAME, "valueOf", "(J)L" + NULLABLE_LONG_TYPE_NAME + ";", false);
174            }
175            else if (type == Type.INT_TYPE) {
176                v.invokestatic("java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
177            }
178            else if (type == Type.BOOLEAN_TYPE) {
179                v.invokestatic("java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
180            }
181            else if (type == Type.CHAR_TYPE) {
182                v.invokestatic("java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false);
183            }
184            else if (type == Type.FLOAT_TYPE) {
185                v.invokestatic("java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false);
186            }
187            else if (type == Type.DOUBLE_TYPE) {
188                v.invokestatic("java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false);
189            }
190        }
191    
192        private static void unbox(Type type, InstructionAdapter v) {
193            if (type == Type.INT_TYPE) {
194                v.invokevirtual("java/lang/Number", "intValue", "()I", false);
195            }
196            else if (type == Type.BOOLEAN_TYPE) {
197                v.invokevirtual("java/lang/Boolean", "booleanValue", "()Z", false);
198            }
199            else if (type == Type.CHAR_TYPE) {
200                v.invokevirtual("java/lang/Character", "charValue", "()C", false);
201            }
202            else if (type == Type.SHORT_TYPE) {
203                v.invokevirtual("java/lang/Number", "shortValue", "()S", false);
204            }
205            else if (type == Type.LONG_TYPE) {
206                v.invokevirtual("java/lang/Number", "longValue", "()J", false);
207            }
208            else if (type == Type.BYTE_TYPE) {
209                v.invokevirtual("java/lang/Number", "byteValue", "()B", false);
210            }
211            else if (type == Type.FLOAT_TYPE) {
212                v.invokevirtual("java/lang/Number", "floatValue", "()F", false);
213            }
214            else if (type == Type.DOUBLE_TYPE) {
215                v.invokevirtual("java/lang/Number", "doubleValue", "()D", false);
216            }
217        }
218    
219        protected void coerceTo(Type toType, InstructionAdapter v) {
220            coerce(this.type, toType, v);
221        }
222    
223        protected void coerceFrom(Type topOfStackType, InstructionAdapter v) {
224            coerce(topOfStackType, this.type, v);
225        }
226    
227        public static void coerce(Type fromType, Type toType, InstructionAdapter v) {
228            if (toType.equals(fromType)) return;
229    
230            if (toType.getSort() == Type.VOID) {
231                pop(v, fromType);
232            }
233            else if (fromType.getSort() == Type.VOID) {
234                if (toType.equals(UNIT_TYPE) || toType.equals(OBJECT_TYPE)) {
235                    putUnitInstance(v);
236                }
237                else if (toType.getSort() == Type.OBJECT || toType.getSort() == Type.ARRAY) {
238                    v.aconst(null);
239                }
240                else {
241                    pushDefaultPrimitiveValueOnStack(toType, v);
242                }
243            }
244            else if (toType.equals(UNIT_TYPE)) {
245                if (fromType.equals(getType(Object.class))) {
246                    v.checkcast(UNIT_TYPE);
247                }
248                else if (!fromType.equals(getType(Void.class))) {
249                    pop(v, fromType);
250                    putUnitInstance(v);
251                }
252            }
253            else if (toType.getSort() == Type.ARRAY) {
254                v.checkcast(toType);
255            }
256            else if (toType.getSort() == Type.OBJECT) {
257                if (fromType.getSort() == Type.OBJECT || fromType.getSort() == Type.ARRAY) {
258                    if (!toType.equals(OBJECT_TYPE)) {
259                        v.checkcast(toType);
260                    }
261                }
262                else {
263                    box(fromType, toType, v);
264                }
265            }
266            else if (fromType.getSort() == Type.OBJECT) {
267                if (fromType.equals(getType(Boolean.class)) || fromType.equals(getType(Character.class))) {
268                    unbox(unboxType(fromType), v);
269                    coerce(unboxType(fromType), toType, v);
270                }
271                else {
272                    if (toType.getSort() == Type.BOOLEAN || toType.getSort() == Type.CHAR) {
273                        coerce(fromType, boxType(toType), v);
274                    }
275                    else {
276                        coerce(fromType, getType(Number.class), v);
277                    }
278                    unbox(toType, v);
279                }
280            }
281            else {
282                v.cast(fromType, toType);
283            }
284        }
285    
286        public static void putUnitInstance(InstructionAdapter v) {
287            v.visitFieldInsn(GETSTATIC, UNIT_TYPE.getInternalName(), JvmAbi.INSTANCE_FIELD, UNIT_TYPE.getDescriptor());
288        }
289    
290        protected void putAsBoolean(InstructionAdapter v) {
291            Label ifTrue = new Label();
292            Label end = new Label();
293            condJump(ifTrue, false, v);
294            v.iconst(0);
295            v.goTo(end);
296            v.mark(ifTrue);
297            v.iconst(1);
298            v.mark(end);
299        }
300    
301        public static StackValue none() {
302            return None.INSTANCE;
303        }
304    
305        public static StackValue fieldForSharedVar(Type localType, Type classType, String fieldName) {
306            return new FieldForSharedVar(localType, classType, fieldName);
307        }
308    
309        public static StackValue composed(StackValue prefix, StackValue suffix) {
310            return new Composed(prefix, suffix);
311        }
312    
313        public static StackValue thisOrOuter(ExpressionCodegen codegen, ClassDescriptor descriptor, boolean isSuper, boolean isExplicit) {
314            // Coerce this/super for traits to support traits with required classes.
315            // Coerce explicit 'this' for the case when it is smartcasted.
316            // Do not coerce for other classes due to the 'protected' access issues (JVMS 7, 4.9.2 Structural Constraints).
317            boolean coerceType = descriptor.getKind() == ClassKind.TRAIT || (isExplicit && !isSuper);
318            return new ThisOuter(codegen, descriptor, isSuper, coerceType);
319        }
320    
321        public static StackValue postIncrement(int index, int increment) {
322            return new PostIncrement(index, increment);
323        }
324    
325        public static StackValue preIncrement(int index, int increment) {
326            return new PreIncrement(index, increment);
327        }
328    
329        public static StackValue receiver(
330                ResolvedCall<?> resolvedCall,
331                StackValue receiver,
332                ExpressionCodegen codegen,
333                @Nullable CallableMethod callableMethod
334        ) {
335            if (resolvedCall.getThisObject().exists() || resolvedCall.getReceiverArgument().exists() || isLocalFunCall(callableMethod)) {
336                return new CallReceiver(resolvedCall, receiver, codegen, callableMethod, true);
337            }
338            return receiver;
339        }
340    
341        @Contract("null -> false")
342        private static boolean isLocalFunCall(@Nullable CallableMethod callableMethod) {
343            return callableMethod != null && callableMethod.getGenerateCalleeType() != null;
344        }
345    
346        public static StackValue receiverWithoutReceiverArgument(StackValue receiverWithParameter) {
347            if (receiverWithParameter instanceof CallReceiver) {
348                CallReceiver callReceiver = (CallReceiver) receiverWithParameter;
349                return new CallReceiver(callReceiver.resolvedCall, callReceiver.receiver,
350                                        callReceiver.codegen, callReceiver.callableMethod, false);
351            }
352            return receiverWithParameter;
353        }
354    
355        public static Field singleton(ClassDescriptor classDescriptor, JetTypeMapper typeMapper) {
356            FieldInfo info = FieldInfo.createForSingleton(classDescriptor, typeMapper);
357            return field(info.getFieldType(), Type.getObjectType(info.getOwnerInternalName()), info.getFieldName(), true);
358        }
359    
360        private static class None extends StackValue {
361            public static final None INSTANCE = new None();
362    
363            private None() {
364                super(Type.VOID_TYPE);
365            }
366    
367            @Override
368            public void put(Type type, InstructionAdapter v) {
369                coerceTo(type, v);
370            }
371        }
372    
373        public static class Local extends StackValue {
374            public final int index;
375    
376            private Local(int index, Type type) {
377                super(type);
378                this.index = index;
379    
380                if (index < 0) {
381                    throw new IllegalStateException("local variable index must be non-negative");
382                }
383            }
384    
385            @Override
386            public void put(Type type, InstructionAdapter v) {
387                v.load(index, this.type);
388                coerceTo(type, v);
389                // TODO unbox
390            }
391    
392            @Override
393            public void store(Type topOfStackType, InstructionAdapter v) {
394                coerceFrom(topOfStackType, v);
395                v.store(index, this.type);
396            }
397        }
398    
399        public static class OnStack extends StackValue {
400            public OnStack(Type type) {
401                super(type);
402            }
403    
404            @Override
405            public void put(Type type, InstructionAdapter v) {
406                coerceTo(type, v);
407            }
408    
409            @Override
410            public void moveToTopOfStack(Type type, InstructionAdapter v, int depth) {
411                if (depth == 0) {
412                    put(type, v);
413                }
414                else if (depth == 1) {
415                    if (type.getSize() != 1) {
416                        throw new UnsupportedOperationException("don't know how to move type " + type + " to top of stack");
417                    }
418                    v.swap();
419                }
420                else {
421                    throw new UnsupportedOperationException("unsupported move-to-top depth " + depth);
422                }
423            }
424        }
425    
426        public static class Constant extends StackValue {
427            @Nullable
428            private final Object value;
429    
430            public Constant(@Nullable Object value, Type type) {
431                super(type);
432                this.value = value;
433            }
434    
435            @Override
436            public void put(Type type, InstructionAdapter v) {
437                if (value instanceof Integer) {
438                    v.iconst((Integer) value);
439                }
440                else if (value instanceof Long) {
441                    v.lconst((Long) value);
442                }
443                else if (value instanceof Float) {
444                    v.fconst((Float) value);
445                }
446                else if (value instanceof Double) {
447                    v.dconst((Double) value);
448                }
449                else {
450                    v.aconst(value);
451                }
452    
453                coerceTo(type, v);
454            }
455    
456            @Override
457            public void condJump(Label label, boolean jumpIfFalse, InstructionAdapter v) {
458                if (value instanceof Boolean) {
459                    boolean boolValue = (Boolean) value;
460                    if (boolValue ^ jumpIfFalse) {
461                        v.goTo(label);
462                    }
463                }
464                else {
465                    throw new UnsupportedOperationException("don't know how to generate this condjump");
466                }
467            }
468        }
469    
470        private static class NumberCompare extends StackValue {
471            protected final IElementType opToken;
472            private final Type operandType;
473    
474            public NumberCompare(IElementType opToken, Type operandType) {
475                super(Type.BOOLEAN_TYPE);
476                this.opToken = opToken;
477                this.operandType = operandType;
478            }
479    
480            @Override
481            public void put(Type type, InstructionAdapter v) {
482                putAsBoolean(v);
483                coerceTo(type, v);
484            }
485    
486            @Override
487            public void condJump(Label label, boolean jumpIfFalse, InstructionAdapter v) {
488                int opcode;
489                if (opToken == JetTokens.EQEQ) {
490                    opcode = jumpIfFalse ? IFNE : IFEQ;
491                }
492                else if (opToken == JetTokens.EXCLEQ) {
493                    opcode = jumpIfFalse ? IFEQ : IFNE;
494                }
495                else if (opToken == JetTokens.GT) {
496                    opcode = jumpIfFalse ? IFLE : IFGT;
497                }
498                else if (opToken == JetTokens.GTEQ) {
499                    opcode = jumpIfFalse ? IFLT : IFGE;
500                }
501                else if (opToken == JetTokens.LT) {
502                    opcode = jumpIfFalse ? IFGE : IFLT;
503                }
504                else if (opToken == JetTokens.LTEQ) {
505                    opcode = jumpIfFalse ? IFGT : IFLE;
506                }
507                else {
508                    throw new UnsupportedOperationException("don't know how to generate this condjump");
509                }
510                if (operandType == Type.FLOAT_TYPE || operandType == Type.DOUBLE_TYPE) {
511                    if (opToken == JetTokens.GT || opToken == JetTokens.GTEQ) {
512                        v.cmpl(operandType);
513                    }
514                    else {
515                        v.cmpg(operandType);
516                    }
517                }
518                else if (operandType == Type.LONG_TYPE) {
519                    v.lcmp();
520                }
521                else {
522                    opcode += (IF_ICMPEQ - IFEQ);
523                }
524                v.visitJumpInsn(opcode, label);
525            }
526        }
527    
528        private static class ObjectCompare extends NumberCompare {
529            public ObjectCompare(IElementType opToken, Type operandType) {
530                super(opToken, operandType);
531            }
532    
533            @Override
534            public void condJump(Label label, boolean jumpIfFalse, InstructionAdapter v) {
535                int opcode;
536                if (opToken == JetTokens.EQEQEQ) {
537                    opcode = jumpIfFalse ? IF_ACMPNE : IF_ACMPEQ;
538                }
539                else if (opToken == JetTokens.EXCLEQEQEQ) {
540                    opcode = jumpIfFalse ? IF_ACMPEQ : IF_ACMPNE;
541                }
542                else {
543                    throw new UnsupportedOperationException("don't know how to generate this condjump");
544                }
545                v.visitJumpInsn(opcode, label);
546            }
547        }
548    
549        private static class Invert extends StackValue {
550            private final StackValue myOperand;
551    
552            private Invert(StackValue operand) {
553                super(Type.BOOLEAN_TYPE);
554                myOperand = operand;
555                if (myOperand.type != Type.BOOLEAN_TYPE) {
556                    throw new UnsupportedOperationException("operand of ! must be boolean");
557                }
558            }
559    
560            @Override
561            public void put(Type type, InstructionAdapter v) {
562                putAsBoolean(v);
563                coerceTo(type, v);
564            }
565    
566            @Override
567            public void condJump(Label label, boolean jumpIfFalse, InstructionAdapter v) {
568                myOperand.condJump(label, !jumpIfFalse, v);
569            }
570        }
571    
572        private static class ArrayElement extends StackValue {
573            public ArrayElement(Type type) {
574                super(type);
575            }
576    
577            @Override
578            public void put(Type type, InstructionAdapter v) {
579                v.aload(this.type);    // assumes array and index are on the stack
580                coerceTo(type, v);
581            }
582    
583            @Override
584            public void store(Type topOfStackType, InstructionAdapter v) {
585                coerceFrom(topOfStackType, v);
586                v.astore(this.type);
587            }
588    
589            @Override
590            public void dupReceiver(InstructionAdapter v) {
591                v.dup2();   // array and index
592            }
593    
594            @Override
595            public int receiverSize() {
596                return 2;
597            }
598        }
599    
600        private static class CollectionElement extends StackValue {
601            private final Callable getter;
602            private final Callable setter;
603            private final ExpressionCodegen codegen;
604            private final GenerationState state;
605            private final FrameMap frame;
606            private final ResolvedCall<FunctionDescriptor> resolvedGetCall;
607            private final ResolvedCall<FunctionDescriptor> resolvedSetCall;
608            private final FunctionDescriptor setterDescriptor;
609            private final FunctionDescriptor getterDescriptor;
610    
611            public CollectionElement(
612                    Type type,
613                    ResolvedCall<FunctionDescriptor> resolvedGetCall,
614                    ResolvedCall<FunctionDescriptor> resolvedSetCall,
615                    ExpressionCodegen codegen,
616                    GenerationState state
617            ) {
618                super(type);
619                this.resolvedGetCall = resolvedGetCall;
620                this.resolvedSetCall = resolvedSetCall;
621                this.state = state;
622                this.setterDescriptor = resolvedSetCall == null ? null : resolvedSetCall.getResultingDescriptor();
623                this.getterDescriptor = resolvedGetCall == null ? null : resolvedGetCall.getResultingDescriptor();
624                this.setter = resolvedSetCall == null ? null : codegen.resolveToCallable(setterDescriptor, false);
625                this.getter = resolvedGetCall == null ? null : codegen.resolveToCallable(getterDescriptor, false);
626                this.codegen = codegen;
627                this.frame = codegen.myFrameMap;
628            }
629    
630            @Override
631            public void put(Type type, InstructionAdapter v) {
632                if (getter == null) {
633                    throw new UnsupportedOperationException("no getter specified");
634                }
635                if (getter instanceof CallableMethod) {
636                    ((CallableMethod) getter).invokeWithNotNullAssertion(v, state, resolvedGetCall);
637                }
638                else {
639                    ((IntrinsicMethod) getter).generate(codegen, v, this.type, null, null, null);
640                }
641                coerceTo(type, v);
642            }
643    
644            @Override
645            public void store(Type topOfStackType, InstructionAdapter v) {
646                if (setter == null) {
647                    throw new UnsupportedOperationException("no setter specified");
648                }
649                if (setter instanceof CallableMethod) {
650                    CallableMethod method = (CallableMethod) setter;
651                    Method asmMethod = method.getAsmMethod();
652                    Type[] argumentTypes = asmMethod.getArgumentTypes();
653                    coerce(topOfStackType, argumentTypes[argumentTypes.length - 1], v);
654                    method.invokeWithNotNullAssertion(v, state, resolvedSetCall);
655                    Type returnType = asmMethod.getReturnType();
656                    if (returnType != Type.VOID_TYPE) {
657                        pop(v, returnType);
658                    }
659                }
660                else {
661                    //noinspection ConstantConditions
662                    ((IntrinsicMethod) setter).generate(codegen, v, null, null, null, null);
663                }
664            }
665    
666            @Override
667            public int receiverSize() {
668                if (isStandardStack(resolvedGetCall, 1) && isStandardStack(resolvedSetCall, 2)) {
669                    return 2;
670                }
671                else {
672                    return -1;
673                }
674            }
675    
676            @Override
677            public void dupReceiver(InstructionAdapter v) {
678                if (isStandardStack(resolvedGetCall, 1) && isStandardStack(resolvedSetCall, 2)) {
679                    v.dup2();   // collection and index
680                    return;
681                }
682    
683                FrameMap.Mark mark = frame.mark();
684    
685                // indexes
686                List<ValueParameterDescriptor> valueParameters = resolvedGetCall.getResultingDescriptor().getValueParameters();
687                int firstParamIndex = -1;
688                for (int i = valueParameters.size() - 1; i >= 0; --i) {
689                    Type type = codegen.typeMapper.mapType(valueParameters.get(i).getType());
690                    firstParamIndex = frame.enterTemp(type);
691                    v.store(firstParamIndex, type);
692                }
693    
694                ReceiverValue receiverParameter = resolvedGetCall.getReceiverArgument();
695                int receiverIndex = -1;
696                if (receiverParameter.exists()) {
697                    Type type = codegen.typeMapper.mapType(receiverParameter.getType());
698                    receiverIndex = frame.enterTemp(type);
699                    v.store(receiverIndex, type);
700                }
701    
702                ReceiverValue thisObject = resolvedGetCall.getThisObject();
703                int thisIndex = -1;
704                if (thisObject.exists()) {
705                    thisIndex = frame.enterTemp(OBJECT_TYPE);
706                    v.store(thisIndex, OBJECT_TYPE);
707                }
708    
709                // for setter
710    
711                int realReceiverIndex;
712                Type realReceiverType;
713                if (receiverIndex != -1) {
714                    realReceiverType = codegen.typeMapper.mapType(receiverParameter.getType());
715                    realReceiverIndex = receiverIndex;
716                }
717                else if (thisIndex != -1) {
718                    realReceiverType = OBJECT_TYPE;
719                    realReceiverIndex = thisIndex;
720                }
721                else {
722                    throw new UnsupportedOperationException();
723                }
724    
725                if (resolvedSetCall.getThisObject().exists()) {
726                    if (resolvedSetCall.getReceiverArgument().exists()) {
727                        codegen.generateReceiverValue(resolvedSetCall.getThisObject(), OBJECT_TYPE);
728                    }
729                    v.load(realReceiverIndex, realReceiverType);
730                }
731                else {
732                    if (resolvedSetCall.getReceiverArgument().exists()) {
733                        v.load(realReceiverIndex, realReceiverType);
734                    }
735                    else {
736                        throw new UnsupportedOperationException();
737                    }
738                }
739    
740                int index = firstParamIndex;
741                for (ValueParameterDescriptor valueParameter : valueParameters) {
742                    Type type = codegen.typeMapper.mapType(valueParameter.getType());
743                    v.load(index, type);
744                    index -= type.getSize();
745                }
746    
747                // restoring original
748                if (thisIndex != -1) {
749                    v.load(thisIndex, OBJECT_TYPE);
750                }
751    
752                if (receiverIndex != -1) {
753                    v.load(receiverIndex, realReceiverType);
754                }
755    
756                index = firstParamIndex;
757                for (ValueParameterDescriptor valueParameter : valueParameters) {
758                    Type type = codegen.typeMapper.mapType(valueParameter.getType());
759                    v.load(index, type);
760                    index -= type.getSize();
761                }
762    
763                mark.dropTo();
764            }
765    
766            private boolean isStandardStack(ResolvedCall<?> call, int valueParamsSize) {
767                if (call == null) {
768                    return true;
769                }
770    
771                List<ValueParameterDescriptor> valueParameters = call.getResultingDescriptor().getValueParameters();
772                if (valueParameters.size() != valueParamsSize) {
773                    return false;
774                }
775    
776                for (ValueParameterDescriptor valueParameter : valueParameters) {
777                    if (codegen.typeMapper.mapType(valueParameter.getType()).getSize() != 1) {
778                        return false;
779                    }
780                }
781    
782                if (call.getThisObject().exists()) {
783                    if (call.getReceiverArgument().exists()) {
784                        return false;
785                    }
786                }
787                else {
788                    if (codegen.typeMapper.mapType(call.getResultingDescriptor().getReceiverParameter().getType())
789                                .getSize() != 1) {
790                        return false;
791                    }
792                }
793    
794                return true;
795            }
796        }
797    
798    
799        public static class Field extends StackValueWithSimpleReceiver {
800            public final Type owner;
801            public final String name;
802    
803            public Field(Type type, Type owner, String name, boolean isStatic) {
804                super(type, isStatic);
805                this.owner = owner;
806                this.name = name;
807            }
808    
809            @Override
810            public void put(Type type, InstructionAdapter v) {
811                v.visitFieldInsn(isStatic ? GETSTATIC : GETFIELD, owner.getInternalName(), name, this.type.getDescriptor());
812                coerceTo(type, v);
813            }
814    
815            @Override
816            public void store(Type topOfStackType, InstructionAdapter v) {
817                coerceFrom(topOfStackType, v);
818                v.visitFieldInsn(isStatic ? PUTSTATIC : PUTFIELD, owner.getInternalName(), name, this.type.getDescriptor());
819            }
820        }
821    
822        static class Property extends StackValueWithSimpleReceiver {
823            private final CallableMethod getter;
824            private final CallableMethod setter;
825            private final Type methodOwner;
826    
827            private final PropertyDescriptor descriptor;
828            private final GenerationState state;
829    
830            private final String fieldName;
831    
832            public Property(
833                    @NotNull PropertyDescriptor descriptor, @NotNull Type methodOwner,
834                    @Nullable CallableMethod getter, @Nullable CallableMethod setter, boolean isStatic,
835                    @Nullable String fieldName, @NotNull Type type, @NotNull GenerationState state
836            ) {
837                super(type, isStatic);
838                this.methodOwner = methodOwner;
839                this.getter = getter;
840                this.setter = setter;
841                this.descriptor = descriptor;
842                this.state = state;
843                this.fieldName = fieldName;
844            }
845    
846            @Override
847            public void put(Type type, InstructionAdapter v) {
848                if (getter == null) {
849                    assert fieldName != null : "Property should have either a getter or a field name: " + descriptor;
850                    v.visitFieldInsn(isStatic ? GETSTATIC : GETFIELD, methodOwner.getInternalName(), fieldName, this.type.getDescriptor());
851                    genNotNullAssertionForField(v, state, descriptor);
852                    coerceTo(type, v);
853                }
854                else {
855                    getter.invokeWithoutAssertions(v);
856                    coerce(getter.getAsmMethod().getReturnType(), type, v);
857                }
858            }
859    
860            @Override
861            public void store(Type topOfStackType, InstructionAdapter v) {
862                coerceFrom(topOfStackType, v);
863                if (setter == null) {
864                    assert fieldName != null : "Property should have either a setter or a field name: " + descriptor;
865                    v.visitFieldInsn(isStatic ? PUTSTATIC : PUTFIELD, methodOwner.getInternalName(), fieldName, this.type.getDescriptor());
866                }
867                else {
868                    setter.invokeWithoutAssertions(v);
869                }
870            }
871    
872            public boolean isPropertyWithBackingFieldInOuterClass() {
873                return descriptor instanceof AccessorForPropertyBackingFieldInOuterClass;
874            }
875        }
876    
877        private static class Expression extends StackValue {
878            private final JetExpression expression;
879            private final ExpressionCodegen generator;
880    
881            public Expression(Type type, JetExpression expression, ExpressionCodegen generator) {
882                super(type);
883                this.expression = expression;
884                this.generator = generator;
885            }
886    
887            @Override
888            public void put(Type type, InstructionAdapter v) {
889                generator.gen(expression, type);
890            }
891        }
892    
893        public static class Shared extends StackValue {
894            private final int index;
895            private boolean isReleaseOnPut = false;
896    
897            public Shared(int index, Type type) {
898                super(type);
899                this.index = index;
900            }
901    
902            public void releaseOnPut() {
903                isReleaseOnPut = true;
904            }
905    
906            public int getIndex() {
907                return index;
908            }
909    
910            @Override
911            public void put(Type type, InstructionAdapter v) {
912                v.load(index, OBJECT_TYPE);
913                Type refType = refType(this.type);
914                Type sharedType = sharedTypeForType(this.type);
915                v.visitFieldInsn(GETFIELD, sharedType.getInternalName(), "element", refType.getDescriptor());
916                coerceFrom(refType, v);
917                coerceTo(type, v);
918                if (isReleaseOnPut) {
919                    v.aconst(null);
920                    v.store(index, OBJECT_TYPE);
921                }
922            }
923    
924            @Override
925            public void store(Type topOfStackType, InstructionAdapter v) {
926                coerceFrom(topOfStackType, v);
927                v.load(index, OBJECT_TYPE);
928                AsmUtil.swap(v, sharedTypeForType(this.type), topOfStackType);
929                Type refType = refType(this.type);
930                Type sharedType = sharedTypeForType(this.type);
931                v.visitFieldInsn(PUTFIELD, sharedType.getInternalName(), "element", refType.getDescriptor());
932            }
933        }
934    
935        public static Type sharedTypeForType(Type type) {
936            switch (type.getSort()) {
937                case Type.OBJECT:
938                case Type.ARRAY:
939                    return OBJECT_REF_TYPE;
940                case Type.BYTE:
941                    return Type.getObjectType("kotlin/jvm/internal/Ref$ByteRef");
942                case Type.SHORT:
943                    return Type.getObjectType("kotlin/jvm/internal/Ref$ShortRef");
944                case Type.CHAR:
945                    return Type.getObjectType("kotlin/jvm/internal/Ref$CharRef");
946                case Type.INT:
947                    return Type.getObjectType("kotlin/jvm/internal/Ref$IntRef");
948                case Type.LONG:
949                    return Type.getObjectType("kotlin/jvm/internal/Ref$LongRef");
950                case Type.BOOLEAN:
951                    return Type.getObjectType("kotlin/jvm/internal/Ref$BooleanRef");
952                case Type.FLOAT:
953                    return Type.getObjectType("kotlin/jvm/internal/Ref$FloatRef");
954                case Type.DOUBLE:
955                    return Type.getObjectType("kotlin/jvm/internal/Ref$DoubleRef");
956                default:
957                    throw new UnsupportedOperationException();
958            }
959        }
960    
961        public static Type refType(Type type) {
962            if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
963                return OBJECT_TYPE;
964            }
965    
966            return type;
967        }
968    
969        static class FieldForSharedVar extends StackValueWithSimpleReceiver {
970            final Type owner;
971            final String name;
972    
973            public FieldForSharedVar(Type type, Type owner, String name) {
974                super(type, false);
975                this.owner = owner;
976                this.name = name;
977            }
978    
979            @Override
980            public void put(Type type, InstructionAdapter v) {
981                Type sharedType = sharedTypeForType(this.type);
982                Type refType = refType(this.type);
983                v.visitFieldInsn(GETFIELD, sharedType.getInternalName(), "element", refType.getDescriptor());
984                coerceFrom(refType, v);
985                coerceTo(type, v);
986            }
987    
988            @Override
989            public void store(Type topOfStackType, InstructionAdapter v) {
990                coerceFrom(topOfStackType, v);
991                v.visitFieldInsn(PUTFIELD, sharedTypeForType(type).getInternalName(), "element", refType(type).getDescriptor());
992            }
993        }
994    
995        public static class Composed extends StackValue {
996            public final StackValue prefix;
997            public final StackValue suffix;
998    
999            public Composed(StackValue prefix, StackValue suffix) {
1000                super(suffix.type);
1001                this.prefix = prefix;
1002                this.suffix = suffix;
1003            }
1004    
1005            @Override
1006            public void put(Type type, InstructionAdapter v) {
1007                prefix.put(prefix.type, v);
1008                suffix.put(type, v);
1009            }
1010    
1011            @Override
1012            public void store(Type topOfStackType, InstructionAdapter v) {
1013                prefix.put(OBJECT_TYPE, v);
1014                suffix.store(topOfStackType, v);
1015            }
1016        }
1017    
1018        private static class ThisOuter extends StackValue {
1019            private final ExpressionCodegen codegen;
1020            private final ClassDescriptor descriptor;
1021            private final boolean isSuper;
1022            private final boolean coerceType;
1023    
1024            public ThisOuter(ExpressionCodegen codegen, ClassDescriptor descriptor, boolean isSuper, boolean coerceType) {
1025                super(OBJECT_TYPE);
1026                this.codegen = codegen;
1027                this.descriptor = descriptor;
1028                this.isSuper = isSuper;
1029                this.coerceType = coerceType;
1030            }
1031    
1032            @Override
1033            public void put(Type type, InstructionAdapter v) {
1034                StackValue stackValue = codegen.generateThisOrOuter(descriptor, isSuper);
1035                stackValue.put(coerceType ? type : stackValue.type, v);
1036            }
1037        }
1038    
1039        private static class PostIncrement extends StackValue {
1040            private final int index;
1041            private final int increment;
1042    
1043            public PostIncrement(int index, int increment) {
1044                super(Type.INT_TYPE);
1045                this.index = index;
1046                this.increment = increment;
1047            }
1048    
1049            @Override
1050            public void put(Type type, InstructionAdapter v) {
1051                if (!type.equals(Type.VOID_TYPE)) {
1052                    v.load(index, Type.INT_TYPE);
1053                    coerceTo(type, v);
1054                }
1055                v.iinc(index, increment);
1056            }
1057        }
1058    
1059        private static class PreIncrement extends StackValue {
1060            private final int index;
1061            private final int increment;
1062    
1063            public PreIncrement(int index, int increment) {
1064                super(Type.INT_TYPE);
1065                this.index = index;
1066                this.increment = increment;
1067            }
1068    
1069            @Override
1070            public void put(Type type, InstructionAdapter v) {
1071                v.iinc(index, increment);
1072                if (!type.equals(Type.VOID_TYPE)) {
1073                    v.load(index, Type.INT_TYPE);
1074                    coerceTo(type, v);
1075                }
1076            }
1077        }
1078    
1079        public static class CallReceiver extends StackValue {
1080            private final ResolvedCall<?> resolvedCall;
1081            private final StackValue receiver;
1082            private final ExpressionCodegen codegen;
1083            private final CallableMethod callableMethod;
1084            private final boolean putReceiverArgumentOnStack;
1085    
1086            public CallReceiver(
1087                    @NotNull ResolvedCall<?> resolvedCall,
1088                    @NotNull StackValue receiver,
1089                    @NotNull ExpressionCodegen codegen,
1090                    @Nullable CallableMethod callableMethod,
1091                    boolean putReceiverArgumentOnStack
1092            ) {
1093                super(calcType(resolvedCall, codegen.typeMapper, callableMethod));
1094                this.resolvedCall = resolvedCall;
1095                this.receiver = receiver;
1096                this.codegen = codegen;
1097                this.callableMethod = callableMethod;
1098                this.putReceiverArgumentOnStack = putReceiverArgumentOnStack;
1099            }
1100    
1101            private static Type calcType(
1102                    @NotNull ResolvedCall<?> resolvedCall,
1103                    @NotNull JetTypeMapper typeMapper,
1104                    @Nullable CallableMethod callableMethod
1105            ) {
1106                CallableDescriptor descriptor = resolvedCall.getResultingDescriptor();
1107    
1108                ReceiverParameterDescriptor expectedThisObject = descriptor.getExpectedThisObject();
1109                ReceiverParameterDescriptor receiverParameter = descriptor.getReceiverParameter();
1110    
1111                if (receiverParameter != null) {
1112                    return callableMethod != null ? callableMethod.getReceiverClass() : typeMapper.mapType(receiverParameter.getType());
1113                }
1114                else if (expectedThisObject != null) {
1115                    return callableMethod != null ? callableMethod.getThisType() : typeMapper.mapType(expectedThisObject.getType());
1116                }
1117                else if (isLocalFunCall(callableMethod)) {
1118                    return callableMethod.getGenerateCalleeType();
1119                }
1120    
1121                return Type.VOID_TYPE;
1122            }
1123    
1124            @Override
1125            public void put(Type type, InstructionAdapter v) {
1126                CallableDescriptor descriptor = resolvedCall.getResultingDescriptor();
1127    
1128                ReceiverValue thisObject = resolvedCall.getThisObject();
1129                ReceiverValue receiverArgument = resolvedCall.getReceiverArgument();
1130                int depth;
1131                if (thisObject.exists()) {
1132                    if (receiverArgument.exists()) {
1133                        //noinspection ConstantConditions
1134                        Type resultType =
1135                                callableMethod != null ?
1136                                callableMethod.getOwner() :
1137                                codegen.typeMapper.mapType(descriptor.getExpectedThisObject().getType());
1138    
1139                        codegen.generateReceiverValue(thisObject, resultType);
1140                    }
1141                    else {
1142                        genReceiver(v, thisObject, type, null, 0);
1143                    }
1144    
1145                    depth = 1;
1146                }
1147                else if (isLocalFunCall(callableMethod)) {
1148                    assert receiver == none() || receiverArgument.exists() :
1149                            "Receiver should be present only for local extension function: " + callableMethod;
1150                    StackValue value = codegen.findLocalOrCapturedValue(descriptor.getOriginal());
1151                    assert value != null : "Local fun should be found in locals or in captured params: " + resolvedCall;
1152                    value.put(callableMethod.getGenerateCalleeType(), v);
1153    
1154                    depth = 1;
1155                }
1156                else {
1157                    depth = 0;
1158                }
1159    
1160                if (putReceiverArgumentOnStack && receiverArgument.exists()) {
1161                    genReceiver(v, receiverArgument, type, descriptor.getReceiverParameter(), depth);
1162                }
1163            }
1164    
1165            private void genReceiver(
1166                    @NotNull InstructionAdapter v,
1167                    @NotNull ReceiverValue receiverArgument,
1168                    @NotNull Type type,
1169                    @Nullable ReceiverParameterDescriptor receiverParameter,
1170                    int depth
1171            ) {
1172                if (receiver == StackValue.none()) {
1173                    if (receiverParameter != null) {
1174                        Type receiverType = codegen.typeMapper.mapType(receiverParameter.getType());
1175                        codegen.generateReceiverValue(receiverArgument, receiverType);
1176                        StackValue.onStack(receiverType).put(type, v);
1177                    }
1178                    else {
1179                        codegen.generateReceiverValue(receiverArgument, type);
1180                    }
1181                }
1182                else {
1183                    receiver.moveToTopOfStack(type, v, depth);
1184                }
1185            }
1186        }
1187    
1188        public abstract static class StackValueWithSimpleReceiver extends StackValue {
1189    
1190            public final boolean isStatic;
1191    
1192            public StackValueWithSimpleReceiver(@NotNull Type type, boolean isStatic) {
1193                super(type);
1194                this.isStatic = isStatic;
1195            }
1196    
1197            @Override
1198            public void dupReceiver(InstructionAdapter v) {
1199                if (!isStatic) {
1200                    v.dup();
1201                }
1202            }
1203    
1204            @Override
1205            public int receiverSize() {
1206                return isStatic ? 0 : 1;
1207            }
1208        }
1209    }