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 }