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