NashornScriptEngineFactory.java revision 1590:1916a2c680d8
14Srgrimes/*
2247047Skib * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
34Srgrimes * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4247047Skib *
54Srgrimes * This code is free software; you can redistribute it and/or modify it
6247047Skib * 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 */
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            return null;
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        return newEngine(DEFAULT_OPTIONS, getAppClassLoader(), Objects.requireNonNull(classFilter));
181    }
182
183    /**
184     * Create a new Script engine initialized by given arguments.
185     *
186     * @param args arguments array passed to script engine.
187     * @return newly created script engine.
188     * @throws NullPointerException if {@code args} is {@code null}
189     * @throws SecurityException
190     *         if the security manager's {@code checkPermission}
191     *         denies {@code RuntimePermission("nashorn.setConfig")}
192     */
193    public ScriptEngine getScriptEngine(final String... args) {
194        return newEngine(Objects.requireNonNull(args), getAppClassLoader(), null);
195    }
196
197    /**
198     * Create a new Script engine initialized by given arguments.
199     *
200     * @param args arguments array passed to script engine.
201     * @param appLoader class loader to be used as script "app" class loader.
202     * @return newly created script engine.
203     * @throws NullPointerException if {@code args} is {@code null}
204     * @throws SecurityException
205     *         if the security manager's {@code checkPermission}
206     *         denies {@code RuntimePermission("nashorn.setConfig")}
207     */
208    public ScriptEngine getScriptEngine(final String[] args, final ClassLoader appLoader) {
209        return newEngine(Objects.requireNonNull(args), appLoader, null);
210    }
211
212    /**
213     * Create a new Script engine initialized by given arguments.
214     *
215     * @param args arguments array passed to script engine.
216     * @param appLoader class loader to be used as script "app" class loader.
217     * @param classFilter class filter to use.
218     * @return newly created script engine.
219     * @throws NullPointerException if {@code args} or {@code classFilter} is {@code null}
220     * @throws SecurityException
221     *         if the security manager's {@code checkPermission}
222     *         denies {@code RuntimePermission("nashorn.setConfig")}
223     */
224    public ScriptEngine getScriptEngine(final String[] args, final ClassLoader appLoader, final ClassFilter classFilter) {
225        return newEngine(Objects.requireNonNull(args), appLoader, Objects.requireNonNull(classFilter));
226    }
227
228    private ScriptEngine newEngine(final String[] args, final ClassLoader appLoader, final ClassFilter classFilter) {
229        checkConfigPermission();
230        try {
231            return new NashornScriptEngine(this, args, appLoader, classFilter);
232        } catch (final RuntimeException e) {
233            if (Context.DEBUG) {
234                e.printStackTrace();
235            }
236            throw e;
237        }
238    }
239
240    // -- Internals only below this point
241
242    private static void checkConfigPermission() {
243        final SecurityManager sm = System.getSecurityManager();
244        if (sm != null) {
245            sm.checkPermission(new RuntimePermission(Context.NASHORN_SET_CONFIG));
246        }
247    }
248
249    private static final List<String> names;
250    private static final List<String> mimeTypes;
251    private static final List<String> extensions;
252
253    static {
254        names = immutableList(
255                    "nashorn", "Nashorn",
256                    "js", "JS",
257                    "JavaScript", "javascript",
258                    "ECMAScript", "ecmascript"
259                );
260
261        mimeTypes = immutableList(
262                        "application/javascript",
263                        "application/ecmascript",
264                        "text/javascript",
265                        "text/ecmascript"
266                    );
267
268        extensions = immutableList("js");
269    }
270
271    private static List<String> immutableList(final String... elements) {
272        return Collections.unmodifiableList(Arrays.asList(elements));
273    }
274
275    private static ClassLoader getAppClassLoader() {
276        // Revisit: script engine implementation needs the capability to
277        // find the class loader of the context in which the script engine
278        // is running so that classes will be found and loaded properly
279        final ClassLoader ccl = Thread.currentThread().getContextClassLoader();
280        return (ccl == null)? NashornScriptEngineFactory.class.getClassLoader() : ccl;
281    }
282}
283