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