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.AnnotationVisitor;
026    import org.jetbrains.asm4.Label;
027    import org.jetbrains.asm4.MethodVisitor;
028    import org.jetbrains.asm4.Type;
029    import org.jetbrains.asm4.commons.InstructionAdapter;
030    import org.jetbrains.jet.codegen.binding.CalculatedClosure;
031    import org.jetbrains.jet.codegen.state.GenerationState;
032    import org.jetbrains.jet.codegen.state.JetTypeMapper;
033    import org.jetbrains.jet.lang.descriptors.*;
034    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
035    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
036    import org.jetbrains.jet.lang.resolve.java.*;
037    import org.jetbrains.jet.lang.resolve.java.descriptor.JavaCallableMemberDescriptor;
038    import org.jetbrains.jet.lang.resolve.name.FqName;
039    import org.jetbrains.jet.lang.types.JetType;
040    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
041    import org.jetbrains.jet.lexer.JetTokens;
042    
043    import java.util.ArrayList;
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    import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.ABI_VERSION_FIELD_NAME;
053    import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.KotlinSyntheticClass;
054    import static org.jetbrains.jet.lang.resolve.java.mapping.PrimitiveTypesUtil.asmTypeForPrimitive;
055    
056    public class AsmUtil {
057        private static final Set<ClassDescriptor> PRIMITIVE_NUMBER_CLASSES = Sets.newHashSet(
058                KotlinBuiltIns.getInstance().getByte(),
059                KotlinBuiltIns.getInstance().getShort(),
060                KotlinBuiltIns.getInstance().getInt(),
061                KotlinBuiltIns.getInstance().getLong(),
062                KotlinBuiltIns.getInstance().getFloat(),
063                KotlinBuiltIns.getInstance().getDouble(),
064                KotlinBuiltIns.getInstance().getChar()
065        );
066    
067        private static final int NO_FLAG_LOCAL = 0;
068        public static final int NO_FLAG_PACKAGE_PRIVATE = 0;
069    
070        @NotNull
071        private static final Map<Visibility, Integer> visibilityToAccessFlag = ImmutableMap.<Visibility, Integer>builder()
072                .put(Visibilities.PRIVATE, ACC_PRIVATE)
073                .put(Visibilities.PROTECTED, ACC_PROTECTED)
074                .put(JavaVisibilities.PROTECTED_STATIC_VISIBILITY, ACC_PROTECTED)
075                .put(JavaVisibilities.PROTECTED_AND_PACKAGE, ACC_PROTECTED)
076                .put(Visibilities.PUBLIC, ACC_PUBLIC)
077                .put(Visibilities.INTERNAL, ACC_PUBLIC)
078                .put(Visibilities.LOCAL, NO_FLAG_LOCAL)
079                .put(JavaVisibilities.PACKAGE_VISIBILITY, NO_FLAG_PACKAGE_PRIVATE)
080                .build();
081    
082        public static final String CAPTURED_RECEIVER_FIELD = "receiver$0";
083        public static final String CAPTURED_THIS_FIELD = "this$0";
084    
085        private static final ImmutableMap<Integer, JvmPrimitiveType> primitiveTypeByAsmSort;
086        private static final ImmutableMap<Type, Type> primitiveTypeByBoxedType;
087    
088        static {
089            ImmutableMap.Builder<Integer, JvmPrimitiveType> typeBySortBuilder = ImmutableMap.builder();
090            ImmutableMap.Builder<Type, Type> typeByWrapperBuilder = ImmutableMap.builder();
091            for (JvmPrimitiveType primitiveType : JvmPrimitiveType.values()) {
092                Type asmType = asmTypeForPrimitive(primitiveType);
093                typeBySortBuilder.put(asmType.getSort(), primitiveType);
094                typeByWrapperBuilder.put(asmTypeByFqNameWithoutInnerClasses(primitiveType.getWrapperFqName()), asmType);
095            }
096            primitiveTypeByAsmSort = typeBySortBuilder.build();
097            primitiveTypeByBoxedType = typeByWrapperBuilder.build();
098        }
099    
100        private AsmUtil() {
101        }
102    
103        @NotNull
104        public static Type boxType(@NotNull Type type) {
105            JvmPrimitiveType jvmPrimitiveType = primitiveTypeByAsmSort.get(type.getSort());
106            return jvmPrimitiveType != null ? asmTypeByFqNameWithoutInnerClasses(jvmPrimitiveType.getWrapperFqName()) : type;
107        }
108    
109        @NotNull
110        public static Type unboxType(@NotNull Type boxedType) {
111            Type primitiveType = primitiveTypeByBoxedType.get(boxedType);
112            if (primitiveType == null) {
113                throw new UnsupportedOperationException("Unboxing: " + boxedType);
114            }
115            return primitiveType;
116        }
117    
118        public static boolean isIntPrimitive(Type type) {
119            return type == Type.INT_TYPE || type == Type.SHORT_TYPE || type == Type.BYTE_TYPE || type == Type.CHAR_TYPE;
120        }
121    
122        public static boolean isNumberPrimitive(Type type) {
123            return isIntPrimitive(type) || type == Type.FLOAT_TYPE || type == Type.DOUBLE_TYPE || type == Type.LONG_TYPE;
124        }
125    
126        public static boolean isPrimitive(Type type) {
127            return type.getSort() != Type.OBJECT && type.getSort() != Type.ARRAY;
128        }
129    
130        public static boolean isPrimitiveNumberClassDescriptor(DeclarationDescriptor descriptor) {
131            if (!(descriptor instanceof ClassDescriptor)) {
132                return false;
133            }
134            return PRIMITIVE_NUMBER_CLASSES.contains(descriptor);
135        }
136    
137        public static Type correctElementType(Type type) {
138            String internalName = type.getInternalName();
139            assert internalName.charAt(0) == '[';
140            return Type.getType(internalName.substring(1));
141        }
142    
143        public static boolean isAbstractMethod(FunctionDescriptor functionDescriptor, OwnerKind kind) {
144            return (functionDescriptor.getModality() == Modality.ABSTRACT
145                    || isInterface(functionDescriptor.getContainingDeclaration()))
146                   && !isStaticMethod(kind, functionDescriptor);
147        }
148    
149        public static boolean isStaticMethod(OwnerKind kind, FunctionDescriptor functionDescriptor) {
150            return isStatic(kind) || JetTypeMapper.isAccessor(functionDescriptor);
151        }
152    
153        public static boolean isStatic(OwnerKind kind) {
154            return kind == OwnerKind.PACKAGE || kind == OwnerKind.TRAIT_IMPL;
155        }
156    
157        public static int getMethodAsmFlags(FunctionDescriptor functionDescriptor, OwnerKind kind) {
158            int flags = getCommonCallableFlags(functionDescriptor);
159    
160            if (functionDescriptor.getModality() == Modality.FINAL && !(functionDescriptor instanceof ConstructorDescriptor)) {
161                DeclarationDescriptor containingDeclaration = functionDescriptor.getContainingDeclaration();
162                if (!(containingDeclaration instanceof ClassDescriptor) ||
163                    ((ClassDescriptor) containingDeclaration).getKind() != ClassKind.TRAIT) {
164                    flags |= ACC_FINAL;
165                }
166            }
167    
168            if (isStaticMethod(kind, functionDescriptor)) {
169                flags |= ACC_STATIC;
170            }
171    
172            if (isAbstractMethod(functionDescriptor, kind)) {
173                flags |= ACC_ABSTRACT;
174            }
175    
176            if (JetTypeMapper.isAccessor(functionDescriptor)) {
177                flags |= ACC_SYNTHETIC;
178            }
179    
180            return flags;
181        }
182    
183        private static int getCommonCallableFlags(FunctionDescriptor functionDescriptor) {
184            int flags = getVisibilityAccessFlag(functionDescriptor);
185            flags |= getVarargsFlag(functionDescriptor);
186            flags |= getDeprecatedAccessFlag(functionDescriptor);
187            return flags;
188        }
189    
190        //TODO: move mapping logic to front-end java
191        public static int getVisibilityAccessFlag(@NotNull MemberDescriptor descriptor) {
192            Integer specialCase = specialCaseVisibility(descriptor);
193            if (specialCase != null) {
194                return specialCase;
195            }
196            Integer defaultMapping = visibilityToAccessFlag.get(descriptor.getVisibility());
197            if (defaultMapping == null) {
198                throw new IllegalStateException(descriptor.getVisibility() + " is not a valid visibility in backend. Descriptor: " + descriptor);
199            }
200            return defaultMapping;
201        }
202    
203        /*
204            Use this method to get visibility flag for class to define it in byte code (v.defineClass method).
205            For other cases use getVisibilityAccessFlag(MemberDescriptor descriptor)
206            Classes in byte code should be public or package private
207         */
208        public static int getVisibilityAccessFlagForClass(ClassDescriptor descriptor) {
209            if (DescriptorUtils.isTopLevelDeclaration(descriptor) ||
210                descriptor.getVisibility() == Visibilities.PUBLIC ||
211                descriptor.getVisibility() == Visibilities.INTERNAL) {
212                return ACC_PUBLIC;
213            }
214            return NO_FLAG_PACKAGE_PRIVATE;
215        }
216    
217        public static int getDeprecatedAccessFlag(@NotNull MemberDescriptor descriptor) {
218            if (descriptor instanceof PropertyAccessorDescriptor) {
219                return KotlinBuiltIns.getInstance().isDeprecated(descriptor)
220                         ? ACC_DEPRECATED
221                         : getDeprecatedAccessFlag(((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty());
222            }
223            else if (KotlinBuiltIns.getInstance().isDeprecated(descriptor)) {
224                return ACC_DEPRECATED;
225            }
226            return 0;
227        }
228    
229        private static int getVarargsFlag(FunctionDescriptor functionDescriptor) {
230            if (!functionDescriptor.getValueParameters().isEmpty()
231                && functionDescriptor.getValueParameters().get(functionDescriptor.getValueParameters().size() - 1)
232                           .getVarargElementType() != null) {
233                return ACC_VARARGS;
234            }
235            return 0;
236        }
237    
238        @Nullable
239        private static Integer specialCaseVisibility(@NotNull MemberDescriptor memberDescriptor) {
240            DeclarationDescriptor containingDeclaration = memberDescriptor.getContainingDeclaration();
241            if (isInterface(containingDeclaration)) {
242                return ACC_PUBLIC;
243            }
244            Visibility memberVisibility = memberDescriptor.getVisibility();
245            if (memberVisibility == Visibilities.LOCAL && memberDescriptor instanceof CallableMemberDescriptor) {
246                return ACC_PUBLIC;
247            }
248            if (isEnumEntry(memberDescriptor)) {
249                return NO_FLAG_PACKAGE_PRIVATE;
250            }
251            if (memberVisibility != Visibilities.PRIVATE) {
252                return null;
253            }
254            // the following code is only for PRIVATE visibility of member
255            if (memberDescriptor instanceof ConstructorDescriptor) {
256                if (isAnonymousObject(containingDeclaration)) {
257                    return NO_FLAG_PACKAGE_PRIVATE;
258                }
259    
260                ClassKind kind = ((ClassDescriptor) containingDeclaration).getKind();
261                if (kind == ClassKind.OBJECT) {
262                    return NO_FLAG_PACKAGE_PRIVATE;
263                }
264                else if (kind == ClassKind.ENUM_ENTRY) {
265                    return NO_FLAG_PACKAGE_PRIVATE;
266                }
267                else if (kind == ClassKind.ENUM_CLASS) {
268                    //TODO: should be ACC_PRIVATE
269                    // see http://youtrack.jetbrains.com/issue/KT-2680
270                    return ACC_PROTECTED;
271                }
272            }
273            if (containingDeclaration instanceof PackageFragmentDescriptor) {
274                return ACC_PUBLIC;
275            }
276            return null;
277        }
278    
279        @NotNull
280        public static Type getTraitImplThisParameterType(@NotNull ClassDescriptor traitDescriptor, @NotNull JetTypeMapper typeMapper) {
281            JetType jetType = getSuperClass(traitDescriptor);
282            Type type = typeMapper.mapType(jetType);
283            if (type.getInternalName().equals("java/lang/Object")) {
284                return typeMapper.mapType(traitDescriptor.getDefaultType());
285            }
286            return type;
287        }
288    
289        private static Type stringValueOfOrStringBuilderAppendType(Type type) {
290            int sort = type.getSort();
291            return sort == Type.OBJECT || sort == Type.ARRAY
292                       ? AsmTypeConstants.OBJECT_TYPE
293                       : sort == Type.BYTE || sort == Type.SHORT ? Type.INT_TYPE : type;
294        }
295    
296        public static void genThrow(@NotNull MethodVisitor mv, @NotNull String exception, @NotNull String message) {
297            InstructionAdapter iv = new InstructionAdapter(mv);
298            iv.anew(Type.getObjectType(exception));
299            iv.dup();
300            iv.aconst(message);
301            iv.invokespecial(exception, "<init>", "(Ljava/lang/String;)V");
302            iv.athrow();
303        }
304    
305        public static void genClosureFields(CalculatedClosure closure, ClassBuilder v, JetTypeMapper typeMapper) {
306            List<Pair<String, Type>> allFields = new ArrayList<Pair<String, Type>>();
307    
308            ClassifierDescriptor captureThis = closure.getCaptureThis();
309            if (captureThis != null) {
310                allFields.add(Pair.create(CAPTURED_THIS_FIELD, typeMapper.mapType(captureThis)));
311            }
312    
313            JetType captureReceiverType = closure.getCaptureReceiverType();
314            if (captureReceiverType != null) {
315                allFields.add(Pair.create(CAPTURED_RECEIVER_FIELD, typeMapper.mapType(captureReceiverType)));
316            }
317    
318            allFields.addAll(closure.getRecordedFields());
319            genClosureFields(allFields, v);
320        }
321    
322        public static void genClosureFields(List<Pair<String, Type>> allFields, ClassBuilder builder) {
323            //noinspection PointlessBitwiseExpression
324            int access = NO_FLAG_PACKAGE_PRIVATE | ACC_SYNTHETIC | ACC_FINAL;
325            for (Pair<String, Type> field : allFields) {
326                builder.newField(null, access, field.first, field.second.getDescriptor(), null, null);
327            }
328        }
329    
330        public static List<FieldInfo> transformCapturedParams(List<Pair<String, Type>> allFields, Type owner) {
331            List<FieldInfo> result = new ArrayList<FieldInfo>();
332            for (Pair<String, Type> field : allFields) {
333                result.add(FieldInfo.createForHiddenField(owner, field.second, field.first));
334            }
335            return result;
336        }
337    
338        public static int genAssignInstanceFieldFromParam(FieldInfo info, int index, InstructionAdapter iv) {
339            assert !info.isStatic();
340            Type fieldType = info.getFieldType();
341            iv.load(0, info.getOwnerType());//this
342            iv.load(index, fieldType); //param
343            iv.visitFieldInsn(PUTFIELD, info.getOwnerInternalName(), info.getFieldName(), fieldType.getDescriptor());
344            index += fieldType.getSize();
345            return index;
346        }
347    
348        public static void genStringBuilderConstructor(InstructionAdapter v) {
349            v.visitTypeInsn(NEW, "java/lang/StringBuilder");
350            v.dup();
351            v.invokespecial("java/lang/StringBuilder", "<init>", "()V");
352        }
353    
354        public static void genInvokeAppendMethod(InstructionAdapter v, Type type) {
355            type = stringValueOfOrStringBuilderAppendType(type);
356            v.invokevirtual("java/lang/StringBuilder", "append", "(" + type.getDescriptor() + ")Ljava/lang/StringBuilder;");
357        }
358    
359        public static StackValue genToString(InstructionAdapter v, StackValue receiver, Type receiverType) {
360            Type type = stringValueOfOrStringBuilderAppendType(receiverType);
361            receiver.put(type, v);
362            v.invokestatic("java/lang/String", "valueOf", "(" + type.getDescriptor() + ")Ljava/lang/String;");
363            return StackValue.onStack(JAVA_STRING_TYPE);
364        }
365    
366        static void genHashCode(MethodVisitor mv, InstructionAdapter iv, Type type) {
367            if (type.getSort() == Type.ARRAY) {
368                Type elementType = correctElementType(type);
369                if (elementType.getSort() == Type.OBJECT || elementType.getSort() == Type.ARRAY) {
370                    iv.invokestatic("java/util/Arrays", "hashCode", "([Ljava/lang/Object;)I");
371                }
372                else {
373                    iv.invokestatic("java/util/Arrays", "hashCode", "(" + type.getDescriptor() + ")I");
374                }
375            }
376            else if (type.getSort() == Type.OBJECT) {
377                iv.invokevirtual("java/lang/Object", "hashCode", "()I");
378            }
379            else if (type.getSort() == Type.LONG) {
380                genLongHashCode(mv, iv);
381            }
382            else if (type.getSort() == Type.DOUBLE) {
383                iv.invokestatic("java/lang/Double", "doubleToLongBits", "(D)J");
384                genLongHashCode(mv, iv);
385            }
386            else if (type.getSort() == Type.FLOAT) {
387                iv.invokestatic("java/lang/Float", "floatToIntBits", "(F)I");
388            }
389            else if (type.getSort() == Type.BOOLEAN) {
390                Label end = new Label();
391                iv.dup();
392                iv.ifeq(end);
393                iv.pop();
394                iv.iconst(1);
395                iv.mark(end);
396            }
397            else { // byte short char int
398                // do nothing
399            }
400        }
401    
402        private static void genLongHashCode(MethodVisitor mv, InstructionAdapter iv) {
403            iv.dup2();
404            iv.iconst(32);
405            iv.ushr(Type.LONG_TYPE);
406            iv.xor(Type.LONG_TYPE);
407            mv.visitInsn(L2I);
408        }
409    
410        static void genInvertBoolean(InstructionAdapter v) {
411            v.iconst(1);
412            v.xor(Type.INT_TYPE);
413        }
414    
415        public static StackValue genEqualsForExpressionsOnStack(
416                InstructionAdapter v,
417                IElementType opToken,
418                Type leftType,
419                Type rightType
420        ) {
421            if ((isNumberPrimitive(leftType) || leftType.getSort() == Type.BOOLEAN) && leftType == rightType) {
422                return StackValue.cmp(opToken, leftType);
423            }
424            else {
425                if (opToken == JetTokens.EQEQEQ || opToken == JetTokens.EXCLEQEQEQ) {
426                    return StackValue.cmp(opToken, leftType);
427                }
428                else {
429                    v.invokestatic("kotlin/jvm/internal/Intrinsics", "areEqual", "(Ljava/lang/Object;Ljava/lang/Object;)Z");
430    
431                    if (opToken == JetTokens.EXCLEQ || opToken == JetTokens.EXCLEQEQEQ) {
432                        genInvertBoolean(v);
433                    }
434    
435                    return StackValue.onStack(Type.BOOLEAN_TYPE);
436                }
437            }
438        }
439    
440        public static void genIncrement(Type expectedType, int myDelta, InstructionAdapter v) {
441            if (expectedType == Type.LONG_TYPE) {
442                v.lconst(myDelta);
443            }
444            else if (expectedType == Type.FLOAT_TYPE) {
445                v.fconst(myDelta);
446            }
447            else if (expectedType == Type.DOUBLE_TYPE) {
448                v.dconst(myDelta);
449            }
450            else {
451                v.iconst(myDelta);
452                v.add(Type.INT_TYPE);
453                StackValue.coerce(Type.INT_TYPE, expectedType, v);
454                return;
455            }
456            v.add(expectedType);
457        }
458    
459        public static Type genNegate(Type expectedType, InstructionAdapter v) {
460            if (expectedType == Type.BYTE_TYPE || expectedType == Type.SHORT_TYPE || expectedType == Type.CHAR_TYPE) {
461                expectedType = Type.INT_TYPE;
462            }
463            v.neg(expectedType);
464            return expectedType;
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().mapType(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("kotlin/jvm/internal/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(descriptor);
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("kotlin/jvm/internal/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        @NotNull
642        public static Type numberFunctionOperandType(@NotNull Type expectedType) {
643            if (expectedType == Type.SHORT_TYPE || expectedType == Type.BYTE_TYPE) {
644                return Type.INT_TYPE;
645            }
646            return expectedType;
647        }
648    
649        public static void pop(@NotNull InstructionAdapter v, @NotNull Type type) {
650            if (type.getSize() == 2) {
651                v.pop2();
652            }
653            else {
654                v.pop();
655            }
656        }
657    
658        public static void dup(@NotNull InstructionAdapter v, @NotNull Type type) {
659            if (type.getSize() == 2) {
660                v.dup2();
661            }
662            else {
663                v.dup();
664            }
665        }
666    
667        public static void writeKotlinSyntheticClassAnnotation(@NotNull ClassBuilder v, @NotNull KotlinSyntheticClass.Kind kind) {
668            AnnotationVisitor av = v.newAnnotation(Type.getObjectType(KotlinSyntheticClass.CLASS_NAME.getInternalName()).getDescriptor(), true);
669            av.visit(ABI_VERSION_FIELD_NAME, JvmAbi.VERSION);
670            av.visitEnum(KotlinSyntheticClass.KIND_FIELD_NAME.asString(),
671                         Type.getObjectType(KotlinSyntheticClass.KIND_INTERNAL_NAME).getDescriptor(),
672                         kind.toString());
673            av.visitEnd();
674        }
675    
676        @NotNull
677        public static String asmDescByFqNameWithoutInnerClasses(@NotNull FqName fqName) {
678            return asmTypeByFqNameWithoutInnerClasses(fqName).getDescriptor();
679        }
680    
681        @NotNull
682        public static String shortNameByAsmType(@NotNull Type type) {
683            String internalName = type.getInternalName();
684            int lastSlash = internalName.lastIndexOf('/');
685            return lastSlash < 0 ? internalName : internalName.substring(lastSlash + 1);
686        }
687    
688        @NotNull
689        public static Type asmTypeByFqNameWithoutInnerClasses(@NotNull FqName fqName) {
690            return Type.getObjectType(JvmClassName.byFqNameWithoutInnerClasses(fqName).getInternalName());
691        }
692    }