Merge
authorlana
Thu, 10 Mar 2016 09:49:51 -0800
changeset 36482 ee5a7cd1e795
parent 36478 d8487e717bd4 (current diff)
parent 36481 9826c19a5310 (diff)
child 36483 2bc58b5e99d3
Merge
--- a/nashorn/.hgignore	Thu Mar 10 09:28:20 2016 -0800
+++ b/nashorn/.hgignore	Thu Mar 10 09:49:51 2016 -0800
@@ -28,3 +28,4 @@
 .project
 .externalToolBuilders/*
 .settings/*
+NashornProfile.txt
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java	Thu Mar 10 09:28:20 2016 -0800
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java	Thu Mar 10 09:49:51 2016 -0800
@@ -2724,12 +2724,9 @@
             // Retrieve current state of ENV variables.
             env.putAll(System.getenv(), scriptEnv._strict);
 
-            // Some platforms, e.g., Windows, do not define the PWD environment
-            // variable, so that the $ENV.PWD property needs to be explicitly
-            // set.
-            if (!env.containsKey(ScriptingFunctions.PWD_NAME)) {
-                env.put(ScriptingFunctions.PWD_NAME, System.getProperty("user.dir"), scriptEnv._strict);
-            }
+            // Set the PWD variable to a value that is guaranteed to be understood
+            // by the underlying platform.
+            env.put(ScriptingFunctions.PWD_NAME, System.getProperty("user.dir"), scriptEnv._strict);
         }
         addOwnProperty(ScriptingFunctions.ENV_NAME, Attribute.NOT_ENUMERABLE, env);
 
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CommandExecutor.java	Thu Mar 10 09:28:20 2016 -0800
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CommandExecutor.java	Thu Mar 10 09:49:51 2016 -0800
@@ -64,6 +64,9 @@
         return System.getProperty("os.name").contains("Windows");
     });
 
+    // Cygwin drive alias prefix.
+    private static final String CYGDRIVE = "/cygdrive/";
+
     // User's home directory
     private static final String HOME_DIRECTORY =
         AccessController.doPrivileged((PrivilegedAction<String>)() -> {
@@ -246,18 +249,22 @@
         // Stream to copy to.
         private final OutputStream output;
 
+        private final Thread thread;
+
         Piper(final InputStream input, final OutputStream output) {
             this.input = input;
             this.output = output;
+            this.thread = new Thread(this, "$EXEC Piper");
         }
 
         /**
          * start - start the Piper in a new daemon thread
+         * @return this Piper
          */
