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