sundar@1232: #// Usage: jjs getclassnpe.js -- sundar@1232: sundar@1232: /* sundar@1232: * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. sundar@1232: * sundar@1232: * Redistribution and use in source and binary forms, with or without sundar@1232: * modification, are permitted provided that the following conditions sundar@1232: * are met: sundar@1232: * sundar@1232: * - Redistributions of source code must retain the above copyright sundar@1232: * notice, this list of conditions and the following disclaimer. sundar@1232: * sundar@1232: * - Redistributions in binary form must reproduce the above copyright sundar@1232: * notice, this list of conditions and the following disclaimer in the sundar@1232: * documentation and/or other materials provided with the distribution. sundar@1232: * sundar@1232: * - Neither the name of Oracle nor the names of its sundar@1232: * contributors may be used to endorse or promote products derived sundar@1232: * from this software without specific prior written permission. sundar@1232: * sundar@1232: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS sundar@1232: * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, sundar@1232: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR sundar@1232: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR sundar@1232: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, sundar@1232: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, sundar@1232: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR sundar@1232: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF sundar@1232: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING sundar@1232: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS sundar@1232: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. sundar@1232: */ sundar@1232: sundar@1232: /* sundar@1232: * java.lang.Object.getClass() is sometimes used to do null check. This sundar@1232: * obfuscating Object.getClass() check relies on non-related intrinsic sundar@1232: * performance, which is potentially not available everywhere. sundar@1232: * See also http://cr.openjdk.java.net/~shade/scratch/NullChecks.java sundar@1232: * This nashorn script checks for such uses in your .java files in the sundar@1232: * given directory (recursively). sundar@1232: */ sundar@1232: sundar@1232: if (arguments.length == 0) { sundar@1232: print("Usage: jjs getclassnpe.js -- "); sundar@1232: exit(1); sundar@1232: } sundar@1232: sundar@1232: // Java types used sundar@1232: var File = Java.type("java.io.File"); sundar@1232: var Files = Java.type("java.nio.file.Files"); sundar@1232: var StringArray = Java.type("java.lang.String[]"); sundar@1232: var ToolProvider = Java.type("javax.tools.ToolProvider"); sundar@1232: var MethodInvocationTree = Java.type("com.sun.source.tree.MethodInvocationTree"); sundar@1232: var TreeScanner = Java.type("com.sun.source.util.TreeScanner"); sundar@1232: sundar@1232: // parse a specific .java file to check if it uses sundar@1232: // Object.getClass() for null check. sundar@1232: function checkGetClassNPE() { sundar@1232: // get the system compiler tool sundar@1232: var compiler = ToolProvider.systemJavaCompiler; sundar@1232: // get standard file manager sundar@1232: var fileMgr = compiler.getStandardFileManager(null, null, null); sundar@1232: // Using Java.to convert script array (arguments) to a Java String[] sundar@1232: var compUnits = fileMgr.getJavaFileObjects( sundar@1232: Java.to(arguments, StringArray)); sundar@1232: // create a new compilation task sundar@1232: var task = compiler.getTask(null, fileMgr, null, null, null, compUnits); sundar@1232: // subclass SimpleTreeVisitor - to check for obj.getClass(); statements sundar@1232: var GetClassNPEChecker = Java.extend(TreeScanner); sundar@1232: sundar@1232: var visitor = new GetClassNPEChecker() { sundar@1232: lineMap: null, sundar@1232: sourceFile: null, sundar@1232: sundar@1232: // save compilation unit details for reporting sundar@1232: visitCompilationUnit: function(node, p) { sundar@1232: this.sourceFile = node.sourceFile; sundar@1232: this.lineMap = node.lineMap; sundar@1232: return Java.super(visitor).visitCompilationUnit(node, p); sundar@1232: }, sundar@1232: sundar@1232: // look for "foo.getClass();" expression statements sundar@1232: visitExpressionStatement: function(node, p) { sundar@1232: var expr = node.expression; sundar@1232: if (expr instanceof MethodInvocationTree) { sundar@1232: var name = String(expr.methodSelect.identifier); sundar@1232: sundar@1232: // will match any "getClass" call with zero arguments! sundar@1232: if (name == "getClass" && expr.arguments.size() == 0) { sundar@1232: print(this.sourceFile.getName() sundar@1232: + " @ " sundar@1232: + this.lineMap.getLineNumber(node.pos) sundar@1232: + ":" sundar@1232: + this.lineMap.getColumnNumber(node.pos)); sundar@1232: sundar@1232: print("\t", node); sundar@1232: } sundar@1232: } sundar@1232: } sundar@1232: } sundar@1232: sundar@1232: for each (var cu in task.parse()) { sundar@1232: cu.accept(visitor, null); sundar@1232: } sundar@1232: } sundar@1232: sundar@1232: // for each ".java" file in the directory (recursively) sundar@1232: function main(dir) { sundar@1232: Files.walk(dir.toPath()). sundar@1232: forEach(function(p) { sundar@1232: var name = p.toFile().absolutePath; sundar@1232: if (name.endsWith(".java")) { sundar@1232: try { sundar@1232: checkGetClassNPE(p.toFile().getAbsolutePath()); sundar@1232: } catch (e) { sundar@1232: print(e); sundar@1232: } sundar@1232: } sundar@1232: }); sundar@1232: } sundar@1232: sundar@1232: main(new File(arguments[0]));