001    /*
002     * Copyright 2010-2015 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.kotlin.codegen.inline;
018    
019    import com.intellij.openapi.vfs.VirtualFile;
020    import com.intellij.psi.PsiFile;
021    import kotlin.text.StringsKt;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.kotlin.backend.common.output.OutputFile;
025    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
026    import org.jetbrains.kotlin.codegen.AsmUtil;
027    import org.jetbrains.kotlin.codegen.ExpressionCodegen;
028    import org.jetbrains.kotlin.codegen.MemberCodegen;
029    import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
030    import org.jetbrains.kotlin.codegen.context.CodegenContext;
031    import org.jetbrains.kotlin.codegen.context.CodegenContextUtil;
032    import org.jetbrains.kotlin.codegen.context.InlineLambdaContext;
033    import org.jetbrains.kotlin.codegen.context.MethodContext;
034    import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt;
035    import org.jetbrains.kotlin.codegen.state.GenerationState;
036    import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
037    import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping;
038    import org.jetbrains.kotlin.descriptors.*;
039    import org.jetbrains.kotlin.fileClasses.FileClasses;
040    import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider;
041    import org.jetbrains.kotlin.load.java.JvmAbi;
042    import org.jetbrains.kotlin.load.kotlin.JvmVirtualFileFinder;
043    import org.jetbrains.kotlin.name.ClassId;
044    import org.jetbrains.kotlin.name.FqName;
045    import org.jetbrains.kotlin.name.Name;
046    import org.jetbrains.kotlin.psi.KtFile;
047    import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
048    import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
049    import org.jetbrains.kotlin.resolve.jvm.JvmClassName;
050    import org.jetbrains.kotlin.types.KotlinType;
051    import org.jetbrains.kotlin.util.OperatorNameConventions;
052    import org.jetbrains.org.objectweb.asm.*;
053    import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
054    import org.jetbrains.org.objectweb.asm.tree.*;
055    import org.jetbrains.org.objectweb.asm.util.Textifier;
056    import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor;
057    
058    import java.io.IOException;
059    import java.io.PrintWriter;
060    import java.io.StringWriter;
061    import java.util.List;
062    import java.util.ListIterator;
063    
064    import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.ENUM_TYPE;
065    import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.JAVA_CLASS_TYPE;
066    
067    public class InlineCodegenUtil {
068        public static final boolean GENERATE_SMAP = true;
069        public static final int API = Opcodes.ASM5;
070    
071        private static final String CAPTURED_FIELD_PREFIX = "$";
072        private static final String NON_CAPTURED_FIELD_PREFIX = "$$";
073        public static final String THIS$0 = "this$0";
074        public static final String THIS = "this";
075        private static final String RECEIVER$0 = "receiver$0";
076        private static final String NON_LOCAL_RETURN = "$$$$$NON_LOCAL_RETURN$$$$$";
077        public static final String FIRST_FUN_LABEL = "$$$$$ROOT$$$$$";
078        public static final String NUMBERED_FUNCTION_PREFIX = "kotlin/jvm/functions/Function";
079        private static final String INLINE_MARKER_CLASS_NAME = "kotlin/jvm/internal/InlineMarker";
080        private static final String INLINE_MARKER_BEFORE_METHOD_NAME = "beforeInlineCall";
081        private static final String INLINE_MARKER_AFTER_METHOD_NAME = "afterInlineCall";
082        private static final String INLINE_MARKER_FINALLY_START = "finallyStart";
083        private static final String INLINE_MARKER_FINALLY_END = "finallyEnd";
084        public static final String SPECIAL_TRANSFORMATION_NAME = "$special";
085        public static final String INLINE_TRANSFORMATION_SUFFIX = "$inlined";
086        public static final String INLINE_CALL_TRANSFORMATION_SUFFIX = "$" + INLINE_TRANSFORMATION_SUFFIX;
087        public static final String INLINE_FUN_THIS_0_SUFFIX = "$inline_fun";
088        public static final String INLINE_FUN_VAR_SUFFIX = "$iv";
089    
090        @Nullable
091        public static SMAPAndMethodNode getMethodNode(
092                byte[] classData,
093                final String methodName,
094                final String methodDescriptor,
095                ClassId classId,
096                final @NotNull GenerationState state
097        ) {
098            ClassReader cr = new ClassReader(classData);
099            final MethodNode[] node = new MethodNode[1];
100            final String[] debugInfo = new String[2];
101            final int[] lines = new int[2];
102            lines[0] = Integer.MAX_VALUE;
103            lines[1] = Integer.MIN_VALUE;
104            //noinspection PointlessBitwiseExpression
105            cr.accept(new ClassVisitor(API) {
106                @Override
107                public void visit(int version, int access, @NotNull String name, String signature, String superName, String[] interfaces) {
108                    assertVersionNotGreaterThanGeneratedOne(version, name, state);
109                }
110    
111                @Override
112                public void visitSource(String source, String debug) {
113                    super.visitSource(source, debug);
114                    debugInfo[0] = source;
115                    debugInfo[1] = debug;
116                }
117    
118                @Override
119                public MethodVisitor visitMethod(
120                        int access,
121                        @NotNull String name,
122                        @NotNull String desc,
123                        String signature,
124                        String[] exceptions
125                ) {
126                    if (methodName.equals(name) && methodDescriptor.equals(desc)) {
127                        node[0] = new MethodNode(API, access, name, desc, signature, exceptions) {
128                            @Override
129                            public void visitLineNumber(int line, @NotNull Label start) {
130                                super.visitLineNumber(line, start);
131                                lines[0] = Math.min(lines[0], line);
132                                lines[1] = Math.max(lines[1], line);
133                            }
134                        };
135                        return node[0];
136                    }
137                    return null;
138                }
139            }, ClassReader.SKIP_FRAMES | (GENERATE_SMAP ? 0 : ClassReader.SKIP_DEBUG));
140    
141            if (node[0] == null) {
142                return null;
143            }
144    
145            if (classId.equals(IntrinsicArrayConstructorsKt.getClassId())) {
146                // Don't load source map for intrinsic array constructors
147                debugInfo[0] = null;
148            }
149    
150            SMAP smap = SMAPParser.parseOrCreateDefault(debugInfo[1], debugInfo[0], classId.asString(), lines[0], lines[1]);
151            return new SMAPAndMethodNode(node[0], smap);
152        }
153    
154        public static void assertVersionNotGreaterThanGeneratedOne(int version, String internalName, @NotNull GenerationState state) {
155            // TODO: report a proper diagnostic
156            if (version > state.getClassFileVersion() && !"true".equals(System.getProperty("kotlin.skip.bytecode.version.check"))) {
157                throw new UnsupportedOperationException(
158                        "Cannot inline bytecode of class " + internalName + " which has version " + version + ". " +
159                        "This compiler can only inline Java 1.6 bytecode (version " + Opcodes.V1_6 + ")"
160                );
161            }
162        }
163    
164        public static void initDefaultSourceMappingIfNeeded(
165                @NotNull CodegenContext context, @NotNull MemberCodegen codegen, @NotNull GenerationState state
166        ) {
167            if (state.isInlineDisabled()) return;
168    
169            CodegenContext<?> parentContext = context.getParentContext();
170            while (parentContext != null) {
171                if (parentContext.isInlineMethodContext()) {
172                    //just init default one to one mapping
173                    codegen.getOrCreateSourceMapper();
174                    break;
175                }
176                parentContext = parentContext.getParentContext();
177            }
178        }
179    
180        @Nullable
181        public static VirtualFile findVirtualFile(@NotNull GenerationState state, @NotNull ClassId classId) {
182            return JvmVirtualFileFinder.SERVICE.getInstance(state.getProject()).findVirtualFileWithHeader(classId);
183        }
184    
185        @Nullable
186        private static VirtualFile findVirtualFileImprecise(@NotNull GenerationState state, @NotNull String internalClassName) {
187            FqName packageFqName = JvmClassName.byInternalName(internalClassName).getPackageFqName();
188            String classNameWithDollars = StringsKt.substringAfterLast(internalClassName, "/", internalClassName);
189            //TODO: we cannot construct proper classId at this point, we need to read InnerClasses info from class file
190            // we construct valid.package.name/RelativeClassNameAsSingleName that should work in compiler, but fails for inner classes in IDE
191            return findVirtualFile(state, new ClassId(packageFqName, Name.identifier(classNameWithDollars)));
192        }
193    
194        @NotNull
195        public static String getInlineName(
196                @NotNull CodegenContext codegenContext,
197                @NotNull KotlinTypeMapper typeMapper,
198                @NotNull JvmFileClassesProvider fileClassesManager
199        ) {
200            return getInlineName(codegenContext, codegenContext.getContextDescriptor(), typeMapper, fileClassesManager);
201        }
202    
203        @NotNull
204        private static String getInlineName(
205                @NotNull CodegenContext codegenContext,
206                @NotNull DeclarationDescriptor currentDescriptor,
207                @NotNull KotlinTypeMapper typeMapper,
208                @NotNull JvmFileClassesProvider fileClassesProvider
209        ) {
210            if (currentDescriptor instanceof PackageFragmentDescriptor) {
211                PsiFile file = DescriptorToSourceUtils.getContainingFile(codegenContext.getContextDescriptor());
212    
213                Type implementationOwnerType;
214                if (file == null) {
215                    implementationOwnerType = CodegenContextUtil.getImplementationOwnerClassType(codegenContext);
216                }
217                else {
218                    implementationOwnerType = FileClasses.getFileClassType(fileClassesProvider, (KtFile) file);
219                }
220    
221                if (implementationOwnerType == null) {
222                    DeclarationDescriptor contextDescriptor = codegenContext.getContextDescriptor();
223                    //noinspection ConstantConditions
224                    throw new RuntimeException(
225                            "Couldn't find declaration for " +
226                            contextDescriptor.getContainingDeclaration().getName() + "." + contextDescriptor.getName() +
227                            "; context: " + codegenContext
228                    );
229                }
230    
231                return implementationOwnerType.getInternalName();
232            }
233            else if (currentDescriptor instanceof ClassifierDescriptor) {
234                Type type = typeMapper.mapType((ClassifierDescriptor) currentDescriptor);
235                return type.getInternalName();
236            }
237            else if (currentDescriptor instanceof FunctionDescriptor) {
238                ClassDescriptor descriptor =
239                        typeMapper.getBindingContext().get(CodegenBinding.CLASS_FOR_CALLABLE, (FunctionDescriptor) currentDescriptor);
240                if (descriptor != null) {
241                    return typeMapper.mapType(descriptor).getInternalName();
242                }
243            }
244    
245            //TODO: add suffix for special case
246            String suffix = currentDescriptor.getName().isSpecial() ? "" : currentDescriptor.getName().asString();
247    
248            //noinspection ConstantConditions
249            return getInlineName(codegenContext, currentDescriptor.getContainingDeclaration(), typeMapper, fileClassesProvider) + "$" + suffix;
250        }
251    
252        public static boolean isInvokeOnLambda(@NotNull String owner, @NotNull String name) {
253            return OperatorNameConventions.INVOKE.asString().equals(name) &&
254                   owner.startsWith(NUMBERED_FUNCTION_PREFIX) &&
255                   isInteger(owner.substring(NUMBERED_FUNCTION_PREFIX.length()));
256        }
257    
258        public static boolean isAnonymousConstructorCall(@NotNull String internalName, @NotNull String methodName) {
259            return "<init>".equals(methodName) && isAnonymousClass(internalName);
260        }
261    
262        public static boolean isWhenMappingAccess(@NotNull String internalName, @NotNull String fieldName) {
263            return fieldName.startsWith(WhenByEnumsMapping.MAPPING_ARRAY_FIELD_PREFIX) &&
264                   internalName.endsWith(WhenByEnumsMapping.MAPPINGS_CLASS_NAME_POSTFIX);
265        }
266    
267        public static boolean isAnonymousSingletonLoad(@NotNull String internalName, @NotNull String fieldName) {
268            return JvmAbi.INSTANCE_FIELD.equals(fieldName) && isAnonymousClass(internalName);
269        }
270    
271        public static boolean isAnonymousClass(@NotNull String internalName) {
272            String shortName = getLastNamePart(internalName);
273            int index = shortName.lastIndexOf("$");
274    
275            if (index < 0) {
276                return false;
277            }
278    
279            String suffix = shortName.substring(index + 1);
280            return isInteger(suffix);
281        }
282    
283        @NotNull
284        private static String getLastNamePart(@NotNull String internalName) {
285            int index = internalName.lastIndexOf("/");
286            return index < 0 ? internalName : internalName.substring(index + 1);
287        }
288    
289        @NotNull
290        public static MethodVisitor wrapWithMaxLocalCalc(@NotNull MethodNode methodNode) {
291            return new MaxStackFrameSizeAndLocalsCalculator(API, methodNode.access, methodNode.desc, methodNode);
292        }
293    
294        private static boolean isInteger(@NotNull String string) {
295            if (string.isEmpty()) {
296                return false;
297            }
298    
299            for (int i = 0; i < string.length(); i++) {
300                 if (!Character.isDigit(string.charAt(i))) {
301                     return false;
302                 }
303            }
304    
305            return true;
306        }
307    
308        public static boolean isCapturedFieldName(@NotNull String fieldName) {
309            // TODO: improve this heuristic
310            return fieldName.startsWith(CAPTURED_FIELD_PREFIX) &&
311                   !fieldName.startsWith(NON_CAPTURED_FIELD_PREFIX) ||
312                   THIS$0.equals(fieldName) ||
313                   RECEIVER$0.equals(fieldName);
314        }
315    
316        public static boolean isReturnOpcode(int opcode) {
317            return opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN;
318        }
319    
320        //marked return could be either non-local or local in case of labeled lambda self-returns
321        public static boolean isMarkedReturn(@NotNull AbstractInsnNode returnIns) {
322            return getMarkedReturnLabelOrNull(returnIns) != null;
323        }
324    
325        @Nullable
326        public static String getMarkedReturnLabelOrNull(@NotNull AbstractInsnNode returnInsn) {
327            if (!isReturnOpcode(returnInsn.getOpcode())) {
328                return null;
329            }
330            AbstractInsnNode previous = returnInsn.getPrevious();
331            if (previous instanceof MethodInsnNode) {
332                MethodInsnNode marker = (MethodInsnNode) previous;
333                if (NON_LOCAL_RETURN.equals(marker.owner)) {
334                    return marker.name;
335                }
336            }
337            return null;
338        }
339    
340        public static void generateGlobalReturnFlag(@NotNull InstructionAdapter iv, @NotNull String labelName) {
341            iv.invokestatic(NON_LOCAL_RETURN, labelName, "()V", false);
342        }
343    
344        @NotNull
345        public static Type getReturnType(int opcode) {
346            switch (opcode) {
347                case Opcodes.RETURN:
348                    return Type.VOID_TYPE;
349                case Opcodes.IRETURN:
350                    return Type.INT_TYPE;
351                case Opcodes.DRETURN:
352                    return Type.DOUBLE_TYPE;
353                case Opcodes.FRETURN:
354                    return Type.FLOAT_TYPE;
355                case Opcodes.LRETURN:
356                    return Type.LONG_TYPE;
357                default:
358                    return AsmTypes.OBJECT_TYPE;
359            }
360        }
361    
362        public static void insertNodeBefore(@NotNull MethodNode from, @NotNull MethodNode to, @NotNull AbstractInsnNode beforeNode) {
363            ListIterator<AbstractInsnNode> iterator = from.instructions.iterator();
364            while (iterator.hasNext()) {
365                AbstractInsnNode next = iterator.next();
366                to.instructions.insertBefore(beforeNode, next);
367            }
368        }
369    
370        @NotNull
371        public static MethodNode createEmptyMethodNode() {
372            return new MethodNode(API, 0, "fake", "()V", null, null);
373        }
374    
375        @NotNull
376        public static LabelNode firstLabelInChain(@NotNull LabelNode node) {
377            LabelNode curNode = node;
378            while (curNode.getPrevious() instanceof LabelNode) {
379                curNode = (LabelNode) curNode.getPrevious();
380            }
381            return curNode;
382        }
383    
384        @NotNull
385        public static String getNodeText(@Nullable MethodNode node) {
386            Textifier textifier = new Textifier();
387            if (node == null) {
388                return "Not generated";
389            }
390            node.accept(new TraceMethodVisitor(textifier));
391            StringWriter sw = new StringWriter();
392            textifier.print(new PrintWriter(sw));
393            sw.flush();
394            return node.name + " " + node.desc + ":\n" + sw.getBuffer().toString();
395        }
396    
397        @NotNull
398        public static String getInsnText(@Nullable AbstractInsnNode node) {
399            if (node == null) return "<null>";
400            Textifier textifier = new Textifier();
401            node.accept(new TraceMethodVisitor(textifier));
402            StringWriter sw = new StringWriter();
403            textifier.print(new PrintWriter(sw));
404            sw.flush();
405            return sw.toString().trim();
406        }
407    
408        @NotNull
409        /* package */ static ClassReader buildClassReaderByInternalName(@NotNull GenerationState state, @NotNull String internalName) {
410            //try to find just compiled classes then in dependencies
411            try {
412                OutputFile outputFile = state.getFactory().get(internalName + ".class");
413                if (outputFile != null) {
414                    return new ClassReader(outputFile.asByteArray());
415                }
416                VirtualFile file = findVirtualFileImprecise(state, internalName);
417                if (file != null) {
418                    return new ClassReader(file.contentsToByteArray());
419                }
420                throw new RuntimeException("Couldn't find virtual file for " + internalName);
421            }
422            catch (IOException e) {
423                throw new RuntimeException(e);
424            }
425        }
426    
427        public static void generateFinallyMarker(@NotNull InstructionAdapter v, int depth, boolean start) {
428            v.iconst(depth);
429            v.invokestatic(INLINE_MARKER_CLASS_NAME, start ? INLINE_MARKER_FINALLY_START : INLINE_MARKER_FINALLY_END, "(I)V", false);
430        }
431    
432        public static boolean isFinallyEnd(@NotNull AbstractInsnNode node) {
433            return isFinallyMarker(node, INLINE_MARKER_FINALLY_END);
434        }
435    
436        public static boolean isFinallyStart(@NotNull AbstractInsnNode node) {
437            return isFinallyMarker(node, INLINE_MARKER_FINALLY_START);
438        }
439    
440        public static boolean isFinallyMarker(@Nullable AbstractInsnNode node) {
441            return node != null && (isFinallyStart(node) || isFinallyEnd(node));
442        }
443    
444        private static boolean isFinallyMarker(@NotNull AbstractInsnNode node, String name) {
445            if (!(node instanceof MethodInsnNode)) return false;
446            MethodInsnNode method = (MethodInsnNode) node;
447            return INLINE_MARKER_CLASS_NAME.equals(method.owner) && name.equals(method.name);
448        }
449    
450        public static boolean isFinallyMarkerRequired(@NotNull MethodContext context) {
451            return context.isInlineMethodContext() || context instanceof InlineLambdaContext;
452        }
453    
454        public static int getConstant(@NotNull AbstractInsnNode ins) {
455            int opcode = ins.getOpcode();
456            Integer value;
457            if (opcode >= Opcodes.ICONST_0 && opcode <= Opcodes.ICONST_5) {
458                return opcode - Opcodes.ICONST_0;
459            }
460            else if (opcode == Opcodes.BIPUSH || opcode == Opcodes.SIPUSH) {
461                return ((IntInsnNode) ins).operand;
462            }
463            else {
464                LdcInsnNode index = (LdcInsnNode) ins;
465                return (Integer) index.cst;
466            }
467        }
468    
469        public static void addInlineMarker(@NotNull InstructionAdapter v, boolean isStartNotEnd) {
470            v.visitMethodInsn(
471                    Opcodes.INVOKESTATIC, INLINE_MARKER_CLASS_NAME,
472                    isStartNotEnd ? INLINE_MARKER_BEFORE_METHOD_NAME : INLINE_MARKER_AFTER_METHOD_NAME,
473                    "()V", false
474            );
475        }
476    
477        public static boolean isInlineMarker(@NotNull AbstractInsnNode insn) {
478            return isInlineMarker(insn, null);
479        }
480    
481        private static boolean isInlineMarker(@NotNull AbstractInsnNode insn, @Nullable String name) {
482            if (!(insn instanceof MethodInsnNode)) {
483                return false;
484            }
485    
486            MethodInsnNode methodInsnNode = (MethodInsnNode) insn;
487            return insn.getOpcode() == Opcodes.INVOKESTATIC &&
488                   methodInsnNode.owner.equals(INLINE_MARKER_CLASS_NAME) &&
489                   (name != null ? methodInsnNode.name.equals(name)
490                                 : methodInsnNode.name.equals(INLINE_MARKER_BEFORE_METHOD_NAME) ||
491                                   methodInsnNode.name.equals(INLINE_MARKER_AFTER_METHOD_NAME));
492        }
493    
494        public static boolean isBeforeInlineMarker(@NotNull AbstractInsnNode insn) {
495            return isInlineMarker(insn, INLINE_MARKER_BEFORE_METHOD_NAME);
496        }
497    
498        public static boolean isAfterInlineMarker(@NotNull AbstractInsnNode insn) {
499            return isInlineMarker(insn, INLINE_MARKER_AFTER_METHOD_NAME);
500        }
501    
502        public static int getLoadStoreArgSize(int opcode) {
503            return opcode == Opcodes.DSTORE || opcode == Opcodes.LSTORE || opcode == Opcodes.DLOAD || opcode == Opcodes.LLOAD ? 2 : 1;
504        }
505    
506        public static boolean isStoreInstruction(int opcode) {
507            return opcode >= Opcodes.ISTORE && opcode <= Opcodes.ASTORE;
508        }
509    
510        public static int calcMarkerShift(@NotNull Parameters parameters, @NotNull MethodNode node) {
511            int markerShiftTemp = getIndexAfterLastMarker(node);
512            return markerShiftTemp - parameters.getRealParametersSizeOnStack() + parameters.getArgsSizeOnStack();
513        }
514    
515        private static int getIndexAfterLastMarker(@NotNull MethodNode node) {
516            int result = -1;
517            for (LocalVariableNode variable : node.localVariables) {
518                if (isFakeLocalVariableForInline(variable.name)) {
519                    result = Math.max(result, variable.index + 1);
520                }
521            }
522            return result;
523        }
524    
525        public static boolean isFakeLocalVariableForInline(@NotNull String name) {
526            return name.startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION) ||
527                   name.startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT);
528        }
529    
530        public static boolean isThis0(@NotNull String name) {
531            return THIS$0.equals(name);
532        }
533    
534        public static boolean isSpecialEnumMethod(@NotNull FunctionDescriptor functionDescriptor) {
535            DeclarationDescriptor containingDeclaration = functionDescriptor.getContainingDeclaration();
536            if (!(containingDeclaration instanceof PackageFragmentDescriptor)) {
537                return false;
538            }
539            if (!((PackageFragmentDescriptor) containingDeclaration).getFqName().equals(KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME)) {
540                return false;
541            }
542            if (functionDescriptor.getTypeParameters().size() != 1) {
543                return false;
544            }
545            String name = functionDescriptor.getName().asString();
546            List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
547            return "enumValues".equals(name) && parameters.size() == 0 ||
548                   "enumValueOf".equals(name) && parameters.size() == 1 && KotlinBuiltIns.isString(parameters.get(0).getType());
549        }
550    
551        public static MethodNode createSpecialEnumMethodBody(
552                @NotNull ExpressionCodegen codegen,
553                @NotNull String name,
554                @NotNull KotlinType type,
555                @NotNull KotlinTypeMapper typeMapper
556        ) {
557            boolean isValueOf = "enumValueOf".equals(name);
558            Type invokeType = typeMapper.mapType(type);
559            String desc = getSpecialEnumFunDescriptor(invokeType, isValueOf);
560            MethodNode node = new MethodNode(API, Opcodes.ACC_STATIC, "fake", desc, null, null);
561            codegen.putReifiedOperationMarkerIfTypeIsReifiedParameter(type, ReifiedTypeInliner.OperationKind.ENUM_REIFIED, new InstructionAdapter(node));
562            if (isValueOf) {
563                node.visitInsn(Opcodes.ACONST_NULL);
564                node.visitVarInsn(Opcodes.ALOAD, 0);
565    
566                node.visitMethodInsn(Opcodes.INVOKESTATIC, ENUM_TYPE.getInternalName(), "valueOf",
567                                     Type.getMethodDescriptor(ENUM_TYPE, JAVA_CLASS_TYPE, AsmTypes.JAVA_STRING_TYPE), false);
568            }
569            else {
570                node.visitInsn(Opcodes.ICONST_0);
571                node.visitTypeInsn(Opcodes.ANEWARRAY, ENUM_TYPE.getInternalName());
572            }
573            node.visitInsn(Opcodes.ARETURN);
574            node.visitMaxs(isValueOf ? 3 : 2, isValueOf ? 1 : 0);
575            return node;
576        }
577    
578        @NotNull
579        public static String getSpecialEnumFunDescriptor(@NotNull Type type, boolean isValueOf) {
580            return isValueOf ? Type.getMethodDescriptor(type, AsmTypes.JAVA_STRING_TYPE) : Type.getMethodDescriptor(AsmUtil.getArrayType(type));
581        }
582    }
583