30 import static jdk.nashorn.internal.runtime.linker.Lookup.MH; |
30 import static jdk.nashorn.internal.runtime.linker.Lookup.MH; |
31 |
31 |
32 import java.io.BufferedReader; |
32 import java.io.BufferedReader; |
33 import java.io.File; |
33 import java.io.File; |
34 import java.io.IOException; |
34 import java.io.IOException; |
|
35 import java.io.InputStream; |
35 import java.io.InputStreamReader; |
36 import java.io.InputStreamReader; |
|
37 import java.io.OutputStream; |
36 import java.lang.invoke.MethodHandle; |
38 import java.lang.invoke.MethodHandle; |
37 import java.lang.invoke.MethodHandles; |
39 import java.lang.invoke.MethodHandles; |
|
40 import java.util.HashMap; |
|
41 import java.util.Map; |
|
42 import java.util.Set; |
|
43 import java.util.StringTokenizer; |
38 |
44 |
39 /** |
45 /** |
40 * Global functions supported only in scripting mode. |
46 * Global functions supported only in scripting mode. |
41 */ |
47 */ |
42 public class ScriptingFunctions { |
48 public class ScriptingFunctions { |
48 public static final MethodHandle READFULLY = findOwnMH("readFully", Object.class, Object.class, Object.class); |
54 public static final MethodHandle READFULLY = findOwnMH("readFully", Object.class, Object.class, Object.class); |
49 |
55 |
50 /** Handle to implementation of {@link ScriptingFunctions#quit} - Nashorn extension */ |
56 /** Handle to implementation of {@link ScriptingFunctions#quit} - Nashorn extension */ |
51 public static final MethodHandle QUIT = findOwnMH("quit", Object.class, Object.class, Object.class); |
57 public static final MethodHandle QUIT = findOwnMH("quit", Object.class, Object.class, Object.class); |
52 |
58 |
|
59 /** Handle to implementation of {@link ScriptingFunctions#quit} - Nashorn extension */ |
|
60 public static final MethodHandle EXEC = findOwnMH("exec", Object.class, Object.class, Object.class, Object.class); |
|
61 |
|
62 /** Names of special properties used by $EXEC API. */ |
|
63 public static final String EXEC_NAME = "$EXEC"; |
|
64 private static final String OUT_NAME = "$OUT"; |
|
65 private static final String ERR_NAME = "$ERR"; |
|
66 private static final String EXIT_NAME = "$EXIT"; |
|
67 |
|
68 /** Names of special properties used by $ENV API. */ |
|
69 public static final String ENV_NAME = "$ENV"; |
|
70 private static final String PWD_NAME = "PWD"; |
|
71 |
53 private ScriptingFunctions() { |
72 private ScriptingFunctions() { |
54 } |
73 } |
55 |
74 |
56 /** |
75 /** |
57 * Nashorn extension: global.readLine (scripting-mode-only) |
76 * Nashorn extension: global.readLine (scripting-mode-only) |
106 public static Object quit(final Object self, final Object code) { |
125 public static Object quit(final Object self, final Object code) { |
107 System.exit(JSType.toInt32(code)); |
126 System.exit(JSType.toInt32(code)); |
108 return UNDEFINED; |
127 return UNDEFINED; |
109 } |
128 } |
110 |
129 |
|
130 /** |
|
131 * Nashorn extension: exec a string in a separate process. |
|
132 * |
|
133 * @param self self reference |
|
134 * @param string string to execute |
|
135 * |
|
136 * @return output string from the request |
|
137 */ |
|
138 public static Object exec(final Object self, final Object string, final Object input) throws IOException, InterruptedException { |
|
139 // Current global is need to fetch additional inputs and for additional results. |
|
140 final ScriptObject global = Context.getGlobal(); |
|
141 |
|
142 // Current ENV property state. |
|
143 final Object env = global.get(ENV_NAME); |
|
144 // Make sure ENV is a valid script object. |
|
145 if (!(env instanceof ScriptObject)) { |
|
146 typeError("env.not.object"); |
|
147 } |
|
148 final ScriptObject envProperties = (ScriptObject)env; |
|
149 |
|
150 // Break exec string into tokens. |
|
151 final StringTokenizer tokenizer = new StringTokenizer(JSType.toString(string)); |
|
152 final String[] cmdArray = new String[tokenizer.countTokens()]; |
|
153 for (int i = 0; tokenizer.hasMoreTokens(); i++) { |
|
154 cmdArray[i] = tokenizer.nextToken(); |
|
155 } |
|
156 |
|
157 // Set up initial process. |
|
158 final ProcessBuilder processBuilder = new ProcessBuilder(cmdArray); |
|
159 |
|
160 // If a working directory is present, use it. |
|
161 final Object pwd = envProperties.get(PWD_NAME); |
|
162 if (pwd != UNDEFINED) { |
|
163 processBuilder.directory(new File(JSType.toString(pwd))); |
|
164 } |
|
165 |
|
166 // Set up ENV variables. |
|
167 final Map<String, String> environment = processBuilder.environment(); |
|
168 environment.clear(); |
|
169 for (Map.Entry<Object, Object> entry : envProperties.entrySet()) { |
|
170 |
|
171 environment.put(JSType.toString(entry.getKey()), JSType.toString(entry.getValue())); |
|
172 } |
|
173 |
|
174 // Start the process. |
|
175 final Process process = processBuilder.start(); |
|
176 |
|
177 // If input is present, pass on to process. |
|
178 try (OutputStream outputStream = process.getOutputStream()) { |
|
179 if (input != UNDEFINED) { |
|
180 outputStream.write(JSType.toString(input).getBytes()); |
|
181 } |
|
182 } |
|
183 |
|
184 // Wait for the process to complete. |
|
185 final int exit = process.waitFor(); |
|
186 |
|
187 // Collect output. |
|
188 String out; |
|
189 try (InputStream inputStream = process.getInputStream()) { |
|
190 final StringBuilder outBuffer = new StringBuilder(); |
|
191 for (int ch; (ch = inputStream.read()) != -1; ) { |
|
192 outBuffer.append((char)ch); |
|
193 } |
|
194 out = outBuffer.toString(); |
|
195 } |
|
196 |
|
197 // Collect errors. |
|
198 String err; |
|
199 try (InputStream errorStream = process.getErrorStream()) { |
|
200 final StringBuilder errBuffer = new StringBuilder(); |
|
201 for (int ch; (ch = errorStream.read()) != -1; ) { |
|
202 errBuffer.append((char)ch); |
|
203 } |
|
204 err = errBuffer.toString(); |
|
205 } |
|
206 |
|
207 // Set globals for secondary results. |
|
208 final boolean isStrict = global.isStrictContext(); |
|
209 global.set(OUT_NAME, out, isStrict); |
|
210 global.set(ERR_NAME, err, isStrict); |
|
211 global.set(EXIT_NAME, exit, isStrict); |
|
212 |
|
213 // Return the result from stdout. |
|
214 return out; |
|
215 } |
|
216 |
|
217 /** |
|
218 * Return an object containing properties mapping to ENV variables. |
|
219 * |
|
220 * @param envProperties object to receive properties |
|
221 * @param isStrict global's strict state |
|
222 * |
|
223 * @return Script object with properties mapping to ENV variables. |
|
224 */ |
|
225 public static ScriptObject getENVValues(final ScriptObject envProperties, final boolean isStrict) { |
|
226 // Retrieve current state of ENV variables. |
|
227 Map<String, String> envVars; |
|
228 try { |
|
229 envVars = System.getenv(); |
|
230 } catch(SecurityException ex) { |
|
231 envVars = new HashMap<>(); |
|
232 } |
|
233 |
|
234 // Map ENV variables. |
|
235 for (Map.Entry<String, String> entry : envVars.entrySet()) { |
|
236 envProperties.set(entry.getKey(), entry.getValue(), isStrict); |
|
237 } |
|
238 |
|
239 return envProperties; |
|
240 } |
|
241 |
111 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { |
242 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { |
112 return MH.findStatic(MethodHandles.lookup(), ScriptingFunctions.class, name, MH.type(rtype, types)); |
243 return MH.findStatic(MethodHandles.lookup(), ScriptingFunctions.class, name, MH.type(rtype, types)); |
113 } |
244 } |
114 } |
245 } |