-        void start() {
-            Thread thread = new Thread(this, "$EXEC Piper");
+        Piper start() {
             thread.setDaemon(true);
             thread.start();
+            return this;
         }
 
         /**
@@ -292,6 +299,10 @@
             }
         }
 
+        public void join() throws InterruptedException {
+            thread.join();
+        }
+
         // Exit thread.
     }
 
@@ -388,7 +399,7 @@
      * @return resolved Path to file
      */
     private static Path resolvePath(final String cwd, final String fileName) {
-        return Paths.get(cwd).resolve(fileName).normalize();
+        return Paths.get(sanitizePath(cwd)).resolve(fileName).normalize();
     }
 
     /**
@@ -402,7 +413,8 @@
         switch (cmd.get(0)) {
             // Set current working directory.
             case "cd":
-                // If zero args then use home dirrectory as cwd else use first arg.
+                final boolean cygpath = IS_WINDOWS && cwd.startsWith(CYGDRIVE);
+                // If zero args then use home directory as cwd else use first arg.
                 final String newCWD = cmd.size() < 2 ? HOME_DIRECTORY : cmd.get(1);
                 // Normalize the cwd
                 final Path cwdPath = resolvePath(cwd, newCWD);
@@ -418,7 +430,13 @@
                 }
 
                 // Set PWD environment variable to be picked up as cwd.
-                environment.put("PWD", cwdPath.toString());
+                // Make sure Cygwin paths look like Unix paths.
+                String scwd = cwdPath.toString();
+                if (cygpath && scwd.length() >= 2 &&
+                        Character.isLetter(scwd.charAt(0)) && scwd.charAt(1) == ':') {
+                    scwd = CYGDRIVE + Character.toLowerCase(scwd.charAt(0)) + "/" + scwd.substring(2);
+                }
+                environment.put("PWD", scwd);
                 return true;
 
             // Set an environment variable.
@@ -445,7 +463,8 @@
     }
 
     /**
-     * preprocessCommand - scan the command for redirects
+     * preprocessCommand - scan the command for redirects, and sanitize the
+     * executable path
      * @param tokens       command tokens
      * @param cwd          current working directory
      * @param redirectInfo redirection information
@@ -471,10 +490,39 @@
             command.add(stripQuotes(token));
         }
 
+        if (command.size() > 0) {
+            command.set(0, sanitizePath(command.get(0)));
+        }
+
         return command;
     }
 
     /**
+     * Sanitize a path in case the underlying platform is Cygwin. In that case,
+     * convert from the {@code /cygdrive/x} drive specification to the usual
+     * Windows {@code X:} format.
+     *
+     * @param d a String representing a path
+     * @return a String representing the same path in a form that can be
+     *         processed by the underlying platform
+     */
+    private static String sanitizePath(final String d) {
+        if (!IS_WINDOWS || (IS_WINDOWS && !d.startsWith(CYGDRIVE))) {
+            return d;
+        }
+        final String pd = d.substring(CYGDRIVE.length());
+        if (pd.length() >= 2 && pd.charAt(1) == '/') {
+            // drive letter plus / -> convert /cygdrive/x/... to X:/...
+            return pd.charAt(0) + ":" + pd.substring(1);
+        } else if (pd.length() == 1) {
+            // just drive letter -> convert /cygdrive/x to X:
+            return pd.charAt(0) + ":";
+        }
+        // remaining case: /cygdrive/ -> can't convert
+        return d;
+    }
+
+    /**
      * createProcessBuilder - create a ProcessBuilder for the command.
      * @param command      command tokens
      * @param cwd          current working directory
@@ -485,7 +533,7 @@
         // Create new ProcessBuilder.
         final ProcessBuilder pb = new ProcessBuilder(command);
         // Set current working directory.
-        pb.directory(new File(cwd));
+        pb.directory(new File(sanitizePath(cwd)));
 
         // Map environment variables.
         final Map<String, String> processEnvironment = pb.environment();
@@ -523,7 +571,7 @@
         // Create ProcessBuilder with cwd and redirects set.
         createProcessBuilder(command, cwd, redirectInfo);
 
-        // If piped the wait for the next command.
+        // If piped, wait for the next command.
         if (isPiped) {
             return;
         }
@@ -581,15 +629,17 @@
         ByteArrayOutputStream byteOutputStream = null;
         ByteArrayOutputStream byteErrorStream = null;
 
+        final List<Piper> piperThreads = new ArrayList<>();
+
         // If input is not redirected.
         if (inputIsPipe) {
             // If inputStream other than System.in is provided.
             if (inputStream != null) {
                 // Pipe inputStream to first process output stream.
-                new Piper(inputStream, firstProcess.getOutputStream()).start();
+                piperThreads.add(new Piper(inputStream, firstProcess.getOutputStream()).start());
             } else {
                 // Otherwise assume an input string has been provided.
-                new Piper(new ByteArrayInputStream(inputString.getBytes()), firstProcess.getOutputStream()).start();
+                piperThreads.add(new Piper(new ByteArrayInputStream(inputString.getBytes()), firstProcess.getOutputStream()).start());
             }
         }
 
@@ -598,11 +648,11 @@
             // If outputStream other than System.out is provided.
             if (outputStream != null ) {
                 // Pipe outputStream from last process input stream.
-                new Piper(lastProcess.getInputStream(), outputStream).start();
+                piperThreads.add(new Piper(lastProcess.getInputStream(), outputStream).start());
             } else {
                 // Otherwise assume an output string needs to be prepared.
                 byteOutputStream = new ByteArrayOutputStream(BUFFER_SIZE);
-                new Piper(lastProcess.getInputStream(), byteOutputStream).start();
+                piperThreads.add(new Piper(lastProcess.getInputStream(), byteOutputStream).start());
             }
         }
 
@@ -610,11 +660,11 @@
         if (errorIsPipe) {
             // If errorStream other than System.err is provided.
             if (errorStream != null) {
-                new Piper(lastProcess.getErrorStream(), errorStream).start();
+                piperThreads.add(new Piper(lastProcess.getErrorStream(), errorStream).start());
             } else {
                 // Otherwise assume an error string needs to be prepared.
                 byteErrorStream = new ByteArrayOutputStream(BUFFER_SIZE);
-                new Piper(lastProcess.getErrorStream(), byteErrorStream).start();
+                piperThreads.add(new Piper(lastProcess.getErrorStream(), byteErrorStream).start());
             }
         }
 
@@ -622,13 +672,13 @@
         for (int i = 0, n = processes.size() - 1; i < n; i++) {
             final Process prev = processes.get(i);
             final Process next = processes.get(i + 1);
-            new Piper(prev.getInputStream(), next.getOutputStream()).start();
+            piperThreads.add(new Piper(prev.getInputStream(), next.getOutputStream()).start());
         }
 
         // Wind up processes.
         try {
             // Get the user specified timeout.
-            long timeout = envVarLongValue("JJS_TIMEOUT");
+            final long timeout = envVarLongValue("JJS_TIMEOUT");
 
             // If user specified timeout (milliseconds.)
             if (timeout != 0) {
@@ -643,6 +693,10 @@
                 // Wait for last process and get exit code.
                 exitCode = lastProcess.waitFor();
             }
+            // Wait for all piper threads to terminate
+            for (final Piper piper : piperThreads) {
+                piper.join();
+            }
 
             // Accumulate the output and error streams.
             outputString += byteOutputStream != null ? byteOutputStream.toString() : "";
@@ -765,7 +819,7 @@
 
     /**
      * process - process a command array of strings
-     * @param script command script to be processed
+     * @param tokens command script to be processed
      */
     void process(final List<String> tokens) {
         // Prepare to accumulate command tokens.
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptingFunctions.java	Thu Mar 10 09:28:20 2016 -0800
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptingFunctions.java	Thu Mar 10 09:49:51 2016 -0800
@@ -72,7 +72,7 @@
     public static final String EXIT_NAME = "$EXIT";
 
     /** Names of special properties used by $ENV API. */
-    public  static final String ENV_NAME  = "$ENV";
+    public static final String ENV_NAME  = "$ENV";
 
     /** Name of the environment variable for the current working directory. */
     public static final String PWD_NAME  = "PWD";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/nosecurity/JDK-8151291.js	Thu Mar 10 09:49:51 2016 -0800
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * Test $EXEC and $ENV.PWD handling across platforms.
+ * There must be a java executable in the PATH.
+ *
+ * @test
+ * @option -scripting
+ * @run
+ */
+
+$EXEC(["java", "-version"])
+if ($EXIT != 0) {
+    throw 'java executable problem: ' + $ERR
+}
+
+function eq(p, q, e) {
+    if (p != q) {
+        throw e
+    }
+}
+
+function neq(p, q, e) {
+    if (p == q) {
+        throw e
+    }
+}
+
+var File    = Java.type("java.io.File"),
+    System  = Java.type("java.lang.System"),
+    win     = System.getProperty("os.name").startsWith("Windows"),
+    sep     = File.separator,
+    startwd = $ENV.PWD,
+    upwd    = startwd.substring(0, startwd.lastIndexOf(sep))
+
+$EXEC("ls")
+var ls_startwd = $OUT
+$EXEC("cd ..; ls")
+var ls_cdupwd = $OUT
+eq($ENV.PWD, startwd, 'PWD changed during $EXEC cd')
+neq(ls_startwd, ls_cdupwd, 'same ls result for startwd and upwd with $EXEC cd')
+
+$ENV.PWD = upwd
+eq($ENV.PWD, upwd, '$ENV.PWD change had no effect')
+$EXEC("ls")
+var ls_epupwd = $OUT
+neq(ls_startwd, ls_epupwd, 'same ls result for startwd and upwd with $ENV.PWD cd')