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.state.GenerationState;
023    import org.jetbrains.kotlin.psi.JetFile;
024    import org.jetbrains.kotlin.resolve.constants.EnumValue;
025    import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
026    import org.jetbrains.org.objectweb.asm.MethodVisitor;
027    import org.jetbrains.org.objectweb.asm.Type;
028    import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
029    
030    import java.util.List;
031    import java.util.Map;
032    
033    import static org.jetbrains.kotlin.codegen.AsmUtil.writeKotlinSyntheticClassAnnotation;
034    import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinSyntheticClass.Kind.WHEN_ON_ENUM_MAPPINGS;
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 JetFile srcFile) {
047            ClassBuilder cb = state.getFactory().newVisitor(JvmDeclarationOrigin.NO_ORIGIN, mappingsClass, srcFile);
048            cb.defineClass(
049                    srcFile,
050                    V1_6,
051                    ACC_FINAL | 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, WHEN_ON_ENUM_MAPPINGS);
062    
063            cb.done();
064        }
065    
066        private static void generateFields(@NotNull ClassBuilder cb, @NotNull List<WhenByEnumsMapping> mappings) {
067            for (WhenByEnumsMapping mapping : mappings) {
068                cb.newField(
069                        JvmDeclarationOrigin.NO_ORIGIN,
070                        ACC_STATIC | ACC_PUBLIC | ACC_FINAL | ACC_SYNTHETIC,
071                        mapping.getFieldName(),
072                        MAPPINGS_FIELD_DESCRIPTOR,
073                        null, null
074                );
075            }
076        }
077    
078        private void generateInitialization(@NotNull ClassBuilder cb, @NotNull List<WhenByEnumsMapping> mappings) {
079            MethodVisitor mv = cb.newMethod(
080                    JvmDeclarationOrigin.NO_ORIGIN,
081                    ACC_STATIC | ACC_SYNTHETIC, "<clinit>", "()V", null, ArrayUtil.EMPTY_STRING_ARRAY
082            );
083    
084            mv.visitCode();
085    
086            InstructionAdapter v = new InstructionAdapter(mv);
087    
088            for (WhenByEnumsMapping mapping : mappings) {
089                generateInitializationForMapping(cb, v, mapping);
090            }
091    
092            v.areturn(Type.VOID_TYPE);
093    
094            mv.visitMaxs(-1, -1);
095            mv.visitEnd();
096        }
097    
098        private void generateInitializationForMapping(
099                @NotNull ClassBuilder cb,
100                @NotNull InstructionAdapter v,
101                @NotNull WhenByEnumsMapping mapping
102        ) {
103            Type enumType = state.getTypeMapper().mapClass(mapping.getEnumClassDescriptor());
104    
105            v.invokestatic(enumType.getInternalName(), "values", Type.getMethodDescriptor(Type.getType("[" + enumType.getDescriptor())), false);
106            v.arraylength();
107    
108            v.newarray(Type.INT_TYPE);
109            v.putstatic(cb.getThisName(), mapping.getFieldName(), MAPPINGS_FIELD_DESCRIPTOR);
110    
111            for (Map.Entry<EnumValue, Integer> item : mapping.enumValuesToIntMapping()) {
112                EnumValue enumEntry = item.getKey();
113                int mappedValue = item.getValue();
114    
115                v.getstatic(cb.getThisName(), mapping.getFieldName(), MAPPINGS_FIELD_DESCRIPTOR);
116                v.getstatic(enumType.getInternalName(), enumEntry.getValue().getName().asString(), enumType.getDescriptor());
117                v.invokevirtual(enumType.getInternalName(), "ordinal", Type.getMethodDescriptor(Type.INT_TYPE), false);
118                v.iconst(mappedValue);
119                v.astore(Type.INT_TYPE);
120            }
121        }
122    }