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