001    /*
002     * Copyright 2010-2013 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.lang.resolve.kotlin;
018    
019    import com.intellij.openapi.util.Ref;
020    import com.intellij.openapi.vfs.VirtualFile;
021    import kotlin.Function0;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.asm4.ClassReader;
025    import org.jetbrains.asm4.ClassVisitor;
026    import org.jetbrains.asm4.FieldVisitor;
027    import org.jetbrains.asm4.MethodVisitor;
028    import org.jetbrains.jet.lang.resolve.java.JvmClassName;
029    import org.jetbrains.jet.lang.resolve.kotlin.header.KotlinClassHeader;
030    import org.jetbrains.jet.lang.resolve.kotlin.header.ReadKotlinClassHeaderAnnotationVisitor;
031    import org.jetbrains.jet.lang.resolve.name.Name;
032    import org.jetbrains.jet.storage.NotNullLazyValue;
033    import org.jetbrains.jet.storage.NullableLazyValue;
034    import org.jetbrains.jet.storage.StorageManager;
035    import org.jetbrains.jet.utils.UtilsPackage;
036    
037    import java.io.IOException;
038    
039    import static org.jetbrains.asm4.ClassReader.*;
040    import static org.jetbrains.asm4.Opcodes.ASM4;
041    
042    public class VirtualFileKotlinClass implements KotlinJvmBinaryClass {
043        private final VirtualFile file;
044        private final NotNullLazyValue<JvmClassName> className;
045        private final NullableLazyValue<KotlinClassHeader> classHeader;
046    
047        public VirtualFileKotlinClass(@NotNull StorageManager storageManager, @NotNull VirtualFile file) {
048            this.file = file;
049            this.className = storageManager.createLazyValue(
050                    new Function0<JvmClassName>() {
051                        @Override
052                        public JvmClassName invoke() {
053                            return computeClassName();
054                        }
055                    }
056            );
057            this.classHeader = storageManager.createNullableLazyValue(
058                    new Function0<KotlinClassHeader>() {
059                        @Override
060                        public KotlinClassHeader invoke() {
061                            return ReadKotlinClassHeaderAnnotationVisitor.read(VirtualFileKotlinClass.this);
062                        }
063                    }
064            );
065        }
066    
067        @NotNull
068        public VirtualFile getFile() {
069            return file;
070        }
071    
072        @NotNull
073        private JvmClassName computeClassName() {
074            final Ref<JvmClassName> classNameRef = Ref.create();
075            try {
076                new ClassReader(file.contentsToByteArray()).accept(new ClassVisitor(ASM4) {
077                    @Override
078                    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
079                        classNameRef.set(JvmClassName.byInternalName(name));
080                    }
081                }, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES);
082            }
083            catch (IOException e) {
084                throw UtilsPackage.rethrow(e);
085            }
086            return classNameRef.get();
087        }
088    
089        @NotNull
090        @Override
091        public JvmClassName getClassName() {
092            return className.invoke();
093        }
094    
095        @Override
096        public KotlinClassHeader getClassHeader() {
097            return classHeader.invoke();
098        }
099    
100        @Override
101        public void loadClassAnnotations(@NotNull final AnnotationVisitor annotationVisitor) {
102            try {
103                new ClassReader(file.contentsToByteArray()).accept(new ClassVisitor(ASM4) {
104                    @Override
105                    public org.jetbrains.asm4.AnnotationVisitor visitAnnotation(String desc, boolean visible) {
106                        return convertAnnotationVisitor(annotationVisitor, desc);
107                    }
108    
109                    @Override
110                    public void visitEnd() {
111                        annotationVisitor.visitEnd();
112                    }
113                }, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES);
114            }
115            catch (IOException e) {
116                throw UtilsPackage.rethrow(e);
117            }
118        }
119    
120        @Nullable
121        private static org.jetbrains.asm4.AnnotationVisitor convertAnnotationVisitor(@NotNull AnnotationVisitor visitor, @NotNull String desc) {
122            AnnotationArgumentVisitor v = visitor.visitAnnotation(classNameFromAsmDesc(desc));
123            return v == null ? null : convertAnnotationVisitor(v);
124        }
125    
126        @NotNull
127        private static org.jetbrains.asm4.AnnotationVisitor convertAnnotationVisitor(@NotNull final AnnotationArgumentVisitor v) {
128            return new org.jetbrains.asm4.AnnotationVisitor(ASM4) {
129                @Override
130                public void visit(String name, Object value) {
131                    v.visit(name == null ? null : Name.identifier(name), value);
132                }
133    
134                @Override
135                public org.jetbrains.asm4.AnnotationVisitor visitArray(String name) {
136                    AnnotationArgumentVisitor av = v.visitArray(Name.guess(name));
137                    return av == null ? null : convertAnnotationVisitor(av);
138                }
139    
140                @Override
141                public void visitEnum(String name, String desc, String value) {
142                    v.visitEnum(Name.identifier(name), classNameFromAsmDesc(desc), Name.identifier(value));
143                }
144    
145                @Override
146                public void visitEnd() {
147                    v.visitEnd();
148                }
149            };
150        }
151    
152        @Override
153        public void loadMemberAnnotations(@NotNull final MemberVisitor memberVisitor) {
154            try {
155                new ClassReader(file.contentsToByteArray()).accept(new ClassVisitor(ASM4) {
156                    @Override
157                    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
158                        final AnnotationVisitor v = memberVisitor.visitField(Name.guess(name), desc);
159                        if (v == null) return null;
160    
161                        return new FieldVisitor(ASM4) {
162                            @Override
163                            public org.jetbrains.asm4.AnnotationVisitor visitAnnotation(String desc, boolean visible) {
164                                return convertAnnotationVisitor(v, desc);
165                            }
166    
167                            @Override
168                            public void visitEnd() {
169                                v.visitEnd();
170                            }
171                        };
172                    }
173    
174                    @Override
175                    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
176                        final MethodAnnotationVisitor v = memberVisitor.visitMethod(Name.guess(name), desc);
177                        if (v == null) return null;
178    
179                        return new MethodVisitor(ASM4) {
180                            @Override
181                            public org.jetbrains.asm4.AnnotationVisitor visitAnnotation(String desc, boolean visible) {
182                                return convertAnnotationVisitor(v, desc);
183                            }
184    
185                            @Override
186                            public org.jetbrains.asm4.AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
187                                AnnotationArgumentVisitor av = v.visitParameterAnnotation(parameter, classNameFromAsmDesc(desc));
188                                return av == null ? null : convertAnnotationVisitor(av);
189                            }
190    
191                            @Override
192                            public void visitEnd() {
193                                v.visitEnd();
194                            }
195                        };
196                    }
197                }, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES);
198            }
199            catch (IOException e) {
200                throw UtilsPackage.rethrow(e);
201            }
202        }
203    
204        @NotNull
205        private static JvmClassName classNameFromAsmDesc(@NotNull String desc) {
206            assert desc.startsWith("L") && desc.endsWith(";") : "Not a JVM descriptor: " + desc;
207            return JvmClassName.byInternalName(desc.substring(1, desc.length() - 1));
208        }
209    
210        @Override
211        public int hashCode() {
212            return file.hashCode();
213        }
214    
215        @Override
216        public boolean equals(Object obj) {
217            return obj instanceof VirtualFileKotlinClass && ((VirtualFileKotlinClass) obj).file.equals(file);
218        }
219    
220        @Override
221        public String toString() {
222            return getClass().getSimpleName() + ": " + file.toString();
223        }
224    }