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