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