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 }