ScriptClassInfoCollector.java revision 973:d564abed1e6a
1139823Simp/* 21541Srgrimes * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3166841Srwatson * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4166841Srwatson * 51541Srgrimes * This code is free software; you can redistribute it and/or modify it 61541Srgrimes * under the terms of the GNU General Public License version 2 only, as 71541Srgrimes * published by the Free Software Foundation. Oracle designates this 81541Srgrimes * particular file as subject to the "Classpath" exception as provided 91541Srgrimes * by Oracle in the LICENSE file that accompanied this code. 101541Srgrimes * 111541Srgrimes * This code is distributed in the hope that it will be useful, but WITHOUT 121541Srgrimes * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 131541Srgrimes * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 141541Srgrimes * version 2 for more details (a copy is included in the LICENSE file that 151541Srgrimes * accompanied this code). 161541Srgrimes * 171541Srgrimes * You should have received a copy of the GNU General Public License version 181541Srgrimes * 2 along with this work; if not, write to the Free Software Foundation, 191541Srgrimes * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 201541Srgrimes * 211541Srgrimes * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 221541Srgrimes * or visit www.oracle.com if you need additional information or have any 231541Srgrimes * questions. 241541Srgrimes */ 251541Srgrimes 261541Srgrimespackage jdk.nashorn.internal.tools.nasgen; 271541Srgrimes 281541Srgrimesimport static jdk.nashorn.internal.tools.nasgen.ScriptClassInfo.SCRIPT_CLASS_ANNO_DESC; 291541Srgrimesimport static jdk.nashorn.internal.tools.nasgen.ScriptClassInfo.WHERE_ENUM_DESC; 301541Srgrimes 3150477Speterimport java.io.BufferedInputStream; 321541Srgrimesimport java.io.FileInputStream; 331541Srgrimesimport java.io.IOException; 342169Spaulimport java.io.PrintStream; 35166841Srwatsonimport java.util.ArrayList; 362169Spaulimport java.util.Collections; 371541Srgrimesimport java.util.List; 381541Srgrimesimport jdk.internal.org.objectweb.asm.AnnotationVisitor; 391541Srgrimesimport jdk.internal.org.objectweb.asm.ClassReader; 40166841Srwatsonimport jdk.internal.org.objectweb.asm.ClassVisitor; 41166841Srwatsonimport jdk.internal.org.objectweb.asm.FieldVisitor; 42166841Srwatsonimport jdk.internal.org.objectweb.asm.MethodVisitor; 431541Srgrimesimport jdk.internal.org.objectweb.asm.Opcodes; 441541Srgrimesimport jdk.nashorn.internal.objects.annotations.Where; 451541Srgrimesimport jdk.nashorn.internal.tools.nasgen.MemberInfo.Kind; 461541Srgrimes 471541Srgrimes/** 481541Srgrimes * This class collects all @ScriptClass and other annotation information from a 491541Srgrimes * compiled .class file. Enforces that @Function/@Getter/@Setter/@Constructor 501541Srgrimes * methods are declared to be 'static'. 511541Srgrimes */ 521541Srgrimespublic class ScriptClassInfoCollector extends ClassVisitor { 531541Srgrimes private String scriptClassName; 54166841Srwatson private List<MemberInfo> scriptMembers; 551541Srgrimes private String javaClassName; 561541Srgrimes 571541Srgrimes ScriptClassInfoCollector(final ClassVisitor visitor) { 581541Srgrimes super(Main.ASM_VERSION, visitor); 5952904Sshin } 601541Srgrimes 611541Srgrimes ScriptClassInfoCollector() { 621541Srgrimes this(new NullVisitor()); 631541Srgrimes } 641541Srgrimes 6516143Swollman private void addScriptMember(final MemberInfo memInfo) { 661541Srgrimes if (scriptMembers == null) { 671541Srgrimes scriptMembers = new ArrayList<>(); 6828270Swollman } 6952904Sshin scriptMembers.add(memInfo); 7052904Sshin } 711541Srgrimes 721541Srgrimes @Override 731541Srgrimes public void visit(final int version, final int access, final String name, final String signature, 74166841Srwatson final String superName, final String[] interfaces) { 751541Srgrimes super.visit(version, access, name, signature, superName, interfaces); 761541Srgrimes javaClassName = name; 77166841Srwatson } 786472Swollman 796472Swollman @Override 8036079Swollman public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) { 81166841Srwatson final AnnotationVisitor delegateAV = super.visitAnnotation(desc, visible); 821541Srgrimes if (SCRIPT_CLASS_ANNO_DESC.equals(desc)) { 83166841Srwatson return new AnnotationVisitor(Main.ASM_VERSION, delegateAV) { 84166841Srwatson @Override 85166841Srwatson public void visit(final String name, final Object value) { 86166841Srwatson if ("value".equals(name)) { 87166841Srwatson scriptClassName = (String) value; 88166841Srwatson } 89166841Srwatson super.visit(name, value); 901541Srgrimes } 911541Srgrimes }; 9255205Speter } 9344078Sdfr 9444078Sdfr return delegateAV; 95166841Srwatson } 96166841Srwatson 97166841Srwatson @Override 98166841Srwatson public FieldVisitor visitField(final int fieldAccess, final String fieldName, final String fieldDesc, final String signature, final Object value) { 99166841Srwatson final FieldVisitor delegateFV = super.visitField(fieldAccess, fieldName, fieldDesc, signature, value); 100166841Srwatson 101166842Srwatson return new FieldVisitor(Main.ASM_VERSION, delegateFV) { 1021541Srgrimes @Override 103166841Srwatson public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) { 104166841Srwatson final AnnotationVisitor delegateAV = super.visitAnnotation(descriptor, visible); 105166841Srwatson 106166841Srwatson if (ScriptClassInfo.PROPERTY_ANNO_DESC.equals(descriptor)) { 107166841Srwatson final MemberInfo memInfo = new MemberInfo(); 1081541Srgrimes 1092169Spaul memInfo.setKind(Kind.PROPERTY); 1102169Spaul memInfo.setJavaName(fieldName); 111 memInfo.setJavaDesc(fieldDesc); 112 memInfo.setJavaAccess(fieldAccess); 113 114 if ((fieldAccess & Opcodes.ACC_STATIC) != 0) { 115 memInfo.setValue(value); 116 } 117 118 addScriptMember(memInfo); 119 120 return new AnnotationVisitor(Main.ASM_VERSION, delegateAV) { 121 // These could be "null" if values are not suppiled, 122 // in which case we have to use the default values. 123 private String name; 124 private Integer attributes; 125 private String clazz = ""; 126 private Where where; 127 128 @Override 129 public void visit(final String annotationName, final Object annotationValue) { 130 switch (annotationName) { 131 case "name": 132 this.name = (String) annotationValue; 133 break; 134 case "attributes": 135 this.attributes = (Integer) annotationValue; 136 break; 137 case "clazz": 138 this.clazz = (annotationValue == null) ? "" : annotationValue.toString(); 139 break; 140 default: 141 break; 142 } 143 super.visit(annotationName, annotationValue); 144 } 145 146 @Override 147 public void visitEnum(final String enumName, final String desc, final String enumValue) { 148 if ("where".equals(enumName) && WHERE_ENUM_DESC.equals(desc)) { 149 this.where = Where.valueOf(enumValue); 150 } 151 super.visitEnum(enumName, desc, enumValue); 152 } 153 154 @Override 155 public void visitEnd() { 156 super.visitEnd(); 157 memInfo.setName(name == null ? fieldName : name); 158 memInfo.setAttributes(attributes == null 159 ? MemberInfo.DEFAULT_ATTRIBUTES : attributes); 160 clazz = clazz.replace('.', '/'); 161 memInfo.setInitClass(clazz); 162 memInfo.setWhere(where == null? Where.INSTANCE : where); 163 } 164 }; 165 } 166 167 return delegateAV; 168 } 169 }; 170 } 171 172 private void error(final String javaName, final String javaDesc, final String msg) { 173 throw new RuntimeException(scriptClassName + "." + javaName + javaDesc + " : " + msg); 174 } 175 176 @Override 177 public MethodVisitor visitMethod(final int methodAccess, final String methodName, 178 final String methodDesc, final String signature, final String[] exceptions) { 179 180 final MethodVisitor delegateMV = super.visitMethod(methodAccess, methodName, methodDesc, 181 signature, exceptions); 182 183 return new MethodVisitor(Main.ASM_VERSION, delegateMV) { 184 185 @Override 186 public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) { 187 final AnnotationVisitor delegateAV = super.visitAnnotation(descriptor, visible); 188 final Kind annoKind = ScriptClassInfo.annotations.get(descriptor); 189 190 if (annoKind != null) { 191 if ((methodAccess & Opcodes.ACC_STATIC) == 0) { 192 error(methodName, methodDesc, "nasgen method annotations cannot be on instance methods"); 193 } 194 195 final MemberInfo memInfo = new MemberInfo(); 196 197 memInfo.setKind(annoKind); 198 memInfo.setJavaName(methodName); 199 memInfo.setJavaDesc(methodDesc); 200 memInfo.setJavaAccess(methodAccess); 201 202 addScriptMember(memInfo); 203 204 return new AnnotationVisitor(Main.ASM_VERSION, delegateAV) { 205 // These could be "null" if values are not suppiled, 206 // in which case we have to use the default values. 207 private String name; 208 private Integer attributes; 209 private Integer arity; 210 private Where where; 211 212 @Override 213 public void visit(final String annotationName, final Object annotationValue) { 214 switch (annotationName) { 215 case "name": 216 this.name = (String)annotationValue; 217 break; 218 case "attributes": 219 this.attributes = (Integer)annotationValue; 220 break; 221 case "arity": 222 this.arity = (Integer)annotationValue; 223 break; 224 default: 225 break; 226 } 227 228 super.visit(annotationName, annotationValue); 229 } 230 231 @Override 232 public void visitEnum(final String enumName, final String desc, final String enumValue) { 233 if ("where".equals(enumName) && WHERE_ENUM_DESC.equals(desc)) { 234 this.where = Where.valueOf(enumValue); 235 } 236 super.visitEnum(enumName, desc, enumValue); 237 } 238 239 @Override 240 public void visitEnd() { 241 super.visitEnd(); 242 243 if (memInfo.getKind() == Kind.CONSTRUCTOR) { 244 memInfo.setName(name == null ? scriptClassName : name); 245 } else { 246 memInfo.setName(name == null ? methodName : name); 247 } 248 memInfo.setAttributes(attributes == null ? MemberInfo.DEFAULT_ATTRIBUTES : attributes); 249 250 memInfo.setArity((arity == null)? MemberInfo.DEFAULT_ARITY : arity); 251 if (where == null) { 252 // by default @Getter/@Setter belongs to INSTANCE 253 // @Function belong to PROTOTYPE. 254 switch (memInfo.getKind()) { 255 case GETTER: 256 case SETTER: 257 where = Where.INSTANCE; 258 break; 259 case SPECIALIZED_CONSTRUCTOR: 260 case CONSTRUCTOR: 261 where = Where.CONSTRUCTOR; 262 break; 263 case FUNCTION: 264 where = Where.PROTOTYPE; 265 break; 266 case SPECIALIZED_FUNCTION: 267 //TODO is this correct 268 default: 269 break; 270 } 271 } 272 memInfo.setWhere(where); 273 } 274 }; 275 } 276 277 return delegateAV; 278 } 279 }; 280 } 281 282 ScriptClassInfo getScriptClassInfo() { 283 ScriptClassInfo sci = null; 284 if (scriptClassName != null) { 285 sci = new ScriptClassInfo(); 286 sci.setName(scriptClassName); 287 if (scriptMembers == null) { 288 scriptMembers = Collections.emptyList(); 289 } 290 sci.setMembers(scriptMembers); 291 sci.setJavaName(javaClassName); 292 } 293 return sci; 294 } 295 296 /** 297 * External entry point for ScriptClassInfoCollector if invoked from the command line 298 * @param args argument vector, args contains a class for which to collect info 299 * @throws IOException if there were problems parsing args or class 300 */ 301 public static void main(final String[] args) throws IOException { 302 if (args.length != 1) { 303 System.err.println("Usage: " + ScriptClassInfoCollector.class.getName() + " <class>"); 304 System.exit(1); 305 } 306 307 args[0] = args[0].replace('.', '/'); 308 final ScriptClassInfoCollector scic = new ScriptClassInfoCollector(); 309 try (final BufferedInputStream bis = new BufferedInputStream(new FileInputStream(args[0] + ".class"))) { 310 final ClassReader reader = new ClassReader(bis); 311 reader.accept(scic, 0); 312 } 313 final ScriptClassInfo sci = scic.getScriptClassInfo(); 314 final PrintStream out = System.out; 315 if (sci != null) { 316 out.println("script class: " + sci.getName()); 317 out.println("==================================="); 318 for (final MemberInfo memInfo : sci.getMembers()) { 319 out.println("kind : " + memInfo.getKind()); 320 out.println("name : " + memInfo.getName()); 321 out.println("attributes: " + memInfo.getAttributes()); 322 out.println("javaName: " + memInfo.getJavaName()); 323 out.println("javaDesc: " + memInfo.getJavaDesc()); 324 out.println("where: " + memInfo.getWhere()); 325 out.println("====================================="); 326 } 327 } else { 328 out.println(args[0] + " is not a @ScriptClass"); 329 } 330 } 331} 332