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