1 #// Usage: jjs getclassnpe.js -- <directory> 2 3 /* 4 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * - Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * - Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * - Neither the name of Oracle nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 22 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 23 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 /* 35 * java.lang.Object.getClass() is sometimes used to do null check. This 36 * obfuscating Object.getClass() check relies on non-related intrinsic 37 * performance, which is potentially not available everywhere. 38 * See also http://cr.openjdk.java.net/~shade/scratch/NullChecks.java 39 * This nashorn script checks for such uses in your .java files in the 40 * given directory (recursively). 41 */ 42 43 if (arguments.length == 0) { 44 print("Usage: jjs getclassnpe.js -- <directory>"); 45 exit(1); 46 } 47 48 // Java types used 49 var File = Java.type("java.io.File"); 50 var Files = Java.type("java.nio.file.Files"); 51 var StringArray = Java.type("java.lang.String[]"); 52 var ToolProvider = Java.type("javax.tools.ToolProvider"); 53 var MethodInvocationTree = Java.type("com.sun.source.tree.MethodInvocationTree"); 54 var TreeScanner = Java.type("com.sun.source.util.TreeScanner"); 55 56 // parse a specific .java file to check if it uses 57 // Object.getClass() for null check. 58 function checkGetClassNPE() { 59 // get the system compiler tool 60 var compiler = ToolProvider.systemJavaCompiler; 61 // get standard file manager 62 var fileMgr = compiler.getStandardFileManager(null, null, null); 63 // Using Java.to convert script array (arguments) to a Java String[] 64 var compUnits = fileMgr.getJavaFileObjects( 65 Java.to(arguments, StringArray)); 66 // create a new compilation task 67 var task = compiler.getTask(null, fileMgr, null, null, null, compUnits); 68 // subclass SimpleTreeVisitor - to check for obj.getClass(); statements 69 var GetClassNPEChecker = Java.extend(TreeScanner); 70 71 var visitor = new GetClassNPEChecker() { 72 lineMap: null, 73 sourceFile: null, 74 75 // save compilation unit details for reporting 76 visitCompilationUnit: function(node, p) { 77 this.sourceFile = node.sourceFile; 78 this.lineMap = node.lineMap; 79 return Java.super(visitor).visitCompilationUnit(node, p); 80 }, 81 82 // look for "foo.getClass();" expression statements 83 visitExpressionStatement: function(node, p) { 84 var expr = node.expression; 85 if (expr instanceof MethodInvocationTree) { 86 var name = String(expr.methodSelect.identifier); 87 88 // will match any "getClass" call with zero arguments! 89 if (name == "getClass" && expr.arguments.size() == 0) { 90 print(this.sourceFile.getName() 91 + " @ " 92 + this.lineMap.getLineNumber(node.pos) 93 + ":" 94 + this.lineMap.getColumnNumber(node.pos)); 95 96 print("\t", node); 97 } 98 } 99 } 100 } 101 102 for each (var cu in task.parse()) { 103 cu.accept(visitor, null); 104 } 105 } 106 107 // for each ".java" file in the directory (recursively) 108 function main(dir) { 109 Files.walk(dir.toPath()). 110 forEach(function(p) { 111 var name = p.toFile().absolutePath; 112 if (name.endsWith(".java")) { 113 try { 114 checkGetClassNPE(p.toFile().getAbsolutePath()); 115 } catch (e) { 116 print(e); 117 } 118 } 119 }); 120 } 121 122 main(new File(arguments[0]));