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]));