NashornScriptEngineFactory.java revision 1177:8e86c58cbb00
1/*
2 * Copyright (c) 2010, 2013, 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.api.scripting;
27
28import java.util.Arrays;
29import java.util.Collections;
30import java.util.List;
31import java.util.Objects;
32import javax.script.ScriptEngine;
33import javax.script.ScriptEngineFactory;
34import jdk.nashorn.internal.runtime.Context;
35import jdk.nashorn.internal.runtime.Version;
36
37/**
38 * JSR-223 compliant script engine factory for Nashorn. The engine answers for:
39 * <ul>
40 * <li>names {@code "nashorn"}, {@code "Nashorn"}, {@code "js"}, {@code "JS"}, {@code "JavaScript"},
41 * {@code "javascript"}, {@code "ECMAScript"}, and {@code "ecmascript"};</li>
42 * <li>MIME types {@code "application/javascript"}, {@code "application/ecmascript"}, {@code "text/javascript"}, and
43 * {@code "text/ecmascript"};</li>
44 * <li>as well as for the extension {@code "js"}.</li>
45 * </ul>
46 * Programs executing in engines created using {@link #getScriptEngine(String[])} will have the passed arguments
47 * accessible as a global variable named {@code "arguments"}.
48 *
49 * @since 1.8u40
50 */
51@jdk.Exported
52public final class NashornScriptEngineFactory implements ScriptEngineFactory {
53    @Override
54    public String getEngineName() {
55        return (String) getParameter(ScriptEngine.ENGINE);
56    }
57
58    @Override
59    public String getEngineVersion() {
60        return (String) getParameter(ScriptEngine.ENGINE_VERSION);
61    }
62
63    @Override
64    public List<String> getExtensions() {
65        return Collections.unmodifiableList(extensions);
66    }
67
68    @Override
69    public String getLanguageName() {
70        return (String) getParameter(ScriptEngine.LANGUAGE);
71    }
72
73    @Override
74    public String getLanguageVersion() {
75        return (String) getParameter(ScriptEngine.LANGUAGE_VERSION);
76    }
77
78    @Override
79    public String getMethodCallSyntax(final String obj, final String method, final String... args) {
80        final StringBuilder sb = new StringBuilder().append(obj).append('.').append(method).append('(');
81        final int len = args.length;
82
83        if (len > 0) {
84            sb.append(args[0]);
85        }
86        for (int i = 1; i < len; i++) {
87            sb.append(',').append(args[i]);
88        }
89        sb.append(')');
90
91        return sb.toString();
92    }
93
94    @Override
95    public List<String> getMimeTypes() {
96        return Collections.unmodifiableList(mimeTypes);
97    }
98
99    @Override
100    public List<String> getNames() {
101        return Collections.unmodifiableList(names);
102    }
103
104    @Override
105    public String getOutputStatement(final String toDisplay) {
106        return "print(" + toDisplay + ")";
107    }
108
109    @Override
110    public Object getParameter(final String key) {
111        switch (key) {
112        case ScriptEngine.NAME:
113            return "javascript";
114        case ScriptEngine.ENGINE:
115            return "Oracle Nashorn";
116        case ScriptEngine.ENGINE_VERSION:
117            return Version.version();
118        case ScriptEngine.LANGUAGE:
119            return "ECMAScript";
120        case ScriptEngine.LANGUAGE_VERSION:
121            return "ECMA - 262 Edition 5.1";
122        case "THREADING":
123            // The engine implementation is not thread-safe. Can't be
124            // used to execute scripts concurrently on multiple threads.
125            return null;
126        default:
127            return null;
128        }
129    }
130
131    @Override
132    public String getProgram(final String... statements) {
133        final StringBuilder sb = new StringBuilder();
134
135        for (final String statement : statements) {
136            sb.append(statement).append(';');
137        }
138
139        return sb.toString();
140    }
141
142    // default options passed to Nashorn script engine
143    private static final String[] DEFAULT_OPTIONS = new String[] { "-doe" };
144
145    @Override
146    public ScriptEngine getScriptEngine() {
147        try {
148            return new NashornScriptEngine(this, DEFAULT_OPTIONS, getAppClassLoader(), null);
149        } catch (final RuntimeException e) {
150            if (Context.DEBUG) {
151                e.printStackTrace();
152            }
153            throw e;
154        }
155    }
156
157    /**
158     * Create a new Script engine initialized by given class loader.
159     *
160     * @param appLoader class loader to be used as script "app" class loader.
161     * @return newly created script engine.
162     * @throws SecurityException
163     *         if the security manager's {@code checkPermission}
164     *         denies {@code RuntimePermission("nashorn.setConfig")}
165     */
166    public ScriptEngine getScriptEngine(final ClassLoader appLoader) {
167        return newEngine(DEFAULT_OPTIONS, appLoader, null);
168    }
169
170    /**
171     * Create a new Script engine initialized by given class filter.
172     *
173     * @param classFilter class filter to use.
174     * @return newly created script engine.
175     * @throws NullPointerException if {@code classFilter} is {@code null}
176     * @throws SecurityException
177     *         if the security manager's {@code checkPermission}
178     *         denies {@code RuntimePermission("nashorn.setConfig")}
179     */
180    public ScriptEngine getScriptEngine(final ClassFilter classFilter) {
181        Objects.requireNonNull(classFilter);
182        return newEngine(DEFAULT_OPTIONS, getAppClassLoader(), classFilter);
183    }
184
185    /**
186     * Create a new Script engine initialized by given arguments.
187     *
188     * @param args arguments array passed to script engine.
189     * @return newly created script engine.
190     * @throws NullPointerException if {@code args} is {@code null}
191     * @throws SecurityException
192     *         if the security manager's {@code checkPermission}
193     *         denies {@code RuntimePermission("nashorn.setConfig")}
194     */
195    public ScriptEngine getScriptEngine(final String... args) {
196        Objects.requireNonNull(args);
197        return newEngine(args, getAppClassLoader(), null);
198    }
199
200    /**
201     * Create a new Script engine initialized by given arguments.
202     *
203     * @param args arguments array passed to script engine.
204     * @param appLoader class loader to be used as script "app" class loader.
205     * @return newly created script engine.
206     * @throws NullPointerException if {@code args} is {@code null}
207     * @throws SecurityException
208     *         if the security manager's {@code checkPermission}
209     *         denies {@code RuntimePermission("nashorn.setConfig")}
210     */
211    public ScriptEngine getScriptEngine(final String[] args, final ClassLoader appLoader) {
212        Objects.requireNonNull(args);
213        return newEngine(args, appLoader, null);
214    }
215
216    /**
217     * Create a new Script engine initialized by given arguments.
218     *
219     * @param args arguments array passed to script engine.
220     * @param appLoader class loader to be used as script "app" class loader.
221     * @param classFilter class filter to use.
222     * @return newly created script engine.
223     * @throws NullPointerException if {@code args} or {@code classFilter} is {@code null}
224     * @throws SecurityException
225     *         if the security manager's {@code checkPermission}
226     *         denies {@code RuntimePermission("nashorn.setConfig")}
227     */
228    public ScriptEngine getScriptEngine(final String[] args, final ClassLoader appLoader, final ClassFilter classFilter) {
229        Objects.requireNonNull(args);
230        Objects.requireNonNull(classFilter);
231        return newEngine(args, appLoader, classFilter);
232    }
233
234    private ScriptEngine newEngine(final String[] args, final ClassLoader appLoader, final ClassFilter classFilter) {
235        checkConfigPermission();
236        try {
237            return new NashornScriptEngine(this, args, appLoader, classFilter);
238        } catch (final RuntimeException e) {
239            if (Context.DEBUG) {
240                e.printStackTrace();
241            }
242            throw e;
243        }
244    }
245
246    // -- Internals only below this point
247
248    private static void checkConfigPermission() {
249        final SecurityManager sm = System.getSecurityManager();
250        if (sm != null) {
251            sm.checkPermission(new RuntimePermission(Context.NASHORN_SET_CONFIG));
252        }
253    }
254
255    private static final List<String> names;
256    private static final List<String> mimeTypes;
257    private static final List<String> extensions;
258
259    static {
260        names = immutableList(
261                    "nashorn", "Nashorn",
262                    "js", "JS",
263                    "JavaScript", "javascript",
264                    "ECMAScript", "ecmascript"
265                );
266
267        mimeTypes = immutableList(
268                        "application/javascript",
269                        "application/ecmascript",
270                        "text/javascript",
271                        "text/ecmascript"
272                    );
273
274        extensions = immutableList("js");
275    }
276
277    private static List<String> immutableList(final String... elements) {
278        return Collections.unmodifiableList(Arrays.asList(elements));
279    }
280
281    private static ClassLoader getAppClassLoader() {
282        // Revisit: script engine implementation needs the capability to
283        // find the class loader of the context in which the script engine
284        // is running so that classes will be found and loaded properly
285        final ClassLoader ccl = Thread.currentThread().getContextClassLoader();
286        return (ccl == null)? NashornScriptEngineFactory.class.getClassLoader() : ccl;
287    }
288}
289