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 26package jdk.nashorn.internal.runtime; 27 28import static jdk.nashorn.internal.lookup.Lookup.MH; 29import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 30import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 31 32import java.io.BufferedReader; 33import java.io.File; 34import java.io.IOException; 35import java.io.InputStream; 36import java.io.InputStreamReader; 37import java.io.OutputStream; 38import java.lang.invoke.MethodHandle; 39import java.lang.invoke.MethodHandles; 40import java.util.ArrayList; 41import java.util.Arrays; 42import java.util.HashMap; 43import java.util.List; 44import java.util.Map; 45import java.util.Objects; 46import java.util.function.Function; 47import jdk.nashorn.internal.objects.NativeArray; 48import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError; 49 50/** 51 * Global functions supported only in scripting mode. 52 */ 53public final class ScriptingFunctions { 54 55 /** Handle to implementation of {@link ScriptingFunctions#readLine} - Nashorn extension */ 56 public static final MethodHandle READLINE = findOwnMH("readLine", Object.class, Object.class, Object.class); 57 58 /** Handle to implementation of {@link ScriptingFunctions#readFully} - Nashorn extension */ 59 public static final MethodHandle READFULLY = findOwnMH("readFully", Object.class, Object.class, Object.class); 60 61 /** Handle to implementation of {@link ScriptingFunctions#exec} - Nashorn extension */ 62 public static final MethodHandle EXEC = findOwnMH("exec", Object.class, Object.class, Object[].class); 63 64 /** EXEC name - special property used by $EXEC API. */ 65 public static final String EXEC_NAME = "$EXEC"; 66 67 /** OUT name - special property used by $EXEC API. */ 68 public static final String OUT_NAME = "$OUT"; 69 70 /** ERR name - special property used by $EXEC API. */ 71 public static final String ERR_NAME = "$ERR"; 72 73 /** EXIT name - special property used by $EXEC API. */ 74 public static final String EXIT_NAME = "$EXIT"; 75 76 /** Names of special properties used by $ENV API. */ 77 public static final String ENV_NAME = "$ENV"; 78 79 /** Name of the environment variable for the current working directory. */ 80 public static final String PWD_NAME = "PWD"; 81 82 private ScriptingFunctions() { 83 } 84 85 /** 86 * Nashorn extension: global.readLine (scripting-mode-only) 87 * Read one line of input from the standard input. 88 * 89 * @param self self reference 90 * @param prompt String used as input prompt 91 * 92 * @return line that was read 93 * 94 * @throws IOException if an exception occurs 95 */ 96 public static Object readLine(final Object self, final Object prompt) throws IOException { 97 return readLine(prompt); 98 } 99 100 /** 101 * Nashorn extension: Read the entire contents of a text file and return as String. 102 * 103 * @param self self reference 104 * @param file The input file whose content is read. 105 * 106 * @return String content of the input file. 107 * 108 * @throws IOException if an exception occurs 109 */ 110 public static Object readFully(final Object self, final Object file) throws IOException { 111 File f = null; 112 113 if (file instanceof File) { 114 f = (File)file; 115 } else if (JSType.isString(file)) { 116 f = new java.io.File(((CharSequence)file).toString()); 117 } 118 119 if (f == null || !f.isFile()) { 120 throw typeError("not.a.file", ScriptRuntime.safeToString(file)); 121 } 122 123 return new String(Source.readFully(f)); 124 } 125 126 /** 127 * Nashorn extension: exec a string in a separate process. 128 * 129 * @param self self reference 130 * @param args In one of four forms 131 * 1. String script, String input 132 * 2. String script, InputStream input, OutputStream output, OutputStream error 133 * 3. Array scriptTokens, String input 134 * 4. Array scriptTokens, InputStream input, OutputStream output, OutputStream error 135 * 136 * @return output string from the request if in form of 1. or 3., empty string otherwise 137 */ 138 public static Object exec(final Object self, final Object... args) { 139 final Object arg0 = args.length > 0 ? args[0] : UNDEFINED; 140 final Object arg1 = args.length > 1 ? args[1] : UNDEFINED; 141 final Object arg2 = args.length > 2 ? args[2] : UNDEFINED; 142 final Object arg3 = args.length > 3 ? args[3] : UNDEFINED; 143 144 InputStream inputStream = null; 145 OutputStream outputStream = null; 146 OutputStream errorStream = null; 147 String script = null; 148 List<String> tokens = null; 149 String inputString = null; 150 151 if (arg0 instanceof NativeArray) { 152 final String[] array = (String[])JSType.toJavaArray(arg0, String.class); 153 tokens = new ArrayList<>(); 154 tokens.addAll(Arrays.asList(array)); 155 } else { 156 script = JSType.toString(arg0); 157 } 158 159 if (arg1 instanceof InputStream) { 160 inputStream = (InputStream)arg1; 161 } else { 162 inputString = JSType.toString(arg1); 163 } 164 165 if (arg2 instanceof OutputStream) { 166 outputStream = (OutputStream)arg2; 167 } 168 169 if (arg3 instanceof OutputStream) { 170 errorStream = (OutputStream)arg3; 171 } 172 173 // Current global is need to fetch additional inputs and for additional results. 174 final ScriptObject global = Context.getGlobal(); 175 176 // Capture ENV property state. 177 final Map<String, String> environment = new HashMap<>(); 178 final Object env = global.get(ENV_NAME); 179 180 if (env instanceof ScriptObject) { 181 final ScriptObject envProperties = (ScriptObject)env; 182 183 // Copy ENV variables. 184 envProperties.entrySet().stream().forEach((entry) -> { 185 environment.put(JSType.toString(entry.getKey()), JSType.toString(entry.getValue())); 186 }); 187 } 188 189 // get the $EXEC function object from the global object 190 final Object exec = global.get(EXEC_NAME); 191 assert exec instanceof ScriptObject : EXEC_NAME + " is not a script object!"; 192 193 // Execute the commands 194 final CommandExecutor executor = new CommandExecutor(); 195 executor.setInputString(inputString); 196 executor.setInputStream(inputStream); 197 executor.setOutputStream(outputStream); 198 executor.setErrorStream(errorStream); 199 executor.setEnvironment(environment); 200 201 if (tokens != null) { 202 executor.process(tokens); 203 } else { 204 executor.process(script); 205 } 206 207 final String outString = executor.getOutputString(); 208 final String errString = executor.getErrorString(); 209 final int exitCode = executor.getExitCode(); 210 211 // Set globals for secondary results. 212 global.set(OUT_NAME, outString, 0); 213 global.set(ERR_NAME, errString, 0); 214 global.set(EXIT_NAME, exitCode, 0); 215 216 // Return the result from stdout. 217 return outString; 218 } 219 220 // Implementation for pluggable "readLine" functionality 221 // Used by jjs interactive mode 222 223 private static Function<String, String> readLineHelper; 224 225 public static void setReadLineHelper(final Function<String, String> func) { 226 readLineHelper = Objects.requireNonNull(func); 227 } 228 229 public static Function<String, String> getReadLineHelper() { 230 return readLineHelper; 231 } 232 233 public static String readLine(final Object prompt) throws IOException { 234 final String p = (prompt != UNDEFINED)? JSType.toString(prompt) : ""; 235 if (readLineHelper != null) { 236 return readLineHelper.apply(p); 237 } else { 238 System.out.print(p); 239 final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 240 return reader.readLine(); 241 } 242 } 243 244 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { 245 return MH.findStatic(MethodHandles.lookup(), ScriptingFunctions.class, name, MH.type(rtype, types)); 246 } 247} 248