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 MethodAnnotationVisitor 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 org.jetbrains.asm4.AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { 155 AnnotationArgumentVisitor av = v.visitParameterAnnotation(parameter, classNameFromAsmDesc(desc)); 156 return av == null ? null : convertAnnotationVisitor(av); 157 } 158 159 @Override 160 public void visitEnd() { 161 v.visitEnd(); 162 } 163 }; 164 } 165 }, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES); 166 } 167 catch (IOException e) { 168 throw ExceptionUtils.rethrow(e); 169 } 170 } 171 172 @NotNull 173 private static JvmClassName classNameFromAsmDesc(@NotNull String desc) { 174 assert desc.startsWith("L") && desc.endsWith(";") : "Not a JVM descriptor: " + desc; 175 return JvmClassName.byInternalName(desc.substring(1, desc.length() - 1)); 176 } 177 178 @Override 179 public int hashCode() { 180 return file.hashCode(); 181 } 182 183 @Override 184 public boolean equals(Object obj) { 185 return obj instanceof VirtualFileKotlinClass && ((VirtualFileKotlinClass) obj).file.equals(file); 186 } 187 188 @Override 189 public String toString() { 190 return getClass().getSimpleName() + ": " + file.toString(); 191 } 192 }