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.");
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 (memberVisibility != Visibilities.PRIVATE) {
248                return null;
249            }
250            // the following code is only for PRIVATE visibility of member
251            if (isEnumEntry(memberDescriptor)) {
252                return NO_FLAG_PACKAGE_PRIVATE;
253            }
254            if (memberDescriptor instanceof ConstructorDescriptor) {
255                ClassKind kind = ((ClassDescriptor) containingDeclaration).getKind();
256                if (kind == ClassKind.OBJECT) {
257                    return NO_FLAG_PACKAGE_PRIVATE;
258                }
259                else if (kind == ClassKind.ENUM_ENTRY) {
260                    return NO_FLAG_PACKAGE_PRIVATE;
261                }
262                else if (kind == ClassKind.ENUM_CLASS) {
263                    //TODO: should be ACC_PRIVATE
264                    // see http://youtrack.jetbrains.com/issue/KT-2680
265                    return ACC_PROTECTED;
266                }
267            }
268            if (containingDeclaration instanceof NamespaceDescriptor) {
269                return ACC_PUBLIC;
270            }
271            return null;
272        }
273    
274        @NotNull
275        public static Type getTraitImplThisParameterType(@NotNull ClassDescriptor traitDescriptor, @NotNull JetTypeMapper typeMapper) {
276            JetType jetType = getSuperClass(traitDescriptor);
277            Type type = typeMapper.mapType(jetType);
278            if (type.getInternalName().equals("java/lang/Object")) {
279                return typeMapper.mapType(traitDescriptor.getDefaultType());
280            }
281            return type;
282        }
283    
284        private static Type stringValueOfOrStringBuilderAppendType(Type type) {
285            int sort = type.getSort();
286            return sort == Type.OBJECT || sort == Type.ARRAY
287                       ? AsmTypeConstants.OBJECT_TYPE
288                       : sort == Type.BYTE || sort == Type.SHORT ? Type.INT_TYPE : type;
289        }
290    
291        public static void genThrow(MethodVisitor mv, String exception, String message) {
292            InstructionAdapter iv = new InstructionAdapter(mv);
293            iv.anew(Type.getObjectType(exception));
294            iv.dup();
295            iv.aconst(message);
296            iv.invokespecial(exception, "<init>", "(Ljava/lang/String;)V");
297            iv.athrow();
298        }
299    
300        public static void genMethodThrow(MethodVisitor mv, String exception, String message) {
301            mv.visitCode();
302            genThrow(mv, exception, message);
303            mv.visitMaxs(-1, -1);
304            mv.visitEnd();
305        }
306    
307        public static void genClosureFields(CalculatedClosure closure, ClassBuilder v, JetTypeMapper typeMapper) {
308            ClassifierDescriptor captureThis = closure.getCaptureThis();
309            int access = NO_FLAG_PACKAGE_PRIVATE | ACC_SYNTHETIC | ACC_FINAL;
310            if (captureThis != null) {
311                v.newField(null, access, CAPTURED_THIS_FIELD, typeMapper.mapType(captureThis).getDescriptor(), null,
312                           null);
313            }
314    
315            JetType captureReceiverType = closure.getCaptureReceiverType();
316            if (captureReceiverType != null) {
317                v.newField(null, access, CAPTURED_RECEIVER_FIELD, typeMapper.mapType(captureReceiverType).getDescriptor(),
318                           null, null);
319            }
320    
321            List<Pair<String, Type>> fields = closure.getRecordedFields();
322            for (Pair<String, Type> field : fields) {
323                v.newField(null, access, field.first, field.second.getDescriptor(), null, null);
324            }
325        }
326    
327        public static int genAssignInstanceFieldFromParam(FieldInfo info, int index, InstructionAdapter iv) {
328            assert !info.isStatic();
329            Type fieldType = info.getFieldType();
330            iv.load(0, info.getOwnerType());//this
331            iv.load(index, fieldType); //param
332            iv.visitFieldInsn(PUTFIELD, info.getOwnerInternalName(), info.getFieldName(), fieldType.getDescriptor());
333            index += fieldType.getSize();
334            return index;
335        }
336    
337        public static void genStringBuilderConstructor(InstructionAdapter v) {
338            v.visitTypeInsn(NEW, "java/lang/StringBuilder");
339            v.dup();
340            v.invokespecial("java/lang/StringBuilder", "<init>", "()V");
341        }
342    
343        public static void genInvokeAppendMethod(InstructionAdapter v, Type type) {
344            type = stringValueOfOrStringBuilderAppendType(type);
345            v.invokevirtual("java/lang/StringBuilder", "append", "(" + type.getDescriptor() + ")Ljava/lang/StringBuilder;");
346        }
347    
348        public static StackValue genToString(InstructionAdapter v, StackValue receiver, Type receiverType) {
349            Type type = stringValueOfOrStringBuilderAppendType(receiverType);
350            receiver.put(type, v);
351            v.invokestatic("java/lang/String", "valueOf", "(" + type.getDescriptor() + ")Ljava/lang/String;");
352            return StackValue.onStack(JAVA_STRING_TYPE);
353        }
354    
355        static void genHashCode(MethodVisitor mv, InstructionAdapter iv, Type type) {
356            if (type.getSort() == Type.ARRAY) {
357                Type elementType = correctElementType(type);
358                if (elementType.getSort() == Type.OBJECT || elementType.getSort() == Type.ARRAY) {
359                    iv.invokestatic("java/util/Arrays", "hashCode", "([Ljava/lang/Object;)I");
360                }
361                else {
362                    iv.invokestatic("java/util/Arrays", "hashCode", "(" + type.getDescriptor() + ")I");
363                }
364            }
365            else if (type.getSort() == Type.OBJECT) {
366                iv.invokevirtual("java/lang/Object", "hashCode", "()I");
367            }
368            else if (type.getSort() == Type.LONG) {
369                genLongHashCode(mv, iv);
370            }
371            else if (type.getSort() == Type.DOUBLE) {
372                iv.invokestatic("java/lang/Double", "doubleToLongBits", "(D)J");
373                genLongHashCode(mv, iv);
374            }
375            else if (type.getSort() == Type.FLOAT) {
376                iv.invokestatic("java/lang/Float", "floatToIntBits", "(F)I");
377            }
378            else if (type.getSort() == Type.BOOLEAN) {
379                Label end = new Label();
380                iv.dup();
381                iv.ifeq(end);
382                iv.pop();
383                iv.iconst(1);
384                iv.mark(end);
385            }
386            else { // byte short char int
387                // do nothing
388            }
389        }
390    
391        private static void genLongHashCode(MethodVisitor mv, InstructionAdapter iv) {
392            iv.dup2();
393            iv.iconst(32);
394            iv.ushr(Type.LONG_TYPE);
395            iv.xor(Type.LONG_TYPE);
396            mv.visitInsn(L2I);
397        }
398    
399        static void genInvertBoolean(InstructionAdapter v) {
400            v.iconst(1);
401            v.xor(Type.INT_TYPE);
402        }
403    
404        public static StackValue genEqualsForExpressionsOnStack(
405                InstructionAdapter v,
406                IElementType opToken,
407                Type leftType,
408                Type rightType
409        ) {
410            if ((isNumberPrimitive(leftType) || leftType.getSort() == Type.BOOLEAN) && leftType == rightType) {
411                return StackValue.cmp(opToken, leftType);
412            }
413            else {
414                if (opToken == JetTokens.EQEQEQ || opToken == JetTokens.EXCLEQEQEQ) {
415                    return StackValue.cmp(opToken, leftType);
416                }
417                else {
418                    v.invokestatic("jet/runtime/Intrinsics", "areEqual", "(Ljava/lang/Object;Ljava/lang/Object;)Z");
419    
420                    if (opToken == JetTokens.EXCLEQ || opToken == JetTokens.EXCLEQEQEQ) {
421                        genInvertBoolean(v);
422                    }
423    
424                    return StackValue.onStack(Type.BOOLEAN_TYPE);
425                }
426            }
427        }
428    
429        public static void genIncrement(Type expectedType, int myDelta, InstructionAdapter v) {
430            if (expectedType == Type.LONG_TYPE) {
431                v.lconst(myDelta);
432            }
433            else if (expectedType == Type.FLOAT_TYPE) {
434                v.fconst(myDelta);
435            }
436            else if (expectedType == Type.DOUBLE_TYPE) {
437                v.dconst(myDelta);
438            }
439            else {
440                v.iconst(myDelta);
441                v.add(Type.INT_TYPE);
442                StackValue.coerce(Type.INT_TYPE, expectedType, v);
443                return;
444            }
445            v.add(expectedType);
446        }
447    
448        public static Type genNegate(Type expectedType, InstructionAdapter v) {
449            if (expectedType == Type.BYTE_TYPE || expectedType == Type.SHORT_TYPE || expectedType == Type.CHAR_TYPE) {
450                expectedType = Type.INT_TYPE;
451            }
452            v.neg(expectedType);
453            return expectedType;
454        }
455    
456        public static void swap(InstructionAdapter v, Type stackTop, Type afterTop) {
457            if (stackTop.getSize() == 1) {
458                if (afterTop.getSize() == 1) {
459                    v.swap();
460                } else {
461                    v.dupX2();
462                    v.pop();
463                }
464            } else {
465                if (afterTop.getSize() == 1) {
466                    v.dup2X1();
467                } else {
468                    v.dup2X2();
469                }
470                v.pop2();
471            }
472        }
473    
474        public static void genNotNullAssertionsForParameters(
475                @NotNull InstructionAdapter v,
476                @NotNull GenerationState state,
477                @NotNull FunctionDescriptor descriptor,
478                @NotNull FrameMap frameMap
479        ) {
480            if (!state.isGenerateNotNullParamAssertions()) return;
481    
482            // Private method is not accessible from other classes, no assertions needed
483            if (getVisibilityAccessFlag(descriptor) == ACC_PRIVATE) return;
484    
485            for (ValueParameterDescriptor parameter : descriptor.getValueParameters()) {
486                JetType type = parameter.getReturnType();
487                if (type == null || isNullableType(type)) continue;
488    
489                int index = frameMap.getIndex(parameter);
490                Type asmType = state.getTypeMapper().mapReturnType(type);
491                if (asmType.getSort() == Type.OBJECT || asmType.getSort() == Type.ARRAY) {
492                    v.load(index, asmType);
493                    v.visitLdcInsn(parameter.getName().asString());
494                    v.invokestatic("jet/runtime/Intrinsics", "checkParameterIsNotNull", "(Ljava/lang/Object;Ljava/lang/String;)V");
495                }
496            }
497        }
498    
499        public static void genNotNullAssertionForField(
500                @NotNull InstructionAdapter v,
501                @NotNull GenerationState state,
502                @NotNull PropertyDescriptor descriptor
503        ) {
504            genNotNullAssertion(v, state, descriptor, "checkFieldIsNotNull");
505        }
506    
507        public static void genNotNullAssertionForMethod(
508                @NotNull InstructionAdapter v,
509                @NotNull GenerationState state,
510                @NotNull ResolvedCall resolvedCall
511        ) {
512            CallableDescriptor descriptor = resolvedCall.getResultingDescriptor();
513            if (descriptor instanceof ConstructorDescriptor) return;
514    
515            genNotNullAssertion(v, state, descriptor, "checkReturnedValueIsNotNull");
516        }
517    
518        private static void genNotNullAssertion(
519                @NotNull InstructionAdapter v,
520                @NotNull GenerationState state,
521                @NotNull CallableDescriptor descriptor,
522                @NotNull String assertMethodToCall
523        ) {
524            if (!state.isGenerateNotNullAssertions()) return;
525    
526            if (!isDeclaredInJava(descriptor)) return;
527    
528            JetType type = descriptor.getReturnType();
529            if (type == null || isNullableType(type)) return;
530    
531            Type asmType = state.getTypeMapper().mapReturnType(type);
532            if (asmType.getSort() == Type.OBJECT || asmType.getSort() == Type.ARRAY) {
533                v.dup();
534                v.visitLdcInsn(descriptor.getContainingDeclaration().getName().asString());
535                v.visitLdcInsn(descriptor.getName().asString());
536                v.invokestatic("jet/runtime/Intrinsics", assertMethodToCall, "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V");
537            }
538        }
539    
540        private static boolean isDeclaredInJava(@NotNull CallableDescriptor callableDescriptor) {
541            CallableDescriptor descriptor = callableDescriptor;
542            while (true) {
543                if (descriptor instanceof JavaCallableMemberDescriptor) {
544                    return true;
545                }
546                CallableDescriptor original = descriptor.getOriginal();
547                if (descriptor == original) break;
548                descriptor = original;
549            }
550            return false;
551        }
552    
553        public static void pushDefaultValueOnStack(@NotNull Type type, @NotNull InstructionAdapter v) {
554            if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
555                v.aconst(null);
556            }
557            else {
558                pushDefaultPrimitiveValueOnStack(type, v);
559            }
560        }
561    
562        public static void pushDefaultPrimitiveValueOnStack(@NotNull Type type, @NotNull InstructionAdapter v) {
563            if (type.getSort() == Type.FLOAT) {
564                v.fconst(0);
565            }
566            else if (type.getSort() == Type.DOUBLE) {
567                v.dconst(0);
568            }
569            else if (type.getSort() == Type.LONG) {
570                v.lconst(0);
571            }
572            else {
573                v.iconst(0);
574            }
575        }
576    
577        public static boolean isPropertyWithBackingFieldInOuterClass(@NotNull PropertyDescriptor propertyDescriptor) {
578            return isPropertyWithSpecialBackingField(propertyDescriptor.getContainingDeclaration(), ClassKind.CLASS);
579        }
580    
581        public static int getVisibilityForSpecialPropertyBackingField(@NotNull PropertyDescriptor propertyDescriptor, boolean isDelegate) {
582            boolean isExtensionProperty = propertyDescriptor.getReceiverParameter() != null;
583            if (isDelegate || isExtensionProperty) {
584                return ACC_PRIVATE;
585            } else {
586                return areBothAccessorDefault(propertyDescriptor) ?  getVisibilityAccessFlag(descriptorForVisibility(propertyDescriptor)) : ACC_PRIVATE;
587            }
588        }
589    
590        private static MemberDescriptor descriptorForVisibility(@NotNull PropertyDescriptor propertyDescriptor) {
591            if (!propertyDescriptor.isVar() ) {
592                return propertyDescriptor;
593            } else {
594                return propertyDescriptor.getSetter() != null ? propertyDescriptor.getSetter() : propertyDescriptor;
595            }
596        }
597    
598        public static boolean isPropertyWithBackingFieldCopyInOuterClass(@NotNull PropertyDescriptor propertyDescriptor) {
599            boolean isExtensionProperty = propertyDescriptor.getReceiverParameter() != null;
600            return !propertyDescriptor.isVar() && !isExtensionProperty
601                   && isPropertyWithSpecialBackingField(propertyDescriptor.getContainingDeclaration(), ClassKind.TRAIT)
602                   && areBothAccessorDefault(propertyDescriptor)
603                   && getVisibilityForSpecialPropertyBackingField(propertyDescriptor, false) == ACC_PUBLIC;
604        }
605    
606        public static boolean isClassObjectWithBackingFieldsInOuter(@NotNull DeclarationDescriptor classObject) {
607            return isPropertyWithSpecialBackingField(classObject, ClassKind.CLASS);
608        }
609    
610        private static boolean areBothAccessorDefault(@NotNull PropertyDescriptor propertyDescriptor) {
611            return isAccessorWithEmptyBody(propertyDescriptor.getGetter())
612                   && (!propertyDescriptor.isVar() || isAccessorWithEmptyBody(propertyDescriptor.getSetter()));
613        }
614    
615        private static boolean isAccessorWithEmptyBody(@Nullable PropertyAccessorDescriptor accessorDescriptor) {
616            return accessorDescriptor == null || !accessorDescriptor.hasBody();
617        }
618    
619        private static boolean isPropertyWithSpecialBackingField(@NotNull DeclarationDescriptor classObject, ClassKind kind) {
620            return isClassObject(classObject) && isKindOf(classObject.getContainingDeclaration(), kind);
621        }
622    
623        public static Type comparisonOperandType(Type left, Type right) {
624            if (left == Type.DOUBLE_TYPE || right == Type.DOUBLE_TYPE) return Type.DOUBLE_TYPE;
625            if (left == Type.FLOAT_TYPE || right == Type.FLOAT_TYPE) return Type.FLOAT_TYPE;
626            if (left == Type.LONG_TYPE || right == Type.LONG_TYPE) return Type.LONG_TYPE;
627            return Type.INT_TYPE;
628        }
629    
630        public static void pop(@NotNull InstructionAdapter v, @NotNull Type type) {
631            if (type.getSize() == 2) {
632                v.pop2();
633            }
634            else {
635                v.pop();
636            }
637        }
638    
639        public static void dup(@NotNull InstructionAdapter v, @NotNull Type type) {
640            if (type.getSize() == 2) {
641                v.dup2();
642            }
643            else {
644                v.dup();
645            }
646        }
647    
648        @NotNull
649        public static String asmDescByFqNameWithoutInnerClasses(@NotNull FqName fqName) {
650            return asmTypeByFqNameWithoutInnerClasses(fqName).getDescriptor();
651        }
652    
653        @NotNull
654        public static String shortNameByAsmType(@NotNull Type type) {
655            String internalName = type.getInternalName();
656            int lastSlash = internalName.lastIndexOf('/');
657            return lastSlash < 0 ? internalName : internalName.substring(lastSlash + 1);
658        }
659    
660        @NotNull
661        public static Type asmTypeByFqNameWithoutInnerClasses(@NotNull FqName fqName) {
662            return Type.getObjectType(JvmClassName.byFqNameWithoutInnerClasses(fqName).getInternalName());
663        }
664    }