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.google.common.collect.ImmutableMap;
020    import com.google.common.collect.Sets;
021    import com.intellij.openapi.util.Pair;
022    import com.intellij.psi.tree.IElementType;
023    import org.jetbrains.annotations.NotNull;
024    import org.jetbrains.annotations.Nullable;
025    import org.jetbrains.asm4.Label;
026    import org.jetbrains.asm4.MethodVisitor;
027    import org.jetbrains.asm4.Type;
028    import org.jetbrains.asm4.commons.InstructionAdapter;
029    import org.jetbrains.jet.codegen.binding.CalculatedClosure;
030    import org.jetbrains.jet.codegen.state.GenerationState;
031    import org.jetbrains.jet.codegen.state.JetTypeMapper;
032    import org.jetbrains.jet.lang.descriptors.*;
033    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
034    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
035    import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
036    import org.jetbrains.jet.lang.resolve.java.JavaVisibilities;
037    import org.jetbrains.jet.lang.resolve.java.JvmClassName;
038    import org.jetbrains.jet.lang.resolve.java.JvmPrimitiveType;
039    import org.jetbrains.jet.lang.resolve.java.descriptor.JavaCallableMemberDescriptor;
040    import org.jetbrains.jet.lang.resolve.name.FqName;
041    import org.jetbrains.jet.lang.types.JetType;
042    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
043    import org.jetbrains.jet.lexer.JetTokens;
044    
045    import java.util.List;
046    import java.util.Map;
047    import java.util.Set;
048    
049    import static org.jetbrains.asm4.Opcodes.*;
050    import static org.jetbrains.jet.codegen.CodegenUtil.*;
051    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.*;
052    import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.JAVA_STRING_TYPE;
053    import static org.jetbrains.jet.lang.resolve.java.mapping.PrimitiveTypesUtil.asmTypeForPrimitive;
054    
055    public class AsmUtil {
056        private static final Set<ClassDescriptor> PRIMITIVE_NUMBER_CLASSES = Sets.newHashSet(
057                KotlinBuiltIns.getInstance().getByte(),
058                KotlinBuiltIns.getInstance().getShort(),
059                KotlinBuiltIns.getInstance().getInt(),
060                KotlinBuiltIns.getInstance().getLong(),
061                KotlinBuiltIns.getInstance().getFloat(),
062                KotlinBuiltIns.getInstance().getDouble(),
063                KotlinBuiltIns.getInstance().getChar()
064        );
065    
066        private static final int NO_FLAG_LOCAL = 0;
067        public static final int NO_FLAG_PACKAGE_PRIVATE = 0;
068    
069        @NotNull
070        private static final Map<Visibility, Integer> visibilityToAccessFlag = ImmutableMap.<Visibility, Integer>builder()
071                .put(Visibilities.PRIVATE, ACC_PRIVATE)
072                .put(Visibilities.PROTECTED, ACC_PROTECTED)
073                .put(JavaVisibilities.PROTECTED_STATIC_VISIBILITY, ACC_PROTECTED)
074                .put(JavaVisibilities.PROTECTED_AND_PACKAGE, ACC_PROTECTED)
075                .put(Visibilities.PUBLIC, ACC_PUBLIC)
076                .put(Visibilities.INTERNAL, ACC_PUBLIC)
077                .put(Visibilities.LOCAL, NO_FLAG_LOCAL)
078                .put(JavaVisibilities.PACKAGE_VISIBILITY, NO_FLAG_PACKAGE_PRIVATE)
079                .build();
080    
081        public static final String CAPTURED_RECEIVER_FIELD = "receiver$0";
082        public static final String CAPTURED_THIS_FIELD = "this$0";
083    
084        private static final ImmutableMap<Integer, JvmPrimitiveType> primitiveTypeByAsmSort;
085        private static final ImmutableMap<Type, Type> primitiveTypeByBoxedType;
086    
087        static {
088            ImmutableMap.Builder<Integer, JvmPrimitiveType> typeBySortBuilder = ImmutableMap.builder();
089            ImmutableMap.Builder<Type, Type> typeByWrapperBuilder = ImmutableMap.builder();
090            for (JvmPrimitiveType primitiveType : JvmPrimitiveType.values()) {
091                Type asmType = asmTypeForPrimitive(primitiveType);
092                typeBySortBuilder.put(asmType.getSort(), primitiveType);
093                typeByWrapperBuilder.put(asmTypeByFqNameWithoutInnerClasses(primitiveType.getWrapperFqName()), asmType);
094            }
095            primitiveTypeByAsmSort = typeBySortBuilder.build();
096            primitiveTypeByBoxedType = typeByWrapperBuilder.build();
097        }
098    
099        private AsmUtil() {
100        }
101    
102        @NotNull
103        public static Type boxType(@NotNull Type type) {
104            JvmPrimitiveType jvmPrimitiveType = primitiveTypeByAsmSort.get(type.getSort());
105            return jvmPrimitiveType != null ? asmTypeByFqNameWithoutInnerClasses(jvmPrimitiveType.getWrapperFqName()) : type;
106        }
107    
108        @NotNull
109        public static Type unboxType(@NotNull Type boxedType) {
110            Type primitiveType = primitiveTypeByBoxedType.get(boxedType);
111            if (primitiveType == null) {
112                throw new UnsupportedOperationException("Unboxing: " + boxedType);
113            }
114            return primitiveType;
115        }
116    
117        public static boolean isIntPrimitive(Type type) {
118            return type == Type.INT_TYPE || type == Type.SHORT_TYPE || type == Type.BYTE_TYPE || type == Type.CHAR_TYPE;
119        }
120    
121        public static boolean isNumberPrimitive(Type type) {
122            return isIntPrimitive(type) || type == Type.FLOAT_TYPE || type == Type.DOUBLE_TYPE || type == Type.LONG_TYPE;
123        }
124    
125        public static boolean isPrimitive(Type type) {
126            return type.getSort() != Type.OBJECT && type.getSort() != Type.ARRAY;
127        }
128    
129        public static boolean isPrimitiveNumberClassDescriptor(DeclarationDescriptor descriptor) {
130            if (!(descriptor instanceof ClassDescriptor)) {
131                return false;
132            }
133            return PRIMITIVE_NUMBER_CLASSES.contains(descriptor);
134        }
135    
136        public static Type correctElementType(Type type) {
137            String internalName = type.getInternalName();
138            assert internalName.charAt(0) == '[';
139            return Type.getType(internalName.substring(1));
140        }
141    
142        public static boolean isAbstractMethod(FunctionDescriptor functionDescriptor, OwnerKind kind) {
143            return (functionDescriptor.getModality() == Modality.ABSTRACT
144                    || isInterface(functionDescriptor.getContainingDeclaration()))
145                   && !isStaticMethod(kind, functionDescriptor);
146        }
147    
148        public static boolean isStaticMethod(OwnerKind kind, FunctionDescriptor functionDescriptor) {
149            return isStatic(kind) || JetTypeMapper.isAccessor(functionDescriptor);
150        }
151    
152        public static boolean isStatic(OwnerKind kind) {
153            return kind == OwnerKind.NAMESPACE || kind == OwnerKind.TRAIT_IMPL;
154        }
155    
156        public static int getMethodAsmFlags(FunctionDescriptor functionDescriptor, OwnerKind kind) {
157            int flags = getCommonCallableFlags(functionDescriptor);
158    
159            if (functionDescriptor.getModality() == Modality.FINAL && !(functionDescriptor instanceof ConstructorDescriptor)) {
160                DeclarationDescriptor containingDeclaration = functionDescriptor.getContainingDeclaration();
161                if (!(containingDeclaration instanceof ClassDescriptor) ||
162                    ((ClassDescriptor) containingDeclaration).getKind() != ClassKind.TRAIT) {
163                    flags |= ACC_FINAL;
164                }
165            }
166    
167            if (isStaticMethod(kind, functionDescriptor)) {
168                flags |= ACC_STATIC;
169            }
170    
171            if (isAbstractMethod(functionDescriptor, kind)) {
172                flags |= ACC_ABSTRACT;
173            }
174    
175            if (JetTypeMapper.isAccessor(functionDescriptor)) {
176                flags |= ACC_SYNTHETIC;
177            }
178    
179            return flags;
180        }
181    
182        private static int getCommonCallableFlags(FunctionDescriptor functionDescriptor) {
183            int flags = getVisibilityAccessFlag(functionDescriptor);
184            flags |= getVarargsFlag(functionDescriptor);
185            flags |= getDeprecatedAccessFlag(functionDescriptor);
186            return flags;
187        }
188    
189        //TODO: move mapping logic to front-end java
190        public static int getVisibilityAccessFlag(@NotNull MemberDescriptor descriptor) {
191            Integer specialCase = specialCaseVisibility(descriptor);
192            if (specialCase != null) {
193                return specialCase;
194            }
195            Integer defaultMapping = visibilityToAccessFlag.get(descriptor.getVisibility());
196            if (defaultMapping == null) {
197                throw new IllegalStateException(descriptor.getVisibility() + " is not a valid visibility in backend. Descriptor: " + descriptor);
198            }
199            return defaultMapping;
200        }
201    
202        /*
203            Use this method to get visibility flag for class to define it in byte code (v.defineClass method).
204            For other cases use getVisibilityAccessFlag(MemberDescriptor descriptor)
205            Classes in byte code should be public or package private
206         */
207        public static int getVisibilityAccessFlagForClass(ClassDescriptor descriptor) {
208            if (DescriptorUtils.isTopLevelDeclaration(descriptor) ||
209                descriptor.getVisibility() == Visibilities.PUBLIC ||
210                descriptor.getVisibility() == Visibilities.INTERNAL) {
211                return ACC_PUBLIC;
212            }
213            return NO_FLAG_PACKAGE_PRIVATE;
214        }
215    
216        public static int getDeprecatedAccessFlag(@NotNull MemberDescriptor descriptor) {
217            if (descriptor instanceof PropertyAccessorDescriptor) {
218                return KotlinBuiltIns.getInstance().isDeprecated(descriptor)
219                         ? ACC_DEPRECATED
220                         : getDeprecatedAccessFlag(((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty());
221            }
222            else if (KotlinBuiltIns.getInstance().isDeprecated(descriptor)) {
223                return ACC_DEPRECATED;
224            }
225            return 0;
226        }
227    
228        private static int getVarargsFlag(FunctionDescriptor functionDescriptor) {
229            if (!functionDescriptor.getValueParameters().isEmpty()
230                && functionDescriptor.getValueParameters().get(functionDescriptor.getValueParameters().size() - 1)
231                           .getVarargElementType() != null) {
232                return ACC_VARARGS;
233            }
234            return 0;
235        }
236    
237        @Nullable
238        private static Integer specialCaseVisibility(@NotNull MemberDescriptor memberDescriptor) {
239            DeclarationDescriptor containingDeclaration = memberDescriptor.getContainingDeclaration();
240            if (isInterface(containingDeclaration)) {
241                return ACC_PUBLIC;
242            }
243            Visibility memberVisibility = memberDescriptor.getVisibility();
244            if (memberVisibility == Visibilities.LOCAL && memberDescriptor instanceof CallableMemberDescriptor) {
245                return ACC_PUBLIC;
246            }
247            if (isEnumEntry(memberDescriptor)) {
248                return NO_FLAG_PACKAGE_PRIVATE;
249            }
250            if (memberVisibility != Visibilities.PRIVATE) {
251                return null;
252            }
253            // the following code is only for PRIVATE visibility of member
254            if (memberDescriptor instanceof ConstructorDescriptor) {
255                if (isAnonymousObject(containingDeclaration)) {
256                    return NO_FLAG_PACKAGE_PRIVATE;
257                }
258    
259                ClassKind kind = ((ClassDescriptor) containingDeclaration).getKind();
260                if (kind == ClassKind.OBJECT) {
261                    return NO_FLAG_PACKAGE_PRIVATE;
262                }
263                else if (kind == ClassKind.ENUM_ENTRY) {
264                    return NO_FLAG_PACKAGE_PRIVATE;
265                }
266                else if (kind == ClassKind.ENUM_CLASS) {
267                    //TODO: should be ACC_PRIVATE
268                    // see http://youtrack.jetbrains.com/issue/KT-2680
269                    return ACC_PROTECTED;
270                }
271            }
272            if (containingDeclaration instanceof PackageFragmentDescriptor) {
273                return ACC_PUBLIC;
274            }
275            return null;
276        }
277    
278        @NotNull
279        public static Type getTraitImplThisParameterType(@NotNull ClassDescriptor traitDescriptor, @NotNull JetTypeMapper typeMapper) {
280            JetType jetType = getSuperClass(traitDescriptor);
281            Type type = typeMapper.mapType(jetType);
282            if (type.getInternalName().equals("java/lang/Object")) {
283                return typeMapper.mapType(traitDescriptor.getDefaultType());
284            }
285            return type;
286        }
287    
288        private static Type stringValueOfOrStringBuilderAppendType(Type type) {
289            int sort = type.getSort();
290            return sort == Type.OBJECT || sort == Type.ARRAY
291                       ? AsmTypeConstants.OBJECT_TYPE
292                       : sort == Type.BYTE || sort == Type.SHORT ? Type.INT_TYPE : type;
293        }
294    
295        public static void genThrow(MethodVisitor mv, String exception, String message) {
296            InstructionAdapter iv = new InstructionAdapter(mv);
297            iv.anew(Type.getObjectType(exception));
298            iv.dup();
299            iv.aconst(message);
300            iv.invokespecial(exception, "<init>", "(Ljava/lang/String;)V");
301            iv.athrow();
302        }
303    
304        public static void genMethodThrow(MethodVisitor mv, String exception, String message) {
305            mv.visitCode();
306            genThrow(mv, exception, message);
307            mv.visitMaxs(-1, -1);
308            mv.visitEnd();
309        }
310    
311        public static void genClosureFields(CalculatedClosure closure, ClassBuilder v, JetTypeMapper typeMapper) {
312            ClassifierDescriptor captureThis = closure.getCaptureThis();
313            int access = NO_FLAG_PACKAGE_PRIVATE | ACC_SYNTHETIC | ACC_FINAL;
314            if (captureThis != null) {
315                v.newField(null, access, CAPTURED_THIS_FIELD, typeMapper.mapType(captureThis).getDescriptor(), null,
316                           null);
317            }
318    
319            JetType captureReceiverType = closure.getCaptureReceiverType();
320            if (captureReceiverType != null) {
321                v.newField(null, access, CAPTURED_RECEIVER_FIELD, typeMapper.mapType(captureReceiverType).getDescriptor(),
322                           null, null);
323            }
324    
325            List<Pair<String, Type>> fields = closure.getRecordedFields();
326            for (Pair<String, Type> field : fields) {
327                v.newField(null, access, field.first, field.second.getDescriptor(), null, null);
328            }
329        }
330    
331        public static int genAssignInstanceFieldFromParam(FieldInfo info, int index, InstructionAdapter iv) {
332            assert !info.isStatic();
333            Type fieldType = info.getFieldType();
334            iv.load(0, info.getOwnerType());//this
335            iv.load(index, fieldType); //param
336            iv.visitFieldInsn(PUTFIELD, info.getOwnerInternalName(), info.getFieldName(), fieldType.getDescriptor());
337            index += fieldType.getSize();
338            return index;
339        }
340    
341        public static void genStringBuilderConstructor(InstructionAdapter v) {
342            v.visitTypeInsn(NEW, "java/lang/StringBuilder");
343            v.dup();
344            v.invokespecial("java/lang/StringBuilder", "<init>", "()V");
345        }
346    
347        public static void genInvokeAppendMethod(InstructionAdapter v, Type type) {
348            type = stringValueOfOrStringBuilderAppendType(type);
349            v.invokevirtual("java/lang/StringBuilder", "append", "(" + type.getDescriptor() + ")Ljava/lang/StringBuilder;");
350        }
351    
352        public static StackValue genToString(InstructionAdapter v, StackValue receiver, Type receiverType) {
353            Type type = stringValueOfOrStringBuilderAppendType(receiverType);
354            receiver.put(type, v);
355            v.invokestatic("java/lang/String", "valueOf", "(" + type.getDescriptor() + ")Ljava/lang/String;");
356            return StackValue.onStack(JAVA_STRING_TYPE);
357        }
358    
359        static void genHashCode(MethodVisitor mv, InstructionAdapter iv, Type type) {
360            if (type.getSort() == Type.ARRAY) {
361                Type elementType = correctElementType(type);
362                if (elementType.getSort() == Type.OBJECT || elementType.getSort() == Type.ARRAY) {
363                    iv.invokestatic("java/util/Arrays", "hashCode", "([Ljava/lang/Object;)I");
364                }
365                else {
366                    iv.invokestatic("java/util/Arrays", "hashCode", "(" + type.getDescriptor() + ")I");
367                }
368            }
369            else if (type.getSort() == Type.OBJECT) {
370                iv.invokevirtual("java/lang/Object", "hashCode", "()I");
371            }
372            else if (type.getSort() == Type.LONG) {
373                genLongHashCode(mv, iv);
374            }
375            else if (type.getSort() == Type.DOUBLE) {
376                iv.invokestatic("java/lang/Double", "doubleToLongBits", "(D)J");
377                genLongHashCode(mv, iv);
378            }
379            else if (type.getSort() == Type.FLOAT) {
380                iv.invokestatic("java/lang/Float", "floatToIntBits", "(F)I");
381            }
382            else if (type.getSort() == Type.BOOLEAN) {
383                Label end = new Label();
384                iv.dup();
385                iv.ifeq(end);
386                iv.pop();
387                iv.iconst(1);
388                iv.mark(end);
389            }
390            else { // byte short char int
391                // do nothing
392            }
393        }
394    
395        private static void genLongHashCode(MethodVisitor mv, InstructionAdapter iv) {
396            iv.dup2();
397            iv.iconst(32);
398            iv.ushr(Type.LONG_TYPE);
399            iv.xor(Type.LONG_TYPE);
400            mv.visitInsn(L2I);
401        }
402    
403        static void genInvertBoolean(InstructionAdapter v) {
404            v.iconst(1);
405            v.xor(Type.INT_TYPE);
406        }
407    
408        public static StackValue genEqualsForExpressionsOnStack(
409                InstructionAdapter v,
410                IElementType opToken,
411                Type leftType,
412                Type rightType
413        ) {
414            if ((isNumberPrimitive(leftType) || leftType.getSort() == Type.BOOLEAN) && leftType == rightType) {
415                return StackValue.cmp(opToken, leftType);
416            }
417            else {
418                if (opToken == JetTokens.EQEQEQ || opToken == JetTokens.EXCLEQEQEQ) {
419                    return StackValue.cmp(opToken, leftType);
420                }
421                else {
422                    v.invokestatic("jet/runtime/Intrinsics", "areEqual", "(Ljava/lang/Object;Ljava/lang/Object;)Z");
423    
424                    if (opToken == JetTokens.EXCLEQ || opToken == JetTokens.EXCLEQEQEQ) {
425                        genInvertBoolean(v);
426                    }
427    
428                    return StackValue.onStack(Type.BOOLEAN_TYPE);
429                }
430            }
431        }
432    
433        public static void genIncrement(Type expectedType, int myDelta, InstructionAdapter v) {
434            if (expectedType == Type.LONG_TYPE) {
435                v.lconst(myDelta);
436            }
437            else if (expectedType == Type.FLOAT_TYPE) {
438                v.fconst(myDelta);
439            }
440            else if (expectedType == Type.DOUBLE_TYPE) {
441                v.dconst(myDelta);
442            }
443            else {
444                v.iconst(myDelta);
445                v.add(Type.INT_TYPE);
446                StackValue.coerce(Type.INT_TYPE, expectedType, v);
447                return;
448            }
449            v.add(expectedType);
450        }
451    
452        public static Type genNegate(Type expectedType, InstructionAdapter v) {
453            if (expectedType == Type.BYTE_TYPE || expectedType == Type.SHORT_TYPE || expectedType == Type.CHAR_TYPE) {
454                expectedType = Type.INT_TYPE;
455            }
456            v.neg(expectedType);
457            return expectedType;
458        }
459    
460        public static void swap(InstructionAdapter v, Type stackTop, Type afterTop) {
461            if (stackTop.getSize() == 1) {
462                if (afterTop.getSize() == 1) {
463                    v.swap();
464                } else {
465                    v.dupX2();
466                    v.pop();
467                }
468            } else {
469                if (afterTop.getSize() == 1) {
470                    v.dup2X1();
471                } else {
472                    v.dup2X2();
473                }
474                v.pop2();
475            }
476        }
477    
478        public static void genNotNullAssertionsForParameters(
479                @NotNull InstructionAdapter v,
480                @NotNull GenerationState state,
481                @NotNull FunctionDescriptor descriptor,
482                @NotNull FrameMap frameMap
483        ) {
484            if (!state.isGenerateNotNullParamAssertions()) return;
485    
486            // Private method is not accessible from other classes, no assertions needed
487            if (getVisibilityAccessFlag(descriptor) == ACC_PRIVATE) return;
488    
489            for (ValueParameterDescriptor parameter : descriptor.getValueParameters()) {
490                JetType type = parameter.getReturnType();
491                if (type == null || isNullableType(type)) continue;
492    
493                int index = frameMap.getIndex(parameter);
494                Type asmType = state.getTypeMapper().mapReturnType(type);
495                if (asmType.getSort() == Type.OBJECT || asmType.getSort() == Type.ARRAY) {
496                    v.load(index, asmType);
497                    v.visitLdcInsn(parameter.getName().asString());
498                    v.invokestatic("jet/runtime/Intrinsics", "checkParameterIsNotNull", "(Ljava/lang/Object;Ljava/lang/String;)V");
499                }
500            }
501        }
502    
503        public static void genNotNullAssertionForField(
504                @NotNull InstructionAdapter v,
505                @NotNull GenerationState state,
506                @NotNull PropertyDescriptor descriptor
507        ) {
508            genNotNullAssertion(v, state, descriptor, "checkFieldIsNotNull");
509        }
510    
511        public static void genNotNullAssertionForMethod(
512                @NotNull InstructionAdapter v,
513                @NotNull GenerationState state,
514                @NotNull ResolvedCall resolvedCall
515        ) {
516            CallableDescriptor descriptor = resolvedCall.getResultingDescriptor();
517            if (descriptor instanceof ConstructorDescriptor) return;
518    
519            genNotNullAssertion(v, state, descriptor, "checkReturnedValueIsNotNull");
520        }
521    
522        private static void genNotNullAssertion(
523                @NotNull InstructionAdapter v,
524                @NotNull GenerationState state,
525                @NotNull CallableDescriptor descriptor,
526                @NotNull String assertMethodToCall
527        ) {
528            if (!state.isGenerateNotNullAssertions()) return;
529    
530            if (!isDeclaredInJava(descriptor)) return;
531    
532            JetType type = descriptor.getReturnType();
533            if (type == null || isNullableType(type)) return;
534    
535            Type asmType = state.getTypeMapper().mapReturnType(type);
536            if (asmType.getSort() == Type.OBJECT || asmType.getSort() == Type.ARRAY) {
537                v.dup();
538                v.visitLdcInsn(descriptor.getContainingDeclaration().getName().asString());
539                v.visitLdcInsn(descriptor.getName().asString());
540                v.invokestatic("jet/runtime/Intrinsics", assertMethodToCall, "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V");
541            }
542        }
543    
544        private static boolean isDeclaredInJava(@NotNull CallableDescriptor callableDescriptor) {
545            CallableDescriptor descriptor = callableDescriptor;
546            while (true) {
547                if (descriptor instanceof JavaCallableMemberDescriptor) {
548                    return true;
549                }
550                CallableDescriptor original = descriptor.getOriginal();
551                if (descriptor == original) break;
552                descriptor = original;
553            }
554            return false;
555        }
556    
557        public static void pushDefaultValueOnStack(@NotNull Type type, @NotNull InstructionAdapter v) {
558            if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
559                v.aconst(null);
560            }
561            else {
562                pushDefaultPrimitiveValueOnStack(type, v);
563            }
564        }
565    
566        public static void pushDefaultPrimitiveValueOnStack(@NotNull Type type, @NotNull InstructionAdapter v) {
567            if (type.getSort() == Type.FLOAT) {
568                v.fconst(0);
569            }
570            else if (type.getSort() == Type.DOUBLE) {
571                v.dconst(0);
572            }
573            else if (type.getSort() == Type.LONG) {
574                v.lconst(0);
575            }
576            else {
577                v.iconst(0);
578            }
579        }
580    
581        public static boolean isPropertyWithBackingFieldInOuterClass(@NotNull PropertyDescriptor propertyDescriptor) {
582            return isPropertyWithSpecialBackingField(propertyDescriptor.getContainingDeclaration(), ClassKind.CLASS);
583        }
584    
585        public static int getVisibilityForSpecialPropertyBackingField(@NotNull PropertyDescriptor propertyDescriptor, boolean isDelegate) {
586            boolean isExtensionProperty = propertyDescriptor.getReceiverParameter() != null;
587            if (isDelegate || isExtensionProperty) {
588                return ACC_PRIVATE;
589            } else {
590                return areBothAccessorDefault(propertyDescriptor) ?  getVisibilityAccessFlag(descriptorForVisibility(propertyDescriptor)) : ACC_PRIVATE;
591            }
592        }
593    
594        private static MemberDescriptor descriptorForVisibility(@NotNull PropertyDescriptor propertyDescriptor) {
595            if (!propertyDescriptor.isVar() ) {
596                return propertyDescriptor;
597            } else {
598                return propertyDescriptor.getSetter() != null ? propertyDescriptor.getSetter() : propertyDescriptor;
599            }
600        }
601    
602        public static boolean isPropertyWithBackingFieldCopyInOuterClass(@NotNull PropertyDescriptor propertyDescriptor) {
603            boolean isExtensionProperty = propertyDescriptor.getReceiverParameter() != null;
604            return !propertyDescriptor.isVar() && !isExtensionProperty
605                   && isPropertyWithSpecialBackingField(propertyDescriptor.getContainingDeclaration(), ClassKind.TRAIT)
606                   && areBothAccessorDefault(propertyDescriptor)
607                   && getVisibilityForSpecialPropertyBackingField(propertyDescriptor, false) == ACC_PUBLIC;
608        }
609    
610        public static boolean isClassObjectWithBackingFieldsInOuter(@NotNull DeclarationDescriptor classObject) {
611            return isPropertyWithSpecialBackingField(classObject, ClassKind.CLASS);
612        }
613    
614        private static boolean areBothAccessorDefault(@NotNull PropertyDescriptor propertyDescriptor) {
615            return isAccessorWithEmptyBody(propertyDescriptor.getGetter())
616                   && (!propertyDescriptor.isVar() || isAccessorWithEmptyBody(propertyDescriptor.getSetter()));
617        }
618    
619        private static boolean isAccessorWithEmptyBody(@Nullable PropertyAccessorDescriptor accessorDescriptor) {
620            return accessorDescriptor == null || !accessorDescriptor.hasBody();
621        }
622    
623        private static boolean isPropertyWithSpecialBackingField(@NotNull DeclarationDescriptor classObject, ClassKind kind) {
624            return isClassObject(classObject) && isKindOf(classObject.getContainingDeclaration(), kind);
625        }
626    
627        public static Type comparisonOperandType(Type left, Type right) {
628            if (left == Type.DOUBLE_TYPE || right == Type.DOUBLE_TYPE) return Type.DOUBLE_TYPE;
629            if (left == Type.FLOAT_TYPE || right == Type.FLOAT_TYPE) return Type.FLOAT_TYPE;
630            if (left == Type.LONG_TYPE || right == Type.LONG_TYPE) return Type.LONG_TYPE;
631            return Type.INT_TYPE;
632        }
633    
634        @NotNull
635        public static Type numberFunctionOperandType(@NotNull Type expectedType) {
636            if (expectedType == Type.SHORT_TYPE || expectedType == Type.BYTE_TYPE) {
637                return Type.INT_TYPE;
638            }
639            return expectedType;
640        }
641    
642        public static void pop(@NotNull InstructionAdapter v, @NotNull Type type) {
643            if (type.getSize() == 2) {
644                v.pop2();
645            }
646            else {
647                v.pop();
648            }
649        }
650    
651        public static void dup(@NotNull InstructionAdapter v, @NotNull Type type) {
652            if (type.getSize() == 2) {
653                v.dup2();
654            }
655            else {
656                v.dup();
657            }
658        }
659    
660        @NotNull
661        public static String asmDescByFqNameWithoutInnerClasses(@NotNull FqName fqName) {
662            return asmTypeByFqNameWithoutInnerClasses(fqName).getDescriptor();
663        }
664    
665        @NotNull
666        public static String shortNameByAsmType(@NotNull Type type) {
667            String internalName = type.getInternalName();
668            int lastSlash = internalName.lastIndexOf('/');
669            return lastSlash < 0 ? internalName : internalName.substring(lastSlash + 1);
670        }
671    
672        @NotNull
673        public static Type asmTypeByFqNameWithoutInnerClasses(@NotNull FqName fqName) {
674            return Type.getObjectType(JvmClassName.byFqNameWithoutInnerClasses(fqName).getInternalName());
675        }
676    }