1 /* 2 * Copyright (c) 2010, 2015, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nashorn.internal.runtime; 27 28 import static jdk.nashorn.internal.lookup.Lookup.MH; 29 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 30 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 31 32 import java.io.BufferedReader; 33 import java.io.File; 34 import java.io.IOException; 35 import java.io.InputStream; 36 import java.io.InputStreamReader; 37 import java.io.OutputStream; 38 import java.lang.invoke.MethodHandle; 39 import java.lang.invoke.MethodHandles; 40 import java.util.ArrayList; 41 import java.util.Arrays; 42 import java.util.HashMap; 43 import java.util.List; 44 import java.util.Map; 45 import jdk.nashorn.internal.objects.NativeArray; 46 import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError; 47 48 /** 49 * Global functions supported only in scripting mode. 50 */ 51 public final class ScriptingFunctions { 52 53 /** Handle to implementation of {@link ScriptingFunctions#readLine} - Nashorn extension */ 54 public static final MethodHandle READLINE = findOwnMH("readLine", Object.class, Object.class, Object.class); 55 56 /** Handle to implementation of {@link ScriptingFunctions#readFully} - Nashorn extension */ 57 public static final MethodHandle READFULLY = findOwnMH("readFully", Object.class, Object.class, Object.class); 58 59 /** Handle to implementation of {@link ScriptingFunctions#exec} - Nashorn extension */ 60 public static final MethodHandle EXEC = findOwnMH("exec", Object.class, Object.class, Object[].class); 61 62 /** EXEC name - special property used by $EXEC API. */ 63 public static final String EXEC_NAME = "$EXEC"; 64 65 /** OUT name - special property used by $EXEC API. */ 66 public static final String OUT_NAME = "$OUT"; 67 68 /** ERR name - special property used by $EXEC API. */ 69 public static final String ERR_NAME = "$ERR"; 70 71 /** EXIT name - special property used by $EXEC API. */ 72 public static final String EXIT_NAME = "$EXIT"; 73 74 /** Names of special properties used by $ENV API. */ 75 public static final String ENV_NAME = "$ENV"; 76 77 /** Name of the environment variable for the current working directory. */ 78 public static final String PWD_NAME = "PWD"; 79 80 private ScriptingFunctions() { 81 } 82 83 /** 84 * Nashorn extension: global.readLine (scripting-mode-only) 85 * Read one line of input from the standard input. 86 * 87 * @param self self reference 88 * @param prompt String used as input prompt 89 * 90 * @return line that was read 91 * 92 * @throws IOException if an exception occurs 93 */ 94 public static Object readLine(final Object self, final Object prompt) throws IOException { 95 if (prompt != UNDEFINED) { 96 System.out.print(JSType.toString(prompt)); 97 } 98 final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 99 return reader.readLine(); 100 } 101 102 /** 103 * Nashorn extension: Read the entire contents of a text file and return as String. 104 * 105 * @param self self reference 106 * @param file The input file whose content is read. 107 * 108 * @return String content of the input file. 109 * 110 * @throws IOException if an exception occurs 111 */ 112 public static Object readFully(final Object self, final Object file) throws IOException { 113 File f = null; 114 115 if (file instanceof File) { 116 f = (File)file; 117 } else if (JSType.isString(file)) { 118 f = new java.io.File(((CharSequence)file).toString()); 119 } 120 121 if (f == null || !f.isFile()) { 122 throw typeError("not.a.file", ScriptRuntime.safeToString(file)); 123 } 124 125 return new String(Source.readFully(f)); 126 } 127 128 /** 129 * Nashorn extension: exec a string in a separate process. 130 * 131 * @param self self reference 132 * @param args In one of four forms 133 * 1. String script, String input 134 * 2. String script, InputStream input, OutputStream output, OutputStream error 135 * 3. Array scriptTokens, String input 136 * 4. Array scriptTokens, InputStream input, OutputStream output, OutputStream error 137 * 138 * @return output string from the request if in form of 1. or 3., empty string otherwise 139 */ 140 public static Object exec(final Object self, final Object... args) { 141 final Object arg0 = args.length > 0 ? args[0] : UNDEFINED; 142 final Object arg1 = args.length > 1 ? args[1] : UNDEFINED; 143 final Object arg2 = args.length > 2 ? args[2] : UNDEFINED; 144 final Object arg3 = args.length > 3 ? args[3] : UNDEFINED; 145 146 InputStream inputStream = null; 147 OutputStream outputStream = null; 148 OutputStream errorStream = null; 149 String script = null; 150 List<String> tokens = null; 151 String inputString = null; 152 153 if (arg0 instanceof NativeArray) { 154 String[] array = (String[])JSType.toJavaArray(arg0, String.class); 155 tokens = new ArrayList<>(); 156 tokens.addAll(Arrays.asList(array)); 157 } else { 158 script = JSType.toString(arg0); 159 } 160 161 if (arg1 instanceof InputStream) { 162 inputStream = (InputStream)arg1; 163 } else { 164 inputString = JSType.toString(arg1); 165 } 166 167 if (arg2 instanceof OutputStream) { 168 outputStream = (OutputStream)arg2; 169 } 170 171 if (arg3 instanceof OutputStream) { 172 errorStream = (OutputStream)arg3; 173 } 174 175 // Current global is need to fetch additional inputs and for additional results. 176 final ScriptObject global = Context.getGlobal(); 177 178 // Capture ENV property state. 179 final Map<String, String> environment = new HashMap<>(); 180 final Object env = global.get(ENV_NAME); 181 182 if (env instanceof ScriptObject) { 183 final ScriptObject envProperties = (ScriptObject)env; 184 185 // Copy ENV variables. 186 envProperties.entrySet().stream().forEach((entry) -> { 187 environment.put(JSType.toString(entry.getKey()), JSType.toString(entry.getValue())); 188 }); 189 } 190 191 // get the $EXEC function object from the global object 192 final Object exec = global.get(EXEC_NAME); 193 assert exec instanceof ScriptObject : EXEC_NAME + " is not a script object!"; 194 195 // Execute the commands 196 final CommandExecutor executor = new CommandExecutor(); 197 executor.setInputString(inputString); 198 executor.setInputStream(inputStream); 199 executor.setOutputStream(outputStream); 200 executor.setErrorStream(errorStream); 201 executor.setEnvironment(environment); 202 203 if (tokens != null) { 204 executor.process(tokens); 205 } else { 206 executor.process(script); 207 } 208 209 final String outString = executor.getOutputString(); 210 final String errString = executor.getErrorString(); 211 int exitCode = executor.getExitCode(); 212 213 // Set globals for secondary results. 214 global.set(OUT_NAME, outString, 0); 215 global.set(ERR_NAME, errString, 0); 216 global.set(EXIT_NAME, exitCode, 0); 217 218 // Return the result from stdout. 219 return outString; 220 } 221 222 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { 223 return MH.findStatic(MethodHandles.lookup(), ScriptingFunctions.class, name, MH.type(rtype, types)); 224 } 225 }