1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3 *
4 * This code is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 only, as
6 * published by the Free Software Foundation. Oracle designates this
7 * particular file as subject to the "Classpath" exception as provided
8 * by Oracle in the LICENSE file that accompanied this code.
9 *
10 * This code is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21 * or visit www.oracle.com if you need additional information or have any
22 * questions.
23 */
24
25 /*
26 * This file is available under and governed by the GNU General Public
27 * License version 2 only, as published by the Free Software Foundation.
28 * However, the following notice accompanied the original version of this
29 * file:
30 *
31 * ASM: a very small and fast Java bytecode manipulation framework
32 * Copyright (c) 2000-2011 INRIA, France Telecom
33 * All rights reserved.
34 *
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
37 * are met:
38 * 1. Redistributions of source code must retain the above copyright
39 * notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 * notice, this list of conditions and the following disclaimer in the
42 * documentation and/or other materials provided with the distribution.
43 * 3. Neither the name of the copyright holders nor the names of its
44 * contributors may be used to endorse or promote products derived from
45 * this software without specific prior written permission.
46 *
47 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
48 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
51 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
52 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
53 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
54 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
55 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
56 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
57 * THE POSSIBILITY OF SUCH DAMAGE.
58 */
59 package jdk.internal.org.objectweb.asm.util;
60
61 import java.io.FileInputStream;
62 import java.io.PrintWriter;
63 import java.util.ArrayList;
64 import java.util.HashMap;
65 import java.util.Iterator;
66 import java.util.List;
67 import java.util.Map;
68
69 import jdk.internal.org.objectweb.asm.AnnotationVisitor;
70 import jdk.internal.org.objectweb.asm.Attribute;
71 import jdk.internal.org.objectweb.asm.ClassReader;
72 import jdk.internal.org.objectweb.asm.ClassVisitor;
73 import jdk.internal.org.objectweb.asm.FieldVisitor;
74 import jdk.internal.org.objectweb.asm.Label;
75 import jdk.internal.org.objectweb.asm.MethodVisitor;
76 import jdk.internal.org.objectweb.asm.Opcodes;
77 import jdk.internal.org.objectweb.asm.Type;
78 import jdk.internal.org.objectweb.asm.tree.ClassNode;
79 import jdk.internal.org.objectweb.asm.tree.MethodNode;
80 import jdk.internal.org.objectweb.asm.tree.analysis.Analyzer;
81 import jdk.internal.org.objectweb.asm.tree.analysis.BasicValue;
82 import jdk.internal.org.objectweb.asm.tree.analysis.Frame;
83 import jdk.internal.org.objectweb.asm.tree.analysis.SimpleVerifier;
84
85 /**
86 * A {@link ClassVisitor} that checks that its methods are properly used. More
87 * precisely this class adapter checks each method call individually, based
88 * <i>only</i> on its arguments, but does <i>not</i> check the <i>sequence</i>
89 * of method calls. For example, the invalid sequence
90 * <tt>visitField(ACC_PUBLIC, "i", "I", null)</tt> <tt>visitField(ACC_PUBLIC,
91 * "i", "D", null)</tt>
92 * will <i>not</i> be detected by this class adapter.
93 *
94 * <p><code>CheckClassAdapter</code> can be also used to verify bytecode
95 * transformations in order to make sure transformed bytecode is sane. For
96 * example:
97 *
98 * <pre>
99 * InputStream is = ...; // get bytes for the source class
100 * ClassReader cr = new ClassReader(is);
101 * ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
102 * ClassVisitor cv = new <b>MyClassAdapter</b>(new CheckClassAdapter(cw));
103 * cr.accept(cv, 0);
104 *
105 * StringWriter sw = new StringWriter();
106 * PrintWriter pw = new PrintWriter(sw);
107 * CheckClassAdapter.verify(new ClassReader(cw.toByteArray()), false, pw);
108 * assertTrue(sw.toString(), sw.toString().length()==0);
109 * </pre>
110 *
111 * Above code runs transformed bytecode trough the
112 * <code>CheckClassAdapter</code>. It won't be exactly the same verification
113 * as JVM does, but it run data flow analysis for the code of each method and
114 * checks that expectations are met for each method instruction.
115 *
116 * <p>If method bytecode has errors, assertion text will show the erroneous
117 * instruction number and dump of the failed method with information about
118 * locals and stack slot for each instruction. For example (format is -
119 * insnNumber locals : stack):
120 *
121 * <pre>
122 * jdk.internal.org.objectweb.asm.tree.analysis.AnalyzerException: Error at instruction 71: Expected I, but found .
123 * at jdk.internal.org.objectweb.asm.tree.analysis.Analyzer.analyze(Analyzer.java:289)
124 * at jdk.internal.org.objectweb.asm.util.CheckClassAdapter.verify(CheckClassAdapter.java:135)
125 * ...
126 * remove()V
127 * 00000 LinkedBlockingQueue$Itr . . . . . . . . :
128 * ICONST_0
129 * 00001 LinkedBlockingQueue$Itr . . . . . . . . : I
130 * ISTORE 2
131 * 00001 LinkedBlockingQueue$Itr <b>.</b> I . . . . . . :
132 * ...
133 *
134 * 00071 LinkedBlockingQueue$Itr <b>.</b> I . . . . . . :
135 * ILOAD 1
136 * 00072 <b>?</b>
137 * INVOKESPECIAL java/lang/Integer.<init> (I)V
138 * ...
139 * </pre>
140 *
141 * In the above output you can see that variable 1 loaded by
142 * <code>ILOAD 1</code> instruction at position <code>00071</code> is not
143 * initialized. You can also see that at the beginning of the method (code
144 * inserted by the transformation) variable 2 is initialized.
145 *
146 * <p>Note that when used like that, <code>CheckClassAdapter.verify()</code>
147 * can trigger additional class loading, because it is using
148 * <code>SimpleVerifier</code>.
149 *
150 * @author Eric Bruneton
151 */
152 public class CheckClassAdapter extends ClassVisitor {
153
154 /**
155 * The class version number.
156 */
157 private int version;
158
159 /**
160 * <tt>true</tt> if the visit method has been called.
161 */
162 private boolean start;
163
164 /**
165 * <tt>true</tt> if the visitSource method has been called.
166 */
167 private boolean source;
168
169 /**
170 * <tt>true</tt> if the visitOuterClass method has been called.
171 */
172 private boolean outer;
173
174 /**
175 * <tt>true</tt> if the visitEnd method has been called.
176 */
177 private boolean end;
178
179 /**
180 * The already visited labels. This map associate Integer values to Label
181 * keys.
182 */
183 private Map<Label, Integer> labels;
184
185 /**
186 * <tt>true</tt> if the method code must be checked with a BasicVerifier.
187 */
188 private boolean checkDataFlow;
189
190 /**
191 * Checks a given class. <p> Usage: CheckClassAdapter <binary
192 * class name or class file name>
193 *
194 * @param args the command line arguments.
195 *
196 * @throws Exception if the class cannot be found, or if an IO exception
197 * occurs.
198 */
199 public static void main(final String[] args) throws Exception {
200 if (args.length != 1) {
201 System.err.println("Verifies the given class.");
202 System.err.println("Usage: CheckClassAdapter "
203 + "<fully qualified class name or class file name>");
204 return;
205 }
206 ClassReader cr;
207 if (args[0].endsWith(".class")) {
208 cr = new ClassReader(new FileInputStream(args[0]));
209 } else {
210 cr = new ClassReader(args[0]);
211 }
212
213 verify(cr, false, new PrintWriter(System.err));
214 }
215
216 /**
217 * Checks a given class.
218 *
219 * @param cr a <code>ClassReader</code> that contains bytecode for the
220 * analysis.
221 * @param loader a <code>ClassLoader</code> which will be used to load
222 * referenced classes. This is useful if you are verifiying multiple
223 * interdependent classes.
224 * @param dump true if bytecode should be printed out not only when errors
225 * are found.
226 * @param pw write where results going to be printed
227 */
228 public static void verify(
229 final ClassReader cr,
230 final ClassLoader loader,
231 final boolean dump,
232 final PrintWriter pw)
233 {
234 ClassNode cn = new ClassNode();
235 cr.accept(new CheckClassAdapter(cn, false), ClassReader.SKIP_DEBUG);
236
237 Type syperType = cn.superName == null
238 ? null
239 : Type.getObjectType(cn.superName);
240 List<MethodNode> methods = cn.methods;
241
242 List<Type> interfaces = new ArrayList<Type>();
243 for (Iterator<String> i = cn.interfaces.iterator(); i.hasNext();) {
244 interfaces.add(Type.getObjectType(i.next().toString()));
245 }
246
247 for (int i = 0; i < methods.size(); ++i) {
248 MethodNode method = methods.get(i);
249 SimpleVerifier verifier = new SimpleVerifier(Type.getObjectType(cn.name),
250 syperType,
251 interfaces,
252 (cn.access & Opcodes.ACC_INTERFACE) != 0);
253 Analyzer<BasicValue> a = new Analyzer<BasicValue>(verifier);
254 if (loader != null) {
255 verifier.setClassLoader(loader);
256 }
257 try {
258 a.analyze(cn.name, method);
259 if (!dump) {
260 continue;
261 }
262 } catch (Exception e) {
263 e.printStackTrace(pw);
264 }
265 printAnalyzerResult(method, a, pw);
266 }
267 pw.flush();
268 }
269
270 /**
271 * Checks a given class
272 *
273 * @param cr a <code>ClassReader</code> that contains bytecode for the
274 * analysis.
275 * @param dump true if bytecode should be printed out not only when errors
276 * are found.
277 * @param pw write where results going to be printed
278 */
279 public static void verify(
280 final ClassReader cr,
281 final boolean dump,
282 final PrintWriter pw)
283 {
284 verify(cr, null, dump, pw);
285 }
286
287 static void printAnalyzerResult(
288 MethodNode method,
289 Analyzer<BasicValue> a,
290 final PrintWriter pw)
291 {
292 Frame<BasicValue>[] frames = a.getFrames();
293 Textifier t = new Textifier();
294 TraceMethodVisitor mv = new TraceMethodVisitor(t);
295
296 pw.println(method.name + method.desc);
297 for (int j = 0; j < method.instructions.size(); ++j) {
298 method.instructions.get(j).accept(mv);
299
300 StringBuffer s = new StringBuffer();
301 Frame<BasicValue> f = frames[j];
302 if (f == null) {
303 s.append('?');
304 } else {
305 for (int k = 0; k < f.getLocals(); ++k) {
306 s.append(getShortName(f.getLocal(k).toString()))
307 .append(' ');
308 }
309 s.append(" : ");
310 for (int k = 0; k < f.getStackSize(); ++k) {
311 s.append(getShortName(f.getStack(k).toString()))
312 .append(' ');
313 }
314 }
315 while (s.length() < method.maxStack + method.maxLocals + 1) {
316 s.append(' ');
317 }
318 pw.print(Integer.toString(j + 100000).substring(1));
319 pw.print(" " + s + " : " + t.text.get(t.text.size() - 1));
320 }
321 for (int j = 0; j < method.tryCatchBlocks.size(); ++j) {
322 method.tryCatchBlocks.get(j).accept(mv);
323 pw.print(" " + t.text.get(t.text.size() - 1));
324 }
325 pw.println();
326 }
327
328 private static String getShortName(final String name) {
329 int n = name.lastIndexOf('/');
330 int k = name.length();
331 if (name.charAt(k - 1) == ';') {
332 k--;
333 }
334 return n == -1 ? name : name.substring(n + 1, k);
335 }
336
337 /**
338 * Constructs a new {@link CheckClassAdapter}. <i>Subclasses must not use
339 * this constructor</i>. Instead, they must use the
340 * {@link #CheckClassAdapter(int, ClassVisitor, boolean)} version.
341 *
342 * @param cv the class visitor to which this adapter must delegate calls.
343 */
344 public CheckClassAdapter(final ClassVisitor cv) {
345 this(cv, true);
346 }
347
348 /**
349 * Constructs a new {@link CheckClassAdapter}. <i>Subclasses must not use
350 * this constructor</i>. Instead, they must use the
351 * {@link #CheckClassAdapter(int, ClassVisitor, boolean)} version.
352 *
353 * @param cv the class visitor to which this adapter must delegate calls.
354 * @param checkDataFlow <tt>true</tt> to perform basic data flow checks, or
355 * <tt>false</tt> to not perform any data flow check (see
356 * {@link CheckMethodAdapter}). This option requires valid maxLocals
357 * and maxStack values.
358 */
359 public CheckClassAdapter(final ClassVisitor cv, final boolean checkDataFlow)
360 {
361 this(Opcodes.ASM4, cv, checkDataFlow);
362 }
363
364 /**
365 * Constructs a new {@link CheckClassAdapter}.
366 *
367 * @param api the ASM API version implemented by this visitor. Must be one
368 * of {@link Opcodes#ASM4}.
369 * @param cv the class visitor to which this adapter must delegate calls.
370 * @param checkDataFlow <tt>true</tt> to perform basic data flow checks, or
371 * <tt>false</tt> to not perform any data flow check (see
372 * {@link CheckMethodAdapter}). This option requires valid maxLocals
373 * and maxStack values.
374 */
375 protected CheckClassAdapter(
376 final int api,
377 final ClassVisitor cv,
378 final boolean checkDataFlow)
379 {
380 super(api, cv);
381 this.labels = new HashMap<Label, Integer>();
382 this.checkDataFlow = checkDataFlow;
383 }
384
385 // ------------------------------------------------------------------------
386 // Implementation of the ClassVisitor interface
387 // ------------------------------------------------------------------------
388
389 @Override
390 public void visit(
391 final int version,
392 final int access,
393 final String name,
394 final String signature,
395 final String superName,
396 final String[] interfaces)
397 {
398 if (start) {
399 throw new IllegalStateException("visit must be called only once");
400 }
401 start = true;
402 checkState();
403 checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL
404 + Opcodes.ACC_SUPER + Opcodes.ACC_INTERFACE
405 + Opcodes.ACC_ABSTRACT + Opcodes.ACC_SYNTHETIC
406 + Opcodes.ACC_ANNOTATION + Opcodes.ACC_ENUM
407 + Opcodes.ACC_DEPRECATED
408 + 0x40000); // ClassWriter.ACC_SYNTHETIC_ATTRIBUTE
409 if (name == null || !name.endsWith("package-info")) {
410 CheckMethodAdapter.checkInternalName(name, "class name");
411 }
412 if ("java/lang/Object".equals(name)) {
413 if (superName != null) {
414 throw new IllegalArgumentException("The super class name of the Object class must be 'null'");
415 }
416 } else {
417 CheckMethodAdapter.checkInternalName(superName, "super class name");
418 }
419 if (signature != null) {
420 CheckMethodAdapter.checkClassSignature(signature);
421 }
422 if ((access & Opcodes.ACC_INTERFACE) != 0) {
423 if (!"java/lang/Object".equals(superName)) {
424 throw new IllegalArgumentException("The super class name of interfaces must be 'java/lang/Object'");
425 }
426 }
427 if (interfaces != null) {
428 for (int i = 0; i < interfaces.length; ++i) {
429 CheckMethodAdapter.checkInternalName(interfaces[i],
430 "interface name at index " + i);
431 }
432 }
433 this.version = version;
434 super.visit(version, access, name, signature, superName, interfaces);
435 }
436
437 @Override
438 public void visitSource(final String file, final String debug) {
439 checkState();
440 if (source) {
441 throw new IllegalStateException("visitSource can be called only once.");
442 }
443 source = true;
444 super.visitSource(file, debug);
445 }
446
447 @Override
448 public void visitOuterClass(
449 final String owner,
450 final String name,
451 final String desc)
452 {
453 checkState();
454 if (outer) {
455 throw new IllegalStateException("visitOuterClass can be called only once.");
456 }
457 outer = true;
458 if (owner == null) {
459 throw new IllegalArgumentException("Illegal outer class owner");
460 }
461 if (desc != null) {
462 CheckMethodAdapter.checkMethodDesc(desc);
463 }
464 super.visitOuterClass(owner, name, desc);
465 }
466
467 @Override
468 public void visitInnerClass(
469 final String name,
470 final String outerName,
471 final String innerName,
472 final int access)
473 {
474 checkState();
475 CheckMethodAdapter.checkInternalName(name, "class name");
476 if (outerName != null) {
477 CheckMethodAdapter.checkInternalName(outerName, "outer class name");
478 }
479 if (innerName != null) {
480 CheckMethodAdapter.checkIdentifier(innerName, "inner class name");
481 }
482 checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE
483 + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC
484 + Opcodes.ACC_FINAL + Opcodes.ACC_INTERFACE
485 + Opcodes.ACC_ABSTRACT + Opcodes.ACC_SYNTHETIC
486 + Opcodes.ACC_ANNOTATION + Opcodes.ACC_ENUM);
487 super.visitInnerClass(name, outerName, innerName, access);
488 }
489
490 @Override
491 public FieldVisitor visitField(
492 final int access,
493 final String name,
494 final String desc,
495 final String signature,
496 final Object value)
497 {
498 checkState();
499 checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE
500 + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC
501 + Opcodes.ACC_FINAL + Opcodes.ACC_VOLATILE
502 + Opcodes.ACC_TRANSIENT + Opcodes.ACC_SYNTHETIC
503 + Opcodes.ACC_ENUM + Opcodes.ACC_DEPRECATED
504 + 0x40000); // ClassWriter.ACC_SYNTHETIC_ATTRIBUTE
505 CheckMethodAdapter.checkUnqualifiedName(version, name, "field name");
506 CheckMethodAdapter.checkDesc(desc, false);
507 if (signature != null) {
508 CheckMethodAdapter.checkFieldSignature(signature);
509 }
510 if (value != null) {
511 CheckMethodAdapter.checkConstant(value);
512 }
513 FieldVisitor av = super.visitField(access, name, desc, signature, value);
514 return new CheckFieldAdapter(av);
515 }
516
517 @Override
518 public MethodVisitor visitMethod(
519 final int access,
520 final String name,
521 final String desc,
522 final String signature,
523 final String[] exceptions)
524 {
525 checkState();
526 checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE
527 + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC
528 + Opcodes.ACC_FINAL + Opcodes.ACC_SYNCHRONIZED
529 + Opcodes.ACC_BRIDGE + Opcodes.ACC_VARARGS + Opcodes.ACC_NATIVE
530 + Opcodes.ACC_ABSTRACT + Opcodes.ACC_STRICT
531 + Opcodes.ACC_SYNTHETIC + Opcodes.ACC_DEPRECATED
532 + 0x40000); // ClassWriter.ACC_SYNTHETIC_ATTRIBUTE
533 CheckMethodAdapter.checkMethodIdentifier(version, name, "method name");
534 CheckMethodAdapter.checkMethodDesc(desc);
535 if (signature != null) {
536 CheckMethodAdapter.checkMethodSignature(signature);
537 }
538 if (exceptions != null) {
539 for (int i = 0; i < exceptions.length; ++i) {
540 CheckMethodAdapter.checkInternalName(exceptions[i],
541 "exception name at index " + i);
542 }
543 }
544 CheckMethodAdapter cma;
545 if (checkDataFlow) {
546 cma = new CheckMethodAdapter(access,
547 name,
548 desc,
549 super.visitMethod(access, name, desc, signature, exceptions),
550 labels);
551 } else {
552 cma = new CheckMethodAdapter(super.visitMethod(access,
553 name,
554 desc,
555 signature,
556 exceptions), labels);
557 }
558 cma.version = version;
559 return cma;
560 }
561
562 @Override
563 public AnnotationVisitor visitAnnotation(
564 final String desc,
565 final boolean visible)
566 {
567 checkState();
568 CheckMethodAdapter.checkDesc(desc, false);
569 return new CheckAnnotationAdapter(super.visitAnnotation(desc, visible));
570 }
571
572 @Override
573 public void visitAttribute(final Attribute attr) {
574 checkState();
575 if (attr == null) {
576 throw new IllegalArgumentException("Invalid attribute (must not be null)");
577 }
578 super.visitAttribute(attr);
579 }
580
581 @Override
582 public void visitEnd() {
583 checkState();
584 end = true;
585 super.visitEnd();
586 }
587
588 // ------------------------------------------------------------------------
589 // Utility methods
590 // ------------------------------------------------------------------------
591
592 /**
593 * Checks that the visit method has been called and that visitEnd has not
594 * been called.
595 */
596 private void checkState() {
597 if (!start) {
598 throw new IllegalStateException("Cannot visit member before visit has been called.");
599 }
600 if (end) {
601 throw new IllegalStateException("Cannot visit member after visitEnd has been called.");
602 }
603 }
604
605 /**
606 * Checks that the given access flags do not contain invalid flags. This
607 * method also checks that mutually incompatible flags are not set
608 * simultaneously.
609 *
610 * @param access the access flags to be checked
611 * @param possibleAccess the valid access flags.
612 */
613 static void checkAccess(final int access, final int possibleAccess) {
614 if ((access & ~possibleAccess) != 0) {
615 throw new IllegalArgumentException("Invalid access flags: "
616 + access);
617 }
618 int pub = (access & Opcodes.ACC_PUBLIC) == 0 ? 0 : 1;
619 int pri = (access & Opcodes.ACC_PRIVATE) == 0 ? 0 : 1;
620 int pro = (access & Opcodes.ACC_PROTECTED) == 0 ? 0 : 1;
621 if (pub + pri + pro > 1) {
622 throw new IllegalArgumentException("public private and protected are mutually exclusive: "
623 + access);
624 }
625 int fin = (access & Opcodes.ACC_FINAL) == 0 ? 0 : 1;
626 int abs = (access & Opcodes.ACC_ABSTRACT) == 0 ? 0 : 1;
627 if (fin + abs > 1) {
628 throw new IllegalArgumentException("final and abstract are mutually exclusive: "
629 + access);
630 }
631 }
632 }
--- EOF ---