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.when;
018    
019    import com.intellij.util.ArrayUtil;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.kotlin.codegen.ClassBuilder;
022    import org.jetbrains.kotlin.codegen.WriteAnnotationUtilKt;
023    import org.jetbrains.kotlin.codegen.state.GenerationState;
024    import org.jetbrains.kotlin.psi.KtFile;
025    import org.jetbrains.kotlin.resolve.constants.EnumValue;
026    import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
027    import org.jetbrains.org.objectweb.asm.MethodVisitor;
028    import org.jetbrains.org.objectweb.asm.Type;
029    import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
030    
031    import java.util.List;
032    import java.util.Map;
033    
034    import static org.jetbrains.kotlin.codegen.AsmUtil.writeKotlinSyntheticClassAnnotation;
035    import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE;
036    import static org.jetbrains.org.objectweb.asm.Opcodes.*;
037    
038    public class MappingClassesForWhenByEnumCodegen {
039        public static final String MAPPINGS_FIELD_DESCRIPTOR = Type.getDescriptor(int[].class);
040        private final GenerationState state;
041    
042        public MappingClassesForWhenByEnumCodegen(@NotNull GenerationState state) {
043            this.state = state;
044        }
045    
046        public void generate(@NotNull List<WhenByEnumsMapping> mappings, @NotNull Type mappingsClass, @NotNull KtFile srcFile) {
047            ClassBuilder cb = state.getFactory().newVisitor(JvmDeclarationOrigin.NO_ORIGIN, mappingsClass, srcFile);
048            cb.defineClass(
049                    srcFile,
050                    V1_6,
051                    ACC_PUBLIC | ACC_FINAL | ACC_SUPER | ACC_SYNTHETIC,
052                    mappingsClass.getInternalName(),
053                    null,
054                    OBJECT_TYPE.getInternalName(),
055                    ArrayUtil.EMPTY_STRING_ARRAY
056            );
057    
058            generateFields(cb, mappings);
059            generateInitialization(cb, mappings);
060    
061            writeKotlinSyntheticClassAnnotation(cb, state);
062    
063            WriteAnnotationUtilKt.writeSyntheticClassMetadata(cb);
064    
065            cb.done();
066        }
067    
068        private static void generateFields(@NotNull ClassBuilder cb, @NotNull List<WhenByEnumsMapping> mappings) {
069            for (WhenByEnumsMapping mapping : mappings) {
070                cb.newField(
071                        JvmDeclarationOrigin.NO_ORIGIN,
072                        ACC_STATIC | ACC_PUBLIC | ACC_FINAL | ACC_SYNTHETIC,
073                        mapping.getFieldName(),
074                        MAPPINGS_FIELD_DESCRIPTOR,
075                        null, null
076                );
077            }
078        }
079    
080        private void generateInitialization(@NotNull ClassBuilder cb, @NotNull List<WhenByEnumsMapping> mappings) {
081            MethodVisitor mv = cb.newMethod(
082                    JvmDeclarationOrigin.NO_ORIGIN,
083                    ACC_STATIC | ACC_SYNTHETIC, "<clinit>", "()V", null, ArrayUtil.EMPTY_STRING_ARRAY
084            );
085    
086            mv.visitCode();
087    
088            InstructionAdapter v = new InstructionAdapter(mv);
089    
090            for (WhenByEnumsMapping mapping : mappings) {
091                generateInitializationForMapping(cb, v, mapping);
092            }
093    
094            v.areturn(Type.VOID_TYPE);
095    
096            mv.visitMaxs(-1, -1);
097            mv.visitEnd();
098        }
099    
100        private void generateInitializationForMapping(
101                @NotNull ClassBuilder cb,
102                @NotNull InstructionAdapter v,
103                @NotNull WhenByEnumsMapping mapping
104        ) {
105            Type enumType = state.getTypeMapper().mapClass(mapping.getEnumClassDescriptor());
106    
107            v.invokestatic(enumType.getInternalName(), "values", Type.getMethodDescriptor(Type.getType("[" + enumType.getDescriptor())), false);
108            v.arraylength();
109    
110            v.newarray(Type.INT_TYPE);
111            v.putstatic(cb.getThisName(), mapping.getFieldName(), MAPPINGS_FIELD_DESCRIPTOR);
112    
113            for (Map.Entry<EnumValue, Integer> item : mapping.enumValuesToIntMapping()) {
114                EnumValue enumEntry = item.getKey();
115                int mappedValue = item.getValue();
116    
117                v.getstatic(cb.getThisName(), mapping.getFieldName(), MAPPINGS_FIELD_DESCRIPTOR);
118                v.getstatic(enumType.getInternalName(), enumEntry.getValue().getName().asString(), enumType.getDescriptor());
119                v.invokevirtual(enumType.getInternalName(), "ordinal", Type.getMethodDescriptor(Type.INT_TYPE), false);
120                v.iconst(mappedValue);
121                v.astore(Type.INT_TYPE);
122            }
123        }
124    }