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.vfs.VirtualFile;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.asm4.ClassReader;
023    import org.jetbrains.asm4.ClassVisitor;
024    import org.jetbrains.asm4.FieldVisitor;
025    import org.jetbrains.asm4.MethodVisitor;
026    import org.jetbrains.jet.lang.resolve.java.JvmClassName;
027    import org.jetbrains.jet.lang.resolve.name.Name;
028    import org.jetbrains.jet.utils.ExceptionUtils;
029    
030    import java.io.IOException;
031    
032    import static org.jetbrains.asm4.ClassReader.*;
033    import static org.jetbrains.asm4.Opcodes.ASM4;
034    
035    public class VirtualFileKotlinClass implements KotlinJvmBinaryClass {
036        private final VirtualFile file;
037        private JvmClassName className;
038    
039        public VirtualFileKotlinClass(@NotNull VirtualFile file) {
040            this.file = file;
041        }
042    
043        @NotNull
044        public VirtualFile getFile() {
045            return file;
046        }
047    
048        @NotNull
049        @Override
050        public JvmClassName getClassName() {
051            if (className == null) {
052                try {
053                    new ClassReader(file.contentsToByteArray()).accept(new ClassVisitor(ASM4) {
054                        @Override
055                        public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
056                            className = JvmClassName.byInternalName(name);
057                        }
058                    }, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES);
059                }
060                catch (IOException e) {
061                    throw ExceptionUtils.rethrow(e);
062                }
063            }
064    
065            return className;
066        }
067    
068        @Override
069        public void loadClassAnnotations(@NotNull final AnnotationVisitor annotationVisitor) {
070            try {
071                new ClassReader(file.contentsToByteArray()).accept(new ClassVisitor(ASM4) {
072                    @Override
073                    public org.jetbrains.asm4.AnnotationVisitor visitAnnotation(String desc, boolean visible) {
074                        return convertAnnotationVisitor(annotationVisitor, desc);
075                    }
076    
077                    @Override
078                    public void visitEnd() {
079                        annotationVisitor.visitEnd();
080                    }
081                }, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES);
082            }
083            catch (IOException e) {
084                throw ExceptionUtils.rethrow(e);
085            }
086        }
087    
088        @Nullable
089        private static org.jetbrains.asm4.AnnotationVisitor convertAnnotationVisitor(@NotNull AnnotationVisitor visitor, @NotNull String desc) {
090            AnnotationArgumentVisitor v = visitor.visitAnnotation(classNameFromAsmDesc(desc));
091            return v == null ? null : convertAnnotationVisitor(v);
092        }
093    
094        @NotNull
095        private static org.jetbrains.asm4.AnnotationVisitor convertAnnotationVisitor(@NotNull final AnnotationArgumentVisitor v) {
096            return new org.jetbrains.asm4.AnnotationVisitor(ASM4) {
097                @Override
098                public void visit(String name, Object value) {
099                    v.visit(name == null ? null : Name.identifier(name), value);
100                }
101    
102                @Override
103                public org.jetbrains.asm4.AnnotationVisitor visitArray(String name) {
104                    AnnotationArgumentVisitor av = v.visitArray(Name.guess(name));
105                    return av == null ? null : convertAnnotationVisitor(av);
106                }
107    
108                @Override
109                public void visitEnum(String name, String desc, String value) {
110                    v.visitEnum(Name.identifier(name), classNameFromAsmDesc(desc), Name.identifier(value));
111                }
112    
113                @Override
114                public void visitEnd() {
115                    v.visitEnd();
116                }
117            };
118        }
119    
120        @Override
121        public void loadMemberAnnotations(@NotNull final MemberVisitor memberVisitor) {
122            try {
123                new ClassReader(file.contentsToByteArray()).accept(new ClassVisitor(ASM4) {
124                    @Override
125                    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
126                        final AnnotationVisitor v = memberVisitor.visitField(Name.guess(name), desc);
127                        if (v == null) return null;
128    
129                        return new FieldVisitor(ASM4) {
130                            @Override
131                            public org.jetbrains.asm4.AnnotationVisitor visitAnnotation(String desc, boolean visible) {
132                                return convertAnnotationVisitor(v, desc);
133                            }
134    
135                            @Override
136                            public void visitEnd() {
137                                v.visitEnd();
138                            }
139                        };
140                    }
141    
142                    @Override
143                    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
144                        final AnnotationVisitor v = memberVisitor.visitMethod(Name.guess(name), desc);
145                        if (v == null) return null;
146    
147                        return new MethodVisitor(ASM4) {
148                            @Override
149                            public org.jetbrains.asm4.AnnotationVisitor visitAnnotation(String desc, boolean visible) {
150                                return convertAnnotationVisitor(v, desc);
151                            }
152    
153                            @Override
154                            public void visitEnd() {
155                                v.visitEnd();
156                            }
157                        };
158                    }
159                }, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES);
160            }
161            catch (IOException e) {
162                throw ExceptionUtils.rethrow(e);
163            }
164        }
165    
166        @NotNull
167        private static JvmClassName classNameFromAsmDesc(@NotNull String desc) {
168            assert desc.startsWith("L") && desc.endsWith(";") : "Not a JVM descriptor: " + desc;
169            return JvmClassName.byInternalName(desc.substring(1, desc.length() - 1));
170        }
171    
172        @Override
173        public int hashCode() {
174            return file.hashCode();
175        }
176    
177        @Override
178        public boolean equals(Object obj) {
179            return obj instanceof VirtualFileKotlinClass && ((VirtualFileKotlinClass) obj).file.equals(file);
180        }
181    
182        @Override
183        public String toString() {
184            return getClass().getSimpleName() + ": " + file.toString();
185        }
186    }