ScriptClassInfoCollector.java revision 1326:a35490e96dc5
1/* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package jdk.nashorn.internal.tools.nasgen; 27 28import static jdk.nashorn.internal.tools.nasgen.ScriptClassInfo.SCRIPT_CLASS_ANNO_DESC; 29import static jdk.nashorn.internal.tools.nasgen.ScriptClassInfo.WHERE_ENUM_DESC; 30import java.io.BufferedInputStream; 31import java.io.FileInputStream; 32import java.io.IOException; 33import java.io.PrintStream; 34import java.util.ArrayList; 35import java.util.Collections; 36import java.util.List; 37import jdk.internal.org.objectweb.asm.AnnotationVisitor; 38import jdk.internal.org.objectweb.asm.ClassReader; 39import jdk.internal.org.objectweb.asm.ClassVisitor; 40import jdk.internal.org.objectweb.asm.FieldVisitor; 41import jdk.internal.org.objectweb.asm.MethodVisitor; 42import jdk.internal.org.objectweb.asm.Opcodes; 43import jdk.internal.org.objectweb.asm.Type; 44import jdk.nashorn.internal.objects.annotations.Where; 45import jdk.nashorn.internal.tools.nasgen.MemberInfo.Kind; 46 47/** 48 * This class collects all @ScriptClass and other annotation information from a 49 * compiled .class file. Enforces that @Function/@Getter/@Setter/@Constructor 50 * methods are declared to be 'static'. 51 */ 52public class ScriptClassInfoCollector extends ClassVisitor { 53 private String scriptClassName; 54 private List<MemberInfo> scriptMembers; 55 private String javaClassName; 56 57 ScriptClassInfoCollector(final ClassVisitor visitor) { 58 super(Main.ASM_VERSION, visitor); 59 } 60 61 ScriptClassInfoCollector() { 62 this(new NullVisitor()); 63 } 64 65 private void addScriptMember(final MemberInfo memInfo) { 66 if (scriptMembers == null) { 67 scriptMembers = new ArrayList<>(); 68 } 69 scriptMembers.add(memInfo); 70 } 71 72 @Override 73 public void visit(final int version, final int access, final String name, final String signature, 74 final String superName, final String[] interfaces) { 75 super.visit(version, access, name, signature, superName, interfaces); 76 javaClassName = name; 77 } 78 79 @Override 80 public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) { 81 final AnnotationVisitor delegateAV = super.visitAnnotation(desc, visible); 82 if (SCRIPT_CLASS_ANNO_DESC.equals(desc)) { 83 return new AnnotationVisitor(Main.ASM_VERSION, delegateAV) { 84 @Override 85 public void visit(final String name, final Object value) { 86 if ("value".equals(name)) { 87 scriptClassName = (String) value; 88 } 89 super.visit(name, value); 90 } 91 }; 92 } 93 94 return delegateAV; 95 } 96 97 @Override 98 public FieldVisitor visitField(final int fieldAccess, final String fieldName, final String fieldDesc, final String signature, final Object value) { 99 final FieldVisitor delegateFV = super.visitField(fieldAccess, fieldName, fieldDesc, signature, value); 100 101 return new FieldVisitor(Main.ASM_VERSION, delegateFV) { 102 @Override 103 public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) { 104 final AnnotationVisitor delegateAV = super.visitAnnotation(descriptor, visible); 105 106 if (ScriptClassInfo.PROPERTY_ANNO_DESC.equals(descriptor)) { 107 final MemberInfo memInfo = new MemberInfo(); 108 109 memInfo.setKind(Kind.PROPERTY); 110 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 //annokind == e.g. GETTER or SPECIALIZED_FUNCTION 198 memInfo.setKind(annoKind); 199 memInfo.setJavaName(methodName); 200 memInfo.setJavaDesc(methodDesc); 201 memInfo.setJavaAccess(methodAccess); 202 203 addScriptMember(memInfo); 204 205 return new AnnotationVisitor(Main.ASM_VERSION, delegateAV) { 206 // These could be "null" if values are not suppiled, 207 // in which case we have to use the default values. 208 private String name; 209 private Integer attributes; 210 private Integer arity; 211 private Where where; 212 private boolean isSpecializedConstructor; 213 private boolean isOptimistic; 214 private Type linkLogicClass = MethodGenerator.EMPTY_LINK_LOGIC_TYPE; 215 216 @Override 217 public void visit(final String annotationName, final Object annotationValue) { 218 switch (annotationName) { 219 case "name": 220 this.name = (String)annotationValue; 221 if (name.isEmpty()) { 222 name = null; 223 } 224 break; 225 case "attributes": 226 this.attributes = (Integer)annotationValue; 227 break; 228 case "arity": 229 this.arity = (Integer)annotationValue; 230 break; 231 case "isConstructor": 232 assert annoKind == Kind.SPECIALIZED_FUNCTION; 233 this.isSpecializedConstructor = (Boolean)annotationValue; 234 break; 235 case "isOptimistic": 236 assert annoKind == Kind.SPECIALIZED_FUNCTION; 237 this.isOptimistic = (Boolean)annotationValue; 238 break; 239 case "linkLogic": 240 this.linkLogicClass = (Type)annotationValue; 241 break; 242 default: 243 break; 244 } 245 246 super.visit(annotationName, annotationValue); 247 } 248 249 @Override 250 public void visitEnum(final String enumName, final String desc, final String enumValue) { 251 switch (enumName) { 252 case "where": 253 if (WHERE_ENUM_DESC.equals(desc)) { 254 this.where = Where.valueOf(enumValue); 255 } 256 break; 257 default: 258 break; 259 } 260 super.visitEnum(enumName, desc, enumValue); 261 } 262 263 @SuppressWarnings("fallthrough") 264 @Override 265 public void visitEnd() { 266 super.visitEnd(); 267 268 if (memInfo.getKind() == Kind.CONSTRUCTOR) { 269 memInfo.setName(name == null ? scriptClassName : name); 270 } else { 271 memInfo.setName(name == null ? methodName : name); 272 } 273 memInfo.setAttributes(attributes == null ? MemberInfo.DEFAULT_ATTRIBUTES : attributes); 274 275 memInfo.setArity((arity == null)? MemberInfo.DEFAULT_ARITY : arity); 276 if (where == null) { 277 // by default @Getter/@Setter belongs to INSTANCE 278 // @Function belong to PROTOTYPE. 279 switch (memInfo.getKind()) { 280 case GETTER: 281 case SETTER: 282 where = Where.INSTANCE; 283 break; 284 case CONSTRUCTOR: 285 where = Where.CONSTRUCTOR; 286 break; 287 case FUNCTION: 288 where = Where.PROTOTYPE; 289 break; 290 case SPECIALIZED_FUNCTION: 291 where = isSpecializedConstructor? Where.CONSTRUCTOR : Where.PROTOTYPE; 292 //fallthru 293 default: 294 break; 295 } 296 } 297 memInfo.setWhere(where); 298 memInfo.setLinkLogicClass(linkLogicClass); 299 memInfo.setIsSpecializedConstructor(isSpecializedConstructor); 300 memInfo.setIsOptimistic(isOptimistic); 301 } 302 }; 303 } 304 305 return delegateAV; 306 } 307 }; 308 } 309 310 ScriptClassInfo getScriptClassInfo() { 311 ScriptClassInfo sci = null; 312 if (scriptClassName != null) { 313 sci = new ScriptClassInfo(); 314 sci.setName(scriptClassName); 315 if (scriptMembers == null) { 316 scriptMembers = Collections.emptyList(); 317 } 318 sci.setMembers(scriptMembers); 319 sci.setJavaName(javaClassName); 320 } 321 return sci; 322 } 323 324 /** 325 * External entry point for ScriptClassInfoCollector if invoked from the command line 326 * @param args argument vector, args contains a class for which to collect info 327 * @throws IOException if there were problems parsing args or class 328 */ 329 public static void main(final String[] args) throws IOException { 330 if (args.length != 1) { 331 System.err.println("Usage: " + ScriptClassInfoCollector.class.getName() + " <class>"); 332 System.exit(1); 333 } 334 335 args[0] = args[0].replace('.', '/'); 336 final ScriptClassInfoCollector scic = new ScriptClassInfoCollector(); 337 try (final BufferedInputStream bis = new BufferedInputStream(new FileInputStream(args[0] + ".class"))) { 338 final ClassReader reader = new ClassReader(bis); 339 reader.accept(scic, 0); 340 } 341 final ScriptClassInfo sci = scic.getScriptClassInfo(); 342 final PrintStream out = System.out; 343 if (sci != null) { 344 out.println("script class: " + sci.getName()); 345 out.println("==================================="); 346 for (final MemberInfo memInfo : sci.getMembers()) { 347 out.println("kind : " + memInfo.getKind()); 348 out.println("name : " + memInfo.getName()); 349 out.println("attributes: " + memInfo.getAttributes()); 350 out.println("javaName: " + memInfo.getJavaName()); 351 out.println("javaDesc: " + memInfo.getJavaDesc()); 352 out.println("where: " + memInfo.getWhere()); 353 out.println("====================================="); 354 } 355 } else { 356 out.println(args[0] + " is not a @ScriptClass"); 357 } 358 } 359} 360