ScriptingFunctions.java revision 1637:f27bb66ac9d3
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 jdk.nashorn.internal.objects.NativeArray;
46import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
47
48/**
49 * Global functions supported only in scripting mode.
50 */
51public 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}
226