001    /*
002     * Copyright 2010-2014 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.jet.codegen.when;
018    
019    import com.intellij.util.ArrayUtil;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.jet.codegen.AsmUtil;
022    import org.jetbrains.jet.codegen.ClassBuilder;
023    import org.jetbrains.jet.codegen.state.GenerationState;
024    import org.jetbrains.jet.lang.psi.JetFile;
025    import org.jetbrains.jet.lang.resolve.constants.EnumValue;
026    import org.jetbrains.jet.lang.resolve.java.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.jet.lang.resolve.java.AsmTypeConstants.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(
046                @NotNull List<WhenByEnumsMapping> mappings,
047                @NotNull Type mappingsClass,
048                @NotNull JetFile srcFile
049        ) {
050            ClassBuilder cb = state.getFactory().newVisitor(
051                    JvmDeclarationOrigin.NO_ORIGIN,
052                    mappingsClass,
053                    srcFile
054            );
055            cb.defineClass(
056                    srcFile,
057                    V1_6,
058                    ACC_FINAL | ACC_SYNTHETIC,
059                    mappingsClass.getInternalName(),
060                    null,
061                    OBJECT_TYPE.getInternalName(),
062                    ArrayUtil.EMPTY_STRING_ARRAY
063            );
064    
065            generateFields(cb, mappings);
066            generateInitialization(cb, mappings);
067    
068            cb.done();
069        }
070    
071        private static void generateFields(
072                @NotNull ClassBuilder cb,
073                @NotNull List<WhenByEnumsMapping> mappings
074        ) {
075            for (WhenByEnumsMapping mapping : mappings) {
076                cb.newField(
077                        JvmDeclarationOrigin.NO_ORIGIN,
078                        ACC_STATIC | ACC_PUBLIC | ACC_FINAL | ACC_SYNTHETIC,
079                        mapping.getFieldName(),
080                        MAPPINGS_FIELD_DESCRIPTOR,
081                        null, null
082                );
083            }
084        }
085    
086        private static void generateInitialization(
087                @NotNull ClassBuilder cb,
088                @NotNull List<WhenByEnumsMapping> mappings
089        ) {
090            MethodVisitor mv = cb.newMethod(
091                    JvmDeclarationOrigin.NO_ORIGIN,
092                    ACC_STATIC | ACC_SYNTHETIC, "<clinit>", "()V", null, ArrayUtil.EMPTY_STRING_ARRAY
093            );
094    
095            mv.visitCode();
096    
097            InstructionAdapter v = new InstructionAdapter(mv);
098    
099            for (WhenByEnumsMapping mapping : mappings) {
100                generateInitializationForMapping(cb, v, mapping);
101            }
102    
103            v.areturn(Type.VOID_TYPE);
104    
105            mv.visitMaxs(-1, -1);
106            mv.visitEnd();
107        }
108    
109        private static void generateInitializationForMapping(
110                @NotNull ClassBuilder cb,
111                @NotNull InstructionAdapter v,
112                @NotNull WhenByEnumsMapping mapping
113        ) {
114            v.invokestatic(
115                    mapping.getEnumClassInternalName(), "values",
116                    Type.getMethodDescriptor(
117                            AsmUtil.getArrayOf(mapping.getEnumClassInternalName())
118                    ),
119                    false
120            );
121            v.arraylength();
122    
123            v.newarray(Type.INT_TYPE);
124            v.putstatic(cb.getThisName(), mapping.getFieldName(), MAPPINGS_FIELD_DESCRIPTOR);
125    
126            String enumClassDesc = Type.getObjectType(mapping.getEnumClassInternalName()).getDescriptor();
127    
128            for (Map.Entry<EnumValue, Integer> item : mapping.enumValuesToIntMapping()) {
129                EnumValue enumEntry = item.getKey();
130                int mappedValue = item.getValue();
131    
132                v.getstatic(cb.getThisName(), mapping.getFieldName(), MAPPINGS_FIELD_DESCRIPTOR);
133                v.getstatic(
134                        mapping.getEnumClassInternalName(),
135                        enumEntry.getValue().getName().asString(),
136                        enumClassDesc
137                );
138                v.invokevirtual(
139                        mapping.getEnumClassInternalName(), "ordinal",
140                        Type.getMethodDescriptor(Type.INT_TYPE),
141                        false
142                );
143                v.iconst(mappedValue);
144                v.astore(Type.INT_TYPE);
145            }
146        }
147    }