NashornScriptEngineFactory.java revision 1590:1916a2c680d8
126497Sache/*
226497Sache * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
326497Sache * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
426497Sache *
526497Sache * This code is free software; you can redistribute it and/or modify it
626497Sache * under the terms of the GNU General Public License version 2 only, as
726497Sache * published by the Free Software Foundation.  Oracle designates this
826497Sache * particular file as subject to the "Classpath" exception as provided
926497Sache * by Oracle in the LICENSE file that accompanied this code.
1026497Sache *
1126497Sache * This code is distributed in the hope that it will be useful, but WITHOUT
1226497Sache * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1326497Sache * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1426497Sache * version 2 for more details (a copy is included in the LICENSE file that
1526497Sache * accompanied this code).
1626497Sache *
1726497Sache * You should have received a copy of the GNU General Public License version
1826497Sache * 2 along with this work; if not, write to the Free Software Foundation,
1926497Sache * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
2026497Sache *
2126497Sache * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2226497Sache * or visit www.oracle.com if you need additional information or have any
2326497Sache * questions.
2426497Sache */
2526497Sache
2626497Sachepackage jdk.nashorn.api.scripting;
2726497Sache
2826497Sacheimport java.util.Arrays;
2926497Sacheimport java.util.Collections;
3026497Sacheimport java.util.List;
3126497Sacheimport java.util.Objects;
3226497Sacheimport javax.script.ScriptEngine;
3326497Sacheimport javax.script.ScriptEngineFactory;
3426497Sacheimport jdk.nashorn.internal.runtime.Context;
3526497Sacheimport jdk.nashorn.internal.runtime.Version;
3626497Sache
3726497Sache/**
3826497Sache * JSR-223 compliant script engine factory for Nashorn. The engine answers for:
3926497Sache * <ul>
4026497Sache * <li>names {@code "nashorn"}, {@code "Nashorn"}, {@code "js"}, {@code "JS"}, {@code "JavaScript"},
4126497Sache * {@code "javascript"}, {@code "ECMAScript"}, and {@code "ecmascript"};</li>
4226497Sache * <li>MIME types {@code "application/javascript"}, {@code "application/ecmascript"}, {@code "text/javascript"}, and
4326497Sache * {@code "text/ecmascript"};</li>
4426497Sache * <li>as well as for the extension {@code "js"}.</li>
4526497Sache * </ul>
4626497Sache * Programs executing in engines created using {@link #getScriptEngine(String[])} will have the passed arguments
4726497Sache * accessible as a global variable named {@code "arguments"}.
4826497Sache *
4926497Sache * @since 1.8u40
5026497Sache */
5126497Sachepublic final class NashornScriptEngineFactory implements ScriptEngineFactory {
5226497Sache    @Override
5326497Sache    public String getEngineName() {
5426497Sache        return (String) getParameter(ScriptEngine.ENGINE);
5526497Sache    }
5626497Sache
5726497Sache    @Override
5826497Sache    public String getEngineVersion() {
5926497Sache        return (String) getParameter(ScriptEngine.ENGINE_VERSION);
6026497Sache    }
6126497Sache
6226497Sache    @Override
6326497Sache    public List<String> getExtensions() {
6426497Sache        return Collections.unmodifiableList(extensions);
6526497Sache    }
6626497Sache
6726497Sache    @Override
6826497Sache    public String getLanguageName() {
6926497Sache        return (String) getParameter(ScriptEngine.LANGUAGE);
7026497Sache    }
7126497Sache
7226497Sache    @Override
7326497Sache    public String getLanguageVersion() {
7426497Sache        return (String) getParameter(ScriptEngine.LANGUAGE_VERSION);
7526497Sache    }
7626497Sache
7726497Sache    @Override
7826497Sache    public String getMethodCallSyntax(final String obj, final String method, final String... args) {
7926497Sache        final StringBuilder sb = new StringBuilder().append(obj).append('.').append(method).append('(');
8026497Sache        final int len = args.length;
8126497Sache
8226497Sache        if (len > 0) {
8326497Sache            sb.append(args[0]);
8426497Sache        }
8526497Sache        for (int i = 1; i < len; i++) {
8626497Sache            sb.append(',').append(args[i]);
8726497Sache        }
8826497Sache        sb.append(')');
8926497Sache
9026497Sache        return sb.toString();
9126497Sache    }
9226497Sache
9326497Sache    @Override
9426497Sache    public List<String> getMimeTypes() {
9526497Sache        return Collections.unmodifiableList(mimeTypes);
9626497Sache    }
9726497Sache
9826497Sache    @Override
9926497Sache    public List<String> getNames() {
10026497Sache        return Collections.unmodifiableList(names);
10126497Sache    }
10226497Sache
10326497Sache    @Override
10426497Sache    public String getOutputStatement(final String toDisplay) {
10526497Sache        return "print(" + toDisplay + ")";
10626497Sache    }
10726497Sache
10826497Sache    @Override
10926497Sache    public Object getParameter(final String key) {
11026497Sache        switch (key) {
11126497Sache        case ScriptEngine.NAME:
11226497Sache            return "javascript";
11326497Sache        case ScriptEngine.ENGINE:
11426497Sache            return "Oracle Nashorn";
11526497Sache        case ScriptEngine.ENGINE_VERSION:
11626497Sache            return Version.version();
11726497Sache        case ScriptEngine.LANGUAGE:
11826497Sache            return "ECMAScript";
11926497Sache        case ScriptEngine.LANGUAGE_VERSION:
12026497Sache            return "ECMA - 262 Edition 5.1";
12126497Sache        case "THREADING":
12226497Sache            // The engine implementation is not thread-safe. Can't be
12326497Sache            // used to execute scripts concurrently on multiple threads.
12426497Sache            return null;
12526497Sache        default:
12626497Sache            return null;
12726497Sache        }
12826497Sache    }
12926497Sache
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