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 26 package jdk.nashorn.internal.tools.nasgen; 27 28 import static jdk.nashorn.internal.tools.nasgen.ScriptClassInfo.SCRIPT_CLASS_ANNO_DESC; 29 import static jdk.nashorn.internal.tools.nasgen.ScriptClassInfo.WHERE_ENUM_DESC; 30 import java.io.BufferedInputStream; 31 import java.io.FileInputStream; 32 import java.io.IOException; 33 import java.io.PrintStream; 34 import java.util.ArrayList; 35 import java.util.Collections; 36 import java.util.List; 37 import jdk.internal.org.objectweb.asm.AnnotationVisitor; 38 import jdk.internal.org.objectweb.asm.ClassReader; 39 import jdk.internal.org.objectweb.asm.ClassVisitor; 40 import jdk.internal.org.objectweb.asm.FieldVisitor; 41 import jdk.internal.org.objectweb.asm.MethodVisitor; 42 import jdk.internal.org.objectweb.asm.Opcodes; 43 import jdk.internal.org.objectweb.asm.Type; 44 import jdk.nashorn.internal.objects.annotations.Where; 45 import 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 */ 52 public 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 supplied, 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 == 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 supplied, 207 // in which case we have to use the default values. 208 private String name; 209 private String documentation; 210 private Integer attributes; 211 private Integer arity; 212 private Where where; 213 private boolean isSpecializedConstructor; 214 private boolean isOptimistic; 215 private Type linkLogicClass = MethodGenerator.EMPTY_LINK_LOGIC_TYPE; 216 217 @Override 218 public void visit(final String annotationName, final Object annotationValue) { 219 switch (annotationName) { 220 case "name": 221 this.name = (String)annotationValue; 222 if (name.isEmpty()) { 223 name = null; 224 } 225 break; 226 case "documentation": 227 this.documentation = (String)annotationValue; 228 if (documentation.isEmpty()) { 229 documentation = null; 230 } 231 232 break; 233 case "attributes": 234 this.attributes = (Integer)annotationValue; 235 break; 236 case "arity": 237 this.arity = (Integer)annotationValue; 238 break; 239 case "isConstructor": 240 assert annoKind == Kind.SPECIALIZED_FUNCTION; 241 this.isSpecializedConstructor = (Boolean)annotationValue; 242 break; 243 case "isOptimistic": 244 assert annoKind == Kind.SPECIALIZED_FUNCTION; 245 this.isOptimistic = (Boolean)annotationValue; 246 break; 247 case "linkLogic": 248 this.linkLogicClass = (Type)annotationValue; 249 break; 250 default: 251 break; 252 } 253 254 super.visit(annotationName, annotationValue); 255 } 256 257 @Override 258 public void visitEnum(final String enumName, final String desc, final String enumValue) { 259 switch (enumName) { 260 case "where": 261 if (WHERE_ENUM_DESC.equals(desc)) { 262 this.where = Where.valueOf(enumValue); 263 } 264 break; 265 default: 266 break; 267 } 268 super.visitEnum(enumName, desc, enumValue); 269 } 270 271 @SuppressWarnings("fallthrough") 272 @Override 273 public void visitEnd() { 274 super.visitEnd(); 275 276 if (memInfo.getKind() == Kind.CONSTRUCTOR) { 277 memInfo.setName(name == null ? scriptClassName : name); 278 } else { 279 memInfo.setName(name == null ? methodName : name); 280 } 281 282 memInfo.setDocumentation(documentation); 283 memInfo.setAttributes(attributes == null ? MemberInfo.DEFAULT_ATTRIBUTES : attributes); 284 285 memInfo.setArity((arity == null)? MemberInfo.DEFAULT_ARITY : arity); 286 if (where == null) { 287 // by default @Getter/@Setter belongs to INSTANCE 288 // @Function belong to PROTOTYPE. 289 switch (memInfo.getKind()) { 290 case GETTER: 291 case SETTER: 292 where = Where.INSTANCE; 293 break; 294 case CONSTRUCTOR: 295 where = Where.CONSTRUCTOR; 296 break; 297 case FUNCTION: 298 where = Where.PROTOTYPE; 299 break; 300 case SPECIALIZED_FUNCTION: 301 where = isSpecializedConstructor? Where.CONSTRUCTOR : Where.PROTOTYPE; 302 //fallthru 303 default: 304 break; 305 } 306 } 307 memInfo.setWhere(where); 308 memInfo.setLinkLogicClass(linkLogicClass); 309 memInfo.setIsSpecializedConstructor(isSpecializedConstructor); 310 memInfo.setIsOptimistic(isOptimistic); 311 } 312 }; 313 } 314 315 return delegateAV; 316 } 317 }; 318 } 319 320 ScriptClassInfo getScriptClassInfo() { 321 ScriptClassInfo sci = null; 322 if (scriptClassName != null) { 323 sci = new ScriptClassInfo(); 324 sci.setName(scriptClassName); 325 if (scriptMembers == null) { 326 scriptMembers = Collections.emptyList(); 327 } 328 sci.setMembers(scriptMembers); 329 sci.setJavaName(javaClassName); 330 } 331 return sci; 332 } 333 334 /** 335 * External entry point for ScriptClassInfoCollector if invoked from the command line 336 * @param args argument vector, args contains a class for which to collect info 337 * @throws IOException if there were problems parsing args or class 338 */ 339 public static void main(final String[] args) throws IOException { 340 if (args.length != 1) { 341 System.err.println("Usage: " + ScriptClassInfoCollector.class.getName() + " <class>"); 342 System.exit(1); 343 } 344 345 args[0] = args[0].replace('.', '/'); 346 final ScriptClassInfoCollector scic = new ScriptClassInfoCollector(); 347 try (final BufferedInputStream bis = new BufferedInputStream(new FileInputStream(args[0] + ".class"))) { 348 final ClassReader reader = new ClassReader(bis); 349 reader.accept(scic, 0); 350 } 351 final ScriptClassInfo sci = scic.getScriptClassInfo(); 352 final PrintStream out = System.out; 353 if (sci != null) { 354 out.println("script class: " + sci.getName()); 355 out.println("==================================="); 356 for (final MemberInfo memInfo : sci.getMembers()) { 357 out.println("kind : " + memInfo.getKind()); 358 out.println("name : " + memInfo.getName()); 359 out.println("attributes: " + memInfo.getAttributes()); 360 out.println("javaName: " + memInfo.getJavaName()); 361 out.println("javaDesc: " + memInfo.getJavaDesc()); 362 out.println("where: " + memInfo.getWhere()); 363 out.println("====================================="); 364 } 365 } else { 366 out.println(args[0] + " is not a @ScriptClass"); 367 } 368 } 369 }