1 /*
   2  * Copyright (c) 2015, 2016, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /**
  25  * Test that shebang handling works properly.
  26  *
  27  * @test
  28  * @option -scripting
  29  * @runif os.not.windows.cmd
  30  */
  31 
  32 // The test generates three different JavaScript source files. The first two
  33 // are generated at the beginning of the test and do not change.
  34 // * a.js
  35 //   print("A: " + arguments)
  36 // * b.js
  37 //   #!<path_to_jjs> -lalelu -- ignore
  38 //   print("B: " + arguments)
  39 //
  40 // The third file, shebang.js, is generated differently for each particular
  41 // test case, containing different shebang lines and one statement:
  42 // * shebang.js
  43 //   #!<path_to_jjs> <shebang_line>
  44 //   print("S: " + arguments)
  45 //
  46 // The path_to_jjs is extracted from the environment based on JAVA_HOME, so the
  47 // latter must be set properly.
  48 //
  49 // Each shebang.js is run four times, in all possible combinations of values
  50 // from the following two axes:
  51 // * without passing any arguments, and passing the arguments 'a.js' and
  52 //   '"hello world"' (the latter being a quoted string);
  53 // * run via jjs, and via direct shell execution (using shebang).
  54 
  55 var pseudosheb  = "#!${jjs} -lalelu -- ignore",
  56     System      = Java.type('java.lang.System'),
  57     Paths       = Java.type('java.nio.file.Paths'),
  58     Files       = Java.type('java.nio.file.Files'),
  59     Opt         = Java.type('java.nio.file.StandardOpenOption'),
  60     Arrays      = Java.type('java.util.Arrays')
  61 
  62 var sep      = Java.type('java.io.File').separator,
  63     win      = System.getProperty("os.name").startsWith("Windows"),
  64     jjsName  = "jjs" + (win ? ".exe" : ""),
  65     javaHome = System.getProperty("java.home")
  66 
  67 var jjs = javaHome + "/../bin/".replace(/\//g, sep) + jjsName
  68 if (!Files.exists(Paths.get(jjs))) {
  69     jjs = javaHome + "/bin/".replace(/\//g, sep) + jjsName
  70 }
  71 
  72 // Create and cwd to a temporary directory.
  73 
  74 var tmpdir = Files.createTempDirectory(null),
  75     tmp    = tmpdir.toAbsolutePath().toString(),
  76     curpwd = $ENV.PWD
  77 
  78 $ENV.PWD = tmp
  79 
  80 // Test cases. Each case is documented with the expected output for the four
  81 // different executions.
  82 
  83 var shebs = [
  84         // No arguments on the shebang line.
  85         // noargs jjs/shebang -> no output but "S" prefix
  86         // args jjs/shebang   -> output the arguments with "S" prefix
  87         "",
  88         // One interpreter argument.
  89         // noargs jjs/shebang -> no output but "S" prefix
  90         // args jjs/shebang   -> output the arguments with "S" prefix
  91         "--language=es6",
  92         // Two interpreter arguments.
  93         // noargs jjs/shebang -> no output but "S" prefix
  94         // args jjs/shebang   -> output the arguments with "S" prefix
  95         "--language=es6 -scripting",
  96         // One interpreter argument and a JavaScript file without shebang.
  97         // (For shebang execution, this is a pathological example, as the
  98         // JavaScript file passed as a shebang argument will be analyzed and
  99         // shebang mode will not be entered.)
 100         // noargs jjs     -> no output but "S" prefix
 101         // args jjs       -> output the arguments with "S" prefix
 102         // noargs shebang -> no output but "A" and "S" prefixes
 103         // args shebang   -> output "A", "S", and "A" prefixes, then the error
 104         //                   message:
 105         //                   "java.io.IOException: hello world is not a file"
 106         "-scripting a.js",
 107         // One interpreter argument and a JavaScript file with shebang. (This
 108         // is another pathological example, as it will force shebang mode,
 109         // leading to all subsequent arguments, including shebang.js, being
 110         // treated as arguments to the script b.js.)
 111         // noargs jjs     -> no output but the "S" prefix
 112         // args jjs       -> output the arguments with "S" prefix
 113         // noargs shebang -> output shebang.js with "B" prefix
 114         // args shebang   -> output shebang.js and the arguments with "B"
 115         //                   prefix
 116         "-scripting b.js"
 117     ]
 118 
 119 function write(file, lines) {
 120     Files.write(Paths.get(tmp, file), Arrays.asList(lines), Opt.CREATE, Opt.WRITE)
 121 }
 122 
 123 function insn(name) {
 124     return "print('${name}:' + arguments)"
 125 }
 126 
 127 function run(viajjs, name, arg1, arg2) {
 128     var prefix = viajjs ? "${jjs} -scripting " : win ? 'sh -c "' : '',
 129         suffix = viajjs ? '' : win ? '"' : ''
 130     $EXEC("${prefix}./shebang.js ${arg1} ${arg2}${suffix}")
 131     print("* ${name} via ${viajjs ? 'jjs' : 'shebang'}")
 132     print($OUT.trim())
 133     print($ERR.trim())
 134 }
 135 
 136 write('a.js', insn('A'))
 137 write('b.js', [pseudosheb, insn('B')])
 138 
 139 shebs.forEach(function(sheb) {
 140     var shebang = "#!${jjs} ${sheb}"
 141     print("<<< ${sheb} >>>")
 142     write('shebang.js', [shebang, insn('S')])
 143     $EXEC('chmod +x shebang.js')
 144     run(false, 'noargs', '', '')
 145     run(true, 'noargs', '', '')
 146     run(false, 'withargs', 'a.js', "'hello world'")
 147     run(true, 'withargs', 'a.js', "'hello world'")
 148     $EXEC('rm shebang.js')
 149 })
 150 
 151 // Cleanup.
 152 
 153 $EXEC('rm a.js b.js')
 154 $ENV.PWD = curpwd
 155 Files.delete(tmpdir)