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 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 }
--- EOF ---