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 kotlin.Function1;
024    import kotlin.Unit;
025    import org.jetbrains.annotations.NotNull;
026    import org.jetbrains.annotations.Nullable;
027    import org.jetbrains.jet.codegen.binding.CalculatedClosure;
028    import org.jetbrains.jet.codegen.binding.CodegenBinding;
029    import org.jetbrains.jet.codegen.context.CodegenContext;
030    import org.jetbrains.jet.codegen.intrinsics.IntrinsicMethods;
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.psi.JetFile;
035    import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils;
036    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
037    import org.jetbrains.jet.lang.resolve.annotations.AnnotationsPackage;
038    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
039    import org.jetbrains.jet.lang.resolve.java.*;
040    import org.jetbrains.jet.lang.resolve.java.descriptor.JavaCallableMemberDescriptor;
041    import org.jetbrains.jet.lang.resolve.java.diagnostics.JvmDeclarationOrigin;
042    import org.jetbrains.jet.lang.resolve.kotlin.PackagePartClassUtils;
043    import org.jetbrains.jet.lang.resolve.name.FqName;
044    import org.jetbrains.jet.lang.types.Approximation;
045    import org.jetbrains.jet.lang.types.JetType;
046    import org.jetbrains.jet.lang.types.TypesPackage;
047    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
048    import org.jetbrains.jet.lexer.JetTokens;
049    import org.jetbrains.org.objectweb.asm.*;
050    import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
051    import org.jetbrains.org.objectweb.asm.commons.Method;
052    
053    import java.util.ArrayList;
054    import java.util.List;
055    import java.util.Map;
056    import java.util.Set;
057    
058    import static org.jetbrains.jet.codegen.JvmCodegenUtil.isInterface;
059    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.*;
060    import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.JAVA_STRING_TYPE;
061    import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.getType;
062    import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.ABI_VERSION_FIELD_NAME;
063    import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.KotlinSyntheticClass;
064    import static org.jetbrains.jet.lang.resolve.java.mapping.PrimitiveTypesUtil.asmTypeForPrimitive;
065    import static org.jetbrains.jet.lang.types.TypeUtils.isNullableType;
066    import static org.jetbrains.org.objectweb.asm.Opcodes.*;
067    
068    public class AsmUtil {
069        private static final Set<ClassDescriptor> PRIMITIVE_NUMBER_CLASSES = Sets.newHashSet(
070                KotlinBuiltIns.getInstance().getByte(),
071                KotlinBuiltIns.getInstance().getShort(),
072                KotlinBuiltIns.getInstance().getInt(),
073                KotlinBuiltIns.getInstance().getLong(),
074                KotlinBuiltIns.getInstance().getFloat(),
075                KotlinBuiltIns.getInstance().getDouble(),
076                KotlinBuiltIns.getInstance().getChar()
077        );
078    
079        private static final Set<Type> STRING_BUILDER_OBJECT_APPEND_ARG_TYPES = Sets.newHashSet(
080                getType(String.class),
081                getType(StringBuffer.class),
082                getType(CharSequence.class)
083        );
084    
085        private static final int NO_FLAG_LOCAL = 0;
086        public static final int NO_FLAG_PACKAGE_PRIVATE = 0;
087    
088        @NotNull
089        private static final Map<Visibility, Integer> visibilityToAccessFlag = ImmutableMap.<Visibility, Integer>builder()
090                .put(Visibilities.PRIVATE, ACC_PRIVATE)
091                .put(Visibilities.PROTECTED, ACC_PROTECTED)
092                .put(JavaVisibilities.PROTECTED_STATIC_VISIBILITY, ACC_PROTECTED)
093                .put(JavaVisibilities.PROTECTED_AND_PACKAGE, ACC_PROTECTED)
094                .put(Visibilities.PUBLIC, ACC_PUBLIC)
095                .put(Visibilities.INTERNAL, ACC_PUBLIC)
096                .put(Visibilities.LOCAL, NO_FLAG_LOCAL)
097                .put(JavaVisibilities.PACKAGE_VISIBILITY, NO_FLAG_PACKAGE_PRIVATE)
098                .build();
099    
100        public static final String CAPTURED_RECEIVER_FIELD = "receiver$0";
101        public static final String CAPTURED_THIS_FIELD = "this$0";
102    
103        private static final ImmutableMap<Integer, JvmPrimitiveType> primitiveTypeByAsmSort;
104        private static final ImmutableMap<Type, Type> primitiveTypeByBoxedType;
105    
106        static {
107            ImmutableMap.Builder<Integer, JvmPrimitiveType> typeBySortBuilder = ImmutableMap.builder();
108            ImmutableMap.Builder<Type, Type> typeByWrapperBuilder = ImmutableMap.builder();
109            for (JvmPrimitiveType primitiveType : JvmPrimitiveType.values()) {
110                Type asmType = asmTypeForPrimitive(primitiveType);
111                typeBySortBuilder.put(asmType.getSort(), primitiveType);
112                typeByWrapperBuilder.put(asmTypeByFqNameWithoutInnerClasses(primitiveType.getWrapperFqName()), asmType);
113            }
114            primitiveTypeByAsmSort = typeBySortBuilder.build();
115            primitiveTypeByBoxedType = typeByWrapperBuilder.build();
116        }
117    
118        private AsmUtil() {
119        }
120    
121        @NotNull
122        public static Type boxType(@NotNull Type type) {
123            JvmPrimitiveType jvmPrimitiveType = primitiveTypeByAsmSort.get(type.getSort());
124            return jvmPrimitiveType != null ? asmTypeByFqNameWithoutInnerClasses(jvmPrimitiveType.getWrapperFqName()) : type;
125        }
126    
127        @NotNull
128        public static Type unboxType(@NotNull Type boxedType) {
129            Type primitiveType = primitiveTypeByBoxedType.get(boxedType);
130            if (primitiveType == null) {
131                throw new UnsupportedOperationException("Unboxing: " + boxedType);
132            }
133            return primitiveType;
134        }
135    
136        public static boolean isIntPrimitive(Type type) {
137            return type == Type.INT_TYPE || type == Type.SHORT_TYPE || type == Type.BYTE_TYPE || type == Type.CHAR_TYPE;
138        }
139    
140        public static boolean isNumberPrimitive(Type type) {
141            return isIntPrimitive(type) || type == Type.FLOAT_TYPE || type == Type.DOUBLE_TYPE || type == Type.LONG_TYPE;
142        }
143    
144        public static boolean isPrimitive(Type type) {
145            return type.getSort() != Type.OBJECT && type.getSort() != Type.ARRAY;
146        }
147    
148        public static boolean isPrimitiveNumberClassDescriptor(DeclarationDescriptor descriptor) {
149            if (!(descriptor instanceof ClassDescriptor)) {
150                return false;
151            }
152            return PRIMITIVE_NUMBER_CLASSES.contains(descriptor);
153        }
154    
155        public static Type correctElementType(Type type) {
156            String internalName = type.getInternalName();
157            assert internalName.charAt(0) == '[';
158            return Type.getType(internalName.substring(1));
159        }
160    
161        @NotNull
162        public static Method method(@NotNull String name, @NotNull Type returnType, @NotNull Type... parameterTypes) {
163            return new Method(name, Type.getMethodDescriptor(returnType, parameterTypes));
164        }
165    
166        public static boolean isAbstractMethod(FunctionDescriptor functionDescriptor, OwnerKind kind) {
167            return (functionDescriptor.getModality() == Modality.ABSTRACT
168                    || isInterface(functionDescriptor.getContainingDeclaration()))
169                   && !isStaticMethod(kind, functionDescriptor);
170        }
171    
172        public static boolean isStaticMethod(OwnerKind kind, CallableMemberDescriptor functionDescriptor) {
173            return isStaticKind(kind) ||
174                   JetTypeMapper.isAccessor(functionDescriptor) ||
175                   AnnotationsPackage.isPlatformStaticInObject(functionDescriptor);
176        }
177    
178        public static boolean isStaticKind(OwnerKind kind) {
179            return kind == OwnerKind.PACKAGE || kind == OwnerKind.TRAIT_IMPL;
180        }
181    
182        public static int getMethodAsmFlags(FunctionDescriptor functionDescriptor, OwnerKind kind) {
183            int flags = getCommonCallableFlags(functionDescriptor);
184    
185            for (AnnotationCodegen.JvmFlagAnnotation flagAnnotation : AnnotationCodegen.METHOD_FLAGS) {
186                if (flagAnnotation.hasAnnotation(functionDescriptor.getOriginal())) {
187                    flags |= flagAnnotation.getJvmFlag();
188                }
189            }
190    
191            if (functionDescriptor.getModality() == Modality.FINAL && !(functionDescriptor instanceof ConstructorDescriptor)) {
192                DeclarationDescriptor containingDeclaration = functionDescriptor.getContainingDeclaration();
193                if (!(containingDeclaration instanceof ClassDescriptor) ||
194                    ((ClassDescriptor) containingDeclaration).getKind() != ClassKind.TRAIT) {
195                    flags |= ACC_FINAL;
196                }
197            }
198    
199            if (isStaticMethod(kind, functionDescriptor)) {
200                flags |= ACC_STATIC;
201            }
202    
203            if (isAbstractMethod(functionDescriptor, kind)) {
204                flags |= ACC_ABSTRACT;
205            }
206    
207            if (JetTypeMapper.isAccessor(functionDescriptor)) {
208                flags |= ACC_SYNTHETIC;
209            }
210    
211            return flags;
212        }
213    
214        private static int getCommonCallableFlags(FunctionDescriptor functionDescriptor) {
215            int flags = getVisibilityAccessFlag(functionDescriptor);
216            flags |= getVarargsFlag(functionDescriptor);
217            flags |= getDeprecatedAccessFlag(functionDescriptor);
218            return flags;
219        }
220    
221        //TODO: move mapping logic to front-end java
222        public static int getVisibilityAccessFlag(@NotNull MemberDescriptor descriptor) {
223            Integer specialCase = specialCaseVisibility(descriptor);
224            if (specialCase != null) {
225                return specialCase;
226            }
227            Integer defaultMapping = visibilityToAccessFlag.get(descriptor.getVisibility());
228            if (defaultMapping == null) {
229                throw new IllegalStateException(descriptor.getVisibility() + " is not a valid visibility in backend: " + descriptor);
230            }
231            return defaultMapping;
232        }
233    
234        /*
235            Use this method to get visibility flag for class to define it in byte code (v.defineClass method).
236            For other cases use getVisibilityAccessFlag(MemberDescriptor descriptor)
237            Classes in byte code should be public or package private
238         */
239        public static int getVisibilityAccessFlagForClass(ClassDescriptor descriptor) {
240            if (DescriptorUtils.isTopLevelDeclaration(descriptor) ||
241                descriptor.getVisibility() == Visibilities.PUBLIC ||
242                descriptor.getVisibility() == Visibilities.INTERNAL) {
243                return ACC_PUBLIC;
244            }
245            return NO_FLAG_PACKAGE_PRIVATE;
246        }
247    
248        public static int getVisibilityAccessFlagForAnonymous(@NotNull ClassDescriptor descriptor) {
249            if (isDeclarationInsideInlineFunction(descriptor)) {
250                return ACC_PUBLIC;
251            }
252            return NO_FLAG_PACKAGE_PRIVATE;
253        }
254    
255        private static boolean isDeclarationInsideInlineFunction(@NotNull ClassDescriptor descriptor) {
256            //NB: constructor context couldn't be inline
257            DeclarationDescriptor parentDeclaration = descriptor.getContainingDeclaration();
258            if (parentDeclaration instanceof SimpleFunctionDescriptor &&
259                ((SimpleFunctionDescriptor) parentDeclaration).getInlineStrategy().isInline()) {
260                return true;
261            }
262            return false;
263        }
264    
265        public static int calculateInnerClassAccessFlags(@NotNull ClassDescriptor innerClass) {
266            return getVisibilityAccessFlag(innerClass) |
267                   innerAccessFlagsForModalityAndKind(innerClass) |
268                   (innerClass.isInner() ? 0 : ACC_STATIC);
269        }
270    
271        private static int innerAccessFlagsForModalityAndKind(@NotNull ClassDescriptor innerClass) {
272            switch (innerClass.getKind()) {
273                case TRAIT:
274                    return ACC_ABSTRACT | ACC_INTERFACE;
275                case ENUM_CLASS:
276                    return ACC_FINAL | ACC_ENUM;
277                case ANNOTATION_CLASS:
278                    return ACC_ABSTRACT | ACC_ANNOTATION | ACC_INTERFACE;
279                default:
280                    if (innerClass.getModality() == Modality.FINAL) {
281                        return ACC_FINAL;
282                    }
283                    else if (innerClass.getModality() == Modality.ABSTRACT) {
284                        return ACC_ABSTRACT;
285                    }
286            }
287            return 0;
288        }
289    
290        public static int getDeprecatedAccessFlag(@NotNull MemberDescriptor descriptor) {
291            if (descriptor instanceof PropertyAccessorDescriptor) {
292                return KotlinBuiltIns.getInstance().isDeprecated(descriptor)
293                       ? ACC_DEPRECATED
294                       : getDeprecatedAccessFlag(((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty());
295            }
296            else if (KotlinBuiltIns.getInstance().isDeprecated(descriptor)) {
297                return ACC_DEPRECATED;
298            }
299            return 0;
300        }
301    
302        private static int getVarargsFlag(FunctionDescriptor functionDescriptor) {
303            if (!functionDescriptor.getValueParameters().isEmpty()
304                && functionDescriptor.getValueParameters().get(functionDescriptor.getValueParameters().size() - 1)
305                           .getVarargElementType() != null) {
306                return ACC_VARARGS;
307            }
308            return 0;
309        }
310    
311        @Nullable
312        private static Integer specialCaseVisibility(@NotNull MemberDescriptor memberDescriptor) {
313            DeclarationDescriptor containingDeclaration = memberDescriptor.getContainingDeclaration();
314            if (isInterface(containingDeclaration)) {
315                return ACC_PUBLIC;
316            }
317            Visibility memberVisibility = memberDescriptor.getVisibility();
318            if (memberVisibility == Visibilities.LOCAL && memberDescriptor instanceof CallableMemberDescriptor) {
319                return ACC_PUBLIC;
320            }
321            if (isEnumEntry(memberDescriptor)) {
322                return NO_FLAG_PACKAGE_PRIVATE;
323            }
324            if (memberDescriptor instanceof ConstructorDescriptor && isAnonymousObject(memberDescriptor.getContainingDeclaration())) {
325                return NO_FLAG_PACKAGE_PRIVATE;
326            }
327            if (memberVisibility != Visibilities.PRIVATE) {
328                return null;
329            }
330            // the following code is only for PRIVATE visibility of member
331            if (memberDescriptor instanceof ConstructorDescriptor) {
332                ClassKind kind = ((ClassDescriptor) containingDeclaration).getKind();
333                if (kind == ClassKind.OBJECT || kind == ClassKind.ENUM_ENTRY) {
334                    return NO_FLAG_PACKAGE_PRIVATE;
335                }
336                if (kind == ClassKind.ENUM_CLASS) {
337                    //TODO: should be ACC_PRIVATE
338                    // see http://youtrack.jetbrains.com/issue/KT-2680
339                    return ACC_PROTECTED;
340                }
341            }
342            if (containingDeclaration instanceof PackageFragmentDescriptor) {
343                return ACC_PUBLIC;
344            }
345            return null;
346        }
347    
348        private static Type stringValueOfType(Type type) {
349            int sort = type.getSort();
350            return sort == Type.OBJECT || sort == Type.ARRAY
351                   ? AsmTypeConstants.OBJECT_TYPE
352                   : sort == Type.BYTE || sort == Type.SHORT ? Type.INT_TYPE : type;
353        }
354    
355        private static Type stringBuilderAppendType(Type type) {
356            switch (type.getSort()) {
357                case Type.OBJECT:
358                    return STRING_BUILDER_OBJECT_APPEND_ARG_TYPES.contains(type) ? type : AsmTypeConstants.OBJECT_TYPE;
359                case Type.ARRAY:
360                    return AsmTypeConstants.OBJECT_TYPE;
361                case Type.BYTE:
362                case Type.SHORT:
363                    return Type.INT_TYPE;
364                default:
365                    return type;
366            }
367        }
368    
369        public static void genThrow(@NotNull InstructionAdapter v, @NotNull String exception, @Nullable String message) {
370            v.anew(Type.getObjectType(exception));
371            v.dup();
372            if (message != null) {
373                v.aconst(message);
374                v.invokespecial(exception, "<init>", "(Ljava/lang/String;)V", false);
375            }
376            else {
377                v.invokespecial(exception, "<init>", "()V", false);
378            }
379            v.athrow();
380        }
381    
382        public static void genClosureFields(CalculatedClosure closure, ClassBuilder v, JetTypeMapper typeMapper) {
383            List<Pair<String, Type>> allFields = new ArrayList<Pair<String, Type>>();
384    
385            ClassifierDescriptor captureThis = closure.getCaptureThis();
386            if (captureThis != null) {
387                allFields.add(Pair.create(CAPTURED_THIS_FIELD, typeMapper.mapType(captureThis)));
388            }
389    
390            JetType captureReceiverType = closure.getCaptureReceiverType();
391            if (captureReceiverType != null) {
392                allFields.add(Pair.create(CAPTURED_RECEIVER_FIELD, typeMapper.mapType(captureReceiverType)));
393            }
394    
395            allFields.addAll(closure.getRecordedFields());
396            genClosureFields(allFields, v);
397        }
398    
399        public static void genClosureFields(List<Pair<String, Type>> allFields, ClassBuilder builder) {
400            //noinspection PointlessBitwiseExpression
401            int access = NO_FLAG_PACKAGE_PRIVATE | ACC_SYNTHETIC | ACC_FINAL;
402            for (Pair<String, Type> field : allFields) {
403                builder.newField(JvmDeclarationOrigin.NO_ORIGIN, access, field.first, field.second.getDescriptor(), null, null);
404            }
405        }
406    
407        public static List<FieldInfo> transformCapturedParams(List<Pair<String, Type>> allFields, Type owner) {
408            List<FieldInfo> result = new ArrayList<FieldInfo>();
409            for (Pair<String, Type> field : allFields) {
410                result.add(FieldInfo.createForHiddenField(owner, field.second, field.first));
411            }
412            return result;
413        }
414    
415        public static int genAssignInstanceFieldFromParam(FieldInfo info, int index, InstructionAdapter iv) {
416            assert !info.isStatic();
417            Type fieldType = info.getFieldType();
418            iv.load(0, info.getOwnerType());//this
419            iv.load(index, fieldType); //param
420            iv.visitFieldInsn(PUTFIELD, info.getOwnerInternalName(), info.getFieldName(), fieldType.getDescriptor());
421            index += fieldType.getSize();
422            return index;
423        }
424    
425        public static void genStringBuilderConstructor(InstructionAdapter v) {
426            v.visitTypeInsn(NEW, "java/lang/StringBuilder");
427            v.dup();
428            v.invokespecial("java/lang/StringBuilder", "<init>", "()V", false);
429        }
430    
431        public static void genInvokeAppendMethod(InstructionAdapter v, Type type) {
432            type = stringBuilderAppendType(type);
433            v.invokevirtual("java/lang/StringBuilder", "append", "(" + type.getDescriptor() + ")Ljava/lang/StringBuilder;", false);
434        }
435    
436        public static StackValue genToString(final StackValue receiver, final Type receiverType) {
437            return StackValue.operation(JAVA_STRING_TYPE, new Function1<InstructionAdapter, Unit>() {
438                @Override
439                public Unit invoke(InstructionAdapter v) {
440                    Type type = stringValueOfType(receiverType);
441                    receiver.put(type, v);
442                    v.invokestatic("java/lang/String", "valueOf", "(" + type.getDescriptor() + ")Ljava/lang/String;", false);
443                    return null;
444                }
445            });
446        }
447    
448        static void genHashCode(MethodVisitor mv, InstructionAdapter iv, Type type) {
449            if (type.getSort() == Type.ARRAY) {
450                Type elementType = correctElementType(type);
451                if (elementType.getSort() == Type.OBJECT || elementType.getSort() == Type.ARRAY) {
452                    iv.invokestatic("java/util/Arrays", "hashCode", "([Ljava/lang/Object;)I", false);
453                }
454                else {
455                    iv.invokestatic("java/util/Arrays", "hashCode", "(" + type.getDescriptor() + ")I", false);
456                }
457            }
458            else if (type.getSort() == Type.OBJECT) {
459                iv.invokevirtual("java/lang/Object", "hashCode", "()I", false);
460            }
461            else if (type.getSort() == Type.LONG) {
462                genLongHashCode(mv, iv);
463            }
464            else if (type.getSort() == Type.DOUBLE) {
465                iv.invokestatic("java/lang/Double", "doubleToLongBits", "(D)J", false);
466                genLongHashCode(mv, iv);
467            }
468            else if (type.getSort() == Type.FLOAT) {
469                iv.invokestatic("java/lang/Float", "floatToIntBits", "(F)I", false);
470            }
471            else if (type.getSort() == Type.BOOLEAN) {
472                Label end = new Label();
473                iv.dup();
474                iv.ifeq(end);
475                iv.pop();
476                iv.iconst(1);
477                iv.mark(end);
478            }
479            else { // byte short char int
480                // do nothing
481            }
482        }
483    
484        private static void genLongHashCode(MethodVisitor mv, InstructionAdapter iv) {
485            iv.dup2();
486            iv.iconst(32);
487            iv.ushr(Type.LONG_TYPE);
488            iv.xor(Type.LONG_TYPE);
489            mv.visitInsn(L2I);
490        }
491    
492        static void genInvertBoolean(InstructionAdapter v) {
493            v.iconst(1);
494            v.xor(Type.INT_TYPE);
495        }
496    
497        @NotNull
498        public static StackValue genEqualsForExpressionsOnStack(
499                final @NotNull IElementType opToken,
500                final @NotNull StackValue left,
501                final @NotNull StackValue right
502        ) {
503            final Type leftType = left.type;
504            final Type rightType = right.type;
505            if (isPrimitive(leftType) && leftType == rightType) {
506                return StackValue.cmp(opToken, leftType, left, right);
507            }
508    
509            if (opToken == JetTokens.EQEQEQ || opToken == JetTokens.EXCLEQEQEQ) {
510                return StackValue.cmp(opToken, leftType, left, right);
511            }
512    
513            return StackValue.operation(Type.BOOLEAN_TYPE, new Function1<InstructionAdapter, Unit>() {
514                @Override
515                public Unit invoke(InstructionAdapter v) {
516                    left.put(leftType, v);
517                    right.put(rightType, v);
518                    v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "areEqual", "(Ljava/lang/Object;Ljava/lang/Object;)Z", false);
519    
520                    if (opToken == JetTokens.EXCLEQ || opToken == JetTokens.EXCLEQEQEQ) {
521                        genInvertBoolean(v);
522                    }
523                    return Unit.INSTANCE$;
524                }
525            });
526        }
527    
528        public static void genIncrement(Type expectedType, int myDelta, InstructionAdapter v) {
529            if (expectedType == Type.LONG_TYPE) {
530                v.lconst(myDelta);
531            }
532            else if (expectedType == Type.FLOAT_TYPE) {
533                v.fconst(myDelta);
534            }
535            else if (expectedType == Type.DOUBLE_TYPE) {
536                v.dconst(myDelta);
537            }
538            else {
539                v.iconst(myDelta);
540                v.add(Type.INT_TYPE);
541                StackValue.coerce(Type.INT_TYPE, expectedType, v);
542                return;
543            }
544            v.add(expectedType);
545        }
546    
547        public static void swap(InstructionAdapter v, Type stackTop, Type afterTop) {
548            if (stackTop.getSize() == 1) {
549                if (afterTop.getSize() == 1) {
550                    v.swap();
551                }
552                else {
553                    v.dupX2();
554                    v.pop();
555                }
556            }
557            else {
558                if (afterTop.getSize() == 1) {
559                    v.dup2X1();
560                }
561                else {
562                    v.dup2X2();
563                }
564                v.pop2();
565            }
566        }
567    
568        public static void genNotNullAssertionsForParameters(
569                @NotNull InstructionAdapter v,
570                @NotNull GenerationState state,
571                @NotNull FunctionDescriptor descriptor,
572                @NotNull FrameMap frameMap
573        ) {
574            if (!state.isParamAssertionsEnabled()) return;
575    
576            // Private method is not accessible from other classes, no assertions needed
577            if (getVisibilityAccessFlag(descriptor) == ACC_PRIVATE) return;
578    
579            for (ValueParameterDescriptor parameter : descriptor.getValueParameters()) {
580                JetType type = parameter.getReturnType();
581                if (type == null || isNullableType(type)) continue;
582    
583                int index = frameMap.getIndex(parameter);
584                Type asmType = state.getTypeMapper().mapType(type);
585                if (asmType.getSort() == Type.OBJECT || asmType.getSort() == Type.ARRAY) {
586                    v.load(index, asmType);
587                    v.visitLdcInsn(parameter.getName().asString());
588                    v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "checkParameterIsNotNull",
589                                   "(Ljava/lang/Object;Ljava/lang/String;)V", false);
590                }
591            }
592        }
593    
594        public static void genNotNullAssertionForField(
595                @NotNull InstructionAdapter v,
596                @NotNull GenerationState state,
597                @NotNull PropertyDescriptor descriptor
598        ) {
599            genNotNullAssertion(v, state, descriptor, "checkFieldIsNotNull");
600        }
601    
602        public static void genNotNullAssertionForMethod(
603                @NotNull InstructionAdapter v,
604                @NotNull GenerationState state,
605                @NotNull ResolvedCall resolvedCall
606        ) {
607            CallableDescriptor descriptor = resolvedCall.getResultingDescriptor();
608            if (descriptor instanceof ConstructorDescriptor) return;
609    
610            genNotNullAssertion(v, state, descriptor, "checkReturnedValueIsNotNull");
611        }
612    
613        private static void genNotNullAssertion(
614                @NotNull InstructionAdapter v,
615                @NotNull GenerationState state,
616                @NotNull CallableDescriptor descriptor,
617                @NotNull String assertMethodToCall
618        ) {
619            // Assertions are generated elsewhere for platform types
620            if (JavaPackage.getPLATFORM_TYPES()) return;
621    
622            if (!state.isCallAssertionsEnabled()) return;
623    
624            if (!isDeclaredInJava(descriptor)) return;
625    
626            JetType type = descriptor.getReturnType();
627            if (type == null || isNullableType(TypesPackage.lowerIfFlexible(type))) return;
628    
629            Type asmType = state.getTypeMapper().mapReturnType(descriptor);
630            if (asmType.getSort() == Type.OBJECT || asmType.getSort() == Type.ARRAY) {
631                v.dup();
632                v.visitLdcInsn(descriptor.getContainingDeclaration().getName().asString());
633                v.visitLdcInsn(descriptor.getName().asString());
634                v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, assertMethodToCall,
635                               "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V", false);
636            }
637        }
638    
639        @NotNull
640        public static StackValue genNotNullAssertions(
641                @NotNull GenerationState state,
642                @NotNull final StackValue stackValue,
643                @Nullable final Approximation.Info approximationInfo
644        ) {
645            if (!state.isCallAssertionsEnabled()) return stackValue;
646            if (approximationInfo == null || !TypesPackage.assertNotNull(approximationInfo)) return stackValue;
647    
648            return new StackValue(stackValue.type) {
649    
650                @Override
651                public void putSelector(@NotNull Type type, @NotNull InstructionAdapter v) {
652                    stackValue.put(type, v);
653                    if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
654                        v.dup();
655                        v.visitLdcInsn(approximationInfo.getMessage());
656                        v.invokestatic("kotlin/jvm/internal/Intrinsics", "checkExpressionValueIsNotNull",
657                                       "(Ljava/lang/Object;Ljava/lang/String;)V", false);
658                    }
659                }
660            };
661        }
662    
663        private static boolean isDeclaredInJava(@NotNull CallableDescriptor callableDescriptor) {
664            CallableDescriptor descriptor = callableDescriptor;
665            while (true) {
666                if (descriptor instanceof JavaCallableMemberDescriptor) {
667                    return true;
668                }
669                CallableDescriptor original = descriptor.getOriginal();
670                if (descriptor == original) break;
671                descriptor = original;
672            }
673            return false;
674        }
675    
676        public static void pushDefaultValueOnStack(@NotNull Type type, @NotNull InstructionAdapter v) {
677            if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
678                v.aconst(null);
679            }
680            else {
681                pushDefaultPrimitiveValueOnStack(type, v);
682            }
683        }
684    
685        public static void pushDefaultPrimitiveValueOnStack(@NotNull Type type, @NotNull InstructionAdapter v) {
686            if (type.getSort() == Type.FLOAT) {
687                v.fconst(0);
688            }
689            else if (type.getSort() == Type.DOUBLE) {
690                v.dconst(0);
691            }
692            else if (type.getSort() == Type.LONG) {
693                v.lconst(0);
694            }
695            else {
696                v.iconst(0);
697            }
698        }
699    
700        public static boolean isInstancePropertyWithStaticBackingField(@NotNull PropertyDescriptor propertyDescriptor) {
701            if (propertyDescriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
702                return false;
703            }
704    
705            return isObject(propertyDescriptor.getContainingDeclaration()) || isPropertyWithBackingFieldInOuterClass(propertyDescriptor);
706        }
707    
708        public static boolean isPropertyWithBackingFieldInOuterClass(@NotNull PropertyDescriptor propertyDescriptor) {
709            return propertyDescriptor.getKind() != CallableMemberDescriptor.Kind.FAKE_OVERRIDE &&
710                   isClassObjectWithBackingFieldsInOuter(propertyDescriptor.getContainingDeclaration());
711        }
712    
713        public static int getVisibilityForSpecialPropertyBackingField(@NotNull PropertyDescriptor propertyDescriptor, boolean isDelegate) {
714            boolean isExtensionProperty = propertyDescriptor.getExtensionReceiverParameter() != null;
715            if (isDelegate || isExtensionProperty) {
716                return ACC_PRIVATE;
717            }
718            else {
719                return areBothAccessorDefault(propertyDescriptor)
720                       ? getVisibilityAccessFlag(descriptorForVisibility(propertyDescriptor))
721                       : ACC_PRIVATE;
722            }
723        }
724    
725        private static MemberDescriptor descriptorForVisibility(@NotNull PropertyDescriptor propertyDescriptor) {
726            if (!propertyDescriptor.isVar()) {
727                return propertyDescriptor;
728            }
729            else {
730                return propertyDescriptor.getSetter() != null ? propertyDescriptor.getSetter() : propertyDescriptor;
731            }
732        }
733    
734        public static boolean isPropertyWithBackingFieldCopyInOuterClass(@NotNull PropertyDescriptor propertyDescriptor) {
735            boolean isExtensionProperty = propertyDescriptor.getExtensionReceiverParameter() != null;
736            DeclarationDescriptor propertyContainer = propertyDescriptor.getContainingDeclaration();
737            return !propertyDescriptor.isVar()
738                   && !isExtensionProperty
739                   && isClassObject(propertyContainer) && isTrait(propertyContainer.getContainingDeclaration())
740                   && areBothAccessorDefault(propertyDescriptor)
741                   && getVisibilityForSpecialPropertyBackingField(propertyDescriptor, false) == ACC_PUBLIC;
742        }
743    
744        public static boolean isClassObjectWithBackingFieldsInOuter(@NotNull DeclarationDescriptor classObject) {
745            DeclarationDescriptor containingClass = classObject.getContainingDeclaration();
746            return isClassObject(classObject) && (isClass(containingClass) || isEnumClass(containingClass));
747        }
748    
749        private static boolean areBothAccessorDefault(@NotNull PropertyDescriptor propertyDescriptor) {
750            return isAccessorWithEmptyBody(propertyDescriptor.getGetter())
751                   && (!propertyDescriptor.isVar() || isAccessorWithEmptyBody(propertyDescriptor.getSetter()));
752        }
753    
754        private static boolean isAccessorWithEmptyBody(@Nullable PropertyAccessorDescriptor accessorDescriptor) {
755            return accessorDescriptor == null || !accessorDescriptor.hasBody();
756        }
757    
758        public static Type comparisonOperandType(Type left, Type right) {
759            if (left == Type.DOUBLE_TYPE || right == Type.DOUBLE_TYPE) return Type.DOUBLE_TYPE;
760            if (left == Type.FLOAT_TYPE || right == Type.FLOAT_TYPE) return Type.FLOAT_TYPE;
761            if (left == Type.LONG_TYPE || right == Type.LONG_TYPE) return Type.LONG_TYPE;
762            return Type.INT_TYPE;
763        }
764    
765        @NotNull
766        public static Type numberFunctionOperandType(@NotNull Type expectedType) {
767            if (expectedType == Type.SHORT_TYPE || expectedType == Type.BYTE_TYPE || expectedType == Type.CHAR_TYPE) {
768                return Type.INT_TYPE;
769            }
770            return expectedType;
771        }
772    
773        public static void pop(@NotNull MethodVisitor v, @NotNull Type type) {
774            if (type.getSize() == 2) {
775                v.visitInsn(Opcodes.POP2);
776            }
777            else {
778                v.visitInsn(Opcodes.POP);
779            }
780        }
781    
782        public static void dup(@NotNull InstructionAdapter v, @NotNull Type type) {
783            int size = type.getSize();
784            if (size == 2) {
785                v.dup2();
786            }
787            else if (size == 1) {
788                v.dup();
789            }
790            else {
791                throw new UnsupportedOperationException();
792            }
793        }
794    
795        public static void writeKotlinSyntheticClassAnnotation(@NotNull ClassBuilder v, @NotNull KotlinSyntheticClass.Kind kind) {
796            AnnotationVisitor av = v.newAnnotation(Type.getObjectType(KotlinSyntheticClass.CLASS_NAME.getInternalName()).getDescriptor(), true);
797            av.visit(ABI_VERSION_FIELD_NAME, JvmAbi.VERSION);
798            av.visitEnum(KotlinSyntheticClass.KIND_FIELD_NAME.asString(),
799                         Type.getObjectType(KotlinSyntheticClass.KIND_INTERNAL_NAME).getDescriptor(),
800                         kind.toString());
801            av.visitEnd();
802        }
803    
804        @NotNull
805        public static String asmDescByFqNameWithoutInnerClasses(@NotNull FqName fqName) {
806            return asmTypeByFqNameWithoutInnerClasses(fqName).getDescriptor();
807        }
808    
809        @NotNull
810        public static String shortNameByAsmType(@NotNull Type type) {
811            String internalName = type.getInternalName();
812            int lastSlash = internalName.lastIndexOf('/');
813            return lastSlash < 0 ? internalName : internalName.substring(lastSlash + 1);
814        }
815    
816        @NotNull
817        public static Type asmTypeByFqNameWithoutInnerClasses(@NotNull FqName fqName) {
818            return Type.getObjectType(internalNameByFqNameWithoutInnerClasses(fqName));
819        }
820    
821        @NotNull
822        public static String internalNameByFqNameWithoutInnerClasses(@NotNull FqName fqName) {
823            return JvmClassName.byFqNameWithoutInnerClasses(fqName).getInternalName();
824        }
825    
826        public static void writeOuterClassAndEnclosingMethod(
827                @NotNull ClassDescriptor descriptor,
828                @NotNull DeclarationDescriptor originalDescriptor,
829                @NotNull JetTypeMapper typeMapper,
830                @NotNull ClassBuilder v
831        ) {
832            String outerClassName = getOuterClassName(descriptor, originalDescriptor, typeMapper);
833            FunctionDescriptor function = DescriptorUtils.getParentOfType(descriptor, FunctionDescriptor.class);
834    
835            if (function != null) {
836                Method method = typeMapper.mapSignature(function).getAsmMethod();
837                v.visitOuterClass(outerClassName, method.getName(), method.getDescriptor());
838            }
839            else {
840                v.visitOuterClass(outerClassName, null, null);
841            }
842        }
843    
844        @NotNull
845        private static String getOuterClassName(
846                @NotNull ClassDescriptor classDescriptor,
847                @NotNull DeclarationDescriptor originalDescriptor,
848                @NotNull JetTypeMapper typeMapper
849        ) {
850            DeclarationDescriptor container = classDescriptor.getContainingDeclaration();
851            while (container != null) {
852                if (container instanceof ClassDescriptor) {
853                    return typeMapper.mapClass((ClassDescriptor) container).getInternalName();
854                }
855                else if (CodegenBinding.isLocalFunOrLambda(container)) {
856                    ClassDescriptor descriptor =
857                            CodegenBinding.anonymousClassForFunction(typeMapper.getBindingContext(), (FunctionDescriptor) container);
858                    return typeMapper.mapClass(descriptor).getInternalName();
859                }
860    
861                container = container.getContainingDeclaration();
862            }
863    
864            JetFile containingFile = DescriptorToSourceUtils.getContainingFile(originalDescriptor);
865            assert containingFile != null : "Containing file should be present for " + classDescriptor;
866            return PackagePartClassUtils.getPackagePartInternalName(containingFile);
867        }
868    
869        public static void putJavaLangClassInstance(@NotNull InstructionAdapter v, @NotNull Type type) {
870            if (isPrimitive(type)) {
871                v.getstatic(boxType(type).getInternalName(), "TYPE", "Ljava/lang/Class;");
872            }
873            else {
874                v.aconst(type);
875            }
876        }
877    
878        public static int getReceiverIndex(@NotNull CodegenContext context, @NotNull CallableMemberDescriptor descriptor) {
879            OwnerKind kind = context.getContextKind();
880            //Trait always should have this descriptor
881            return kind != OwnerKind.TRAIT_IMPL && isStaticMethod(kind, descriptor) ? 0 : 1;
882        }
883    }