       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
      26 package jdk.nashorn.api.scripting;
      27
      28 import java.util.Arrays;
      29 import java.util.Collections;
      30 import java.util.List;
      31 import java.util.Objects;
      32 import javax.script.ScriptEngine;
      33 import javax.script.ScriptEngineFactory;
      34 import jdk.nashorn.internal.runtime.Context;
      35 import 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 public 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().
      80             append(Objects.requireNonNull(obj)).append('.').
      81             append(Objects.requireNonNull(method)).append('(');
      82         final int len = args.length;
      83
      84         if (len > 0) {
      85             sb.append(Objects.requireNonNull(args[0]));
      86         }
      87         for (int i = 1; i < len; i++) {
      88             sb.append(',').append(Objects.requireNonNull(args[i]));
      89         }
      90         sb.append(')');
      91
      92         return sb.toString();
      93     }
      94
      95     @Override
      96     public List<String> getMimeTypes() {
      97         return Collections.unmodifiableList(mimeTypes);
      98     }
      99
     100     @Override
     101     public List<String> getNames() {
     102         return Collections.unmodifiableList(names);
     103     }
     104
     105     @Override
     106     public String getOutputStatement(final String toDisplay) {
     107         return "print(" + toDisplay + ")";
     108     }
     109
     110     @Override
     111     public Object getParameter(final String key) {
     112         switch (key) {
     113         case ScriptEngine.NAME:
     114             return "javascript";
     115         case ScriptEngine.ENGINE:
     116             return "Oracle Nashorn";
     117         case ScriptEngine.ENGINE_VERSION:
     118             return Version.version();
     119         case ScriptEngine.LANGUAGE:
     120             return "ECMAScript";
     121         case ScriptEngine.LANGUAGE_VERSION:
     122             return "ECMA - 262 Edition 5.1";
     123         case "THREADING":
     124             // The engine implementation is not thread-safe. Can't be
     125             // used to execute scripts concurrently on multiple threads.
     126             return null;
     127         default:
     128             return null;
     129         }
     130     }
     131
     132     @Override
     133     public String getProgram(final String... statements) {
     134         Objects.requireNonNull(statements);
     135         final StringBuilder sb = new StringBuilder();
     136
     137         for (final String statement : statements) {
     138             sb.append(Objects.requireNonNull(statement)).append(';');
     139         }
     140
     141         return sb.toString();
     142     }
     143
     144     // default options passed to Nashorn script engine
     145     private static final String[] DEFAULT_OPTIONS = new String[] { "-doe" };
     146
     147     @Override
     148     public ScriptEngine getScriptEngine() {
     149         try {
     150             return new NashornScriptEngine(this, DEFAULT_OPTIONS, getAppClassLoader(), null);
     151         } catch (final RuntimeException e) {
     152             if (Context.DEBUG) {
     153                 e.printStackTrace();
     154             }
     155             throw e;
     156         }
     157     }
     158
     159     /**
     160      * Create a new Script engine initialized with the given class loader.
     161      *
     162      * @param appLoader class loader to be used as script "app" class loader.
     163      * @return newly created script engine.
     164      * @throws SecurityException
     165      *         if the security manager's {@code checkPermission}
     166      *         denies {@code RuntimePermission("nashorn.setConfig")}
     167      */
     168     public ScriptEngine getScriptEngine(final ClassLoader appLoader) {
     169         return newEngine(DEFAULT_OPTIONS, appLoader, null);
     170     }
     171
     172     /**
     173      * Create a new Script engine initialized with the given class filter.
     174      *
     175      * @param classFilter class filter to use.
     176      * @return newly created script engine.
     177      * @throws NullPointerException if {@code classFilter} is {@code null}
     178      * @throws SecurityException
     179      *         if the security manager's {@code checkPermission}
     180      *         denies {@code RuntimePermission("nashorn.setConfig")}
     181      */
     182     public ScriptEngine getScriptEngine(final ClassFilter classFilter) {
     183         return newEngine(DEFAULT_OPTIONS, getAppClassLoader(), Objects.requireNonNull(classFilter));
     184     }
     185
     186     /**
     187      * Create a new Script engine initialized with the given arguments.
     188      *
     189      * @param args arguments array passed to script engine.
     190      * @return newly created script engine.
     191      * @throws NullPointerException if {@code args} is {@code null}
     192      * @throws SecurityException
     193      *         if the security manager's {@code checkPermission}
     194      *         denies {@code RuntimePermission("nashorn.setConfig")}
     195      */
     196     public ScriptEngine getScriptEngine(final String... args) {
     197         return newEngine(Objects.requireNonNull(args), getAppClassLoader(), null);
     198     }
     199
     200     /**
     201      * Create a new Script engine initialized with the given arguments and the given class loader.
     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         return newEngine(Objects.requireNonNull(args), appLoader, null);
     213     }
     214
     215     /**
     216      * Create a new Script engine initialized with the given arguments, class loader and class filter.
     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         return newEngine(Objects.requireNonNull(args), appLoader, Objects.requireNonNull(classFilter));
     229     }
     230
     231     private ScriptEngine newEngine(final String[] args, final ClassLoader appLoader, final ClassFilter classFilter) {
     232         checkConfigPermission();
     233         try {
     234             return new NashornScriptEngine(this, args, appLoader, classFilter);
     235         } catch (final RuntimeException e) {
     236             if (Context.DEBUG) {
     237                 e.printStackTrace();
     238             }
     239             throw e;
     240         }
     241     }
     242
     243     // -- Internals only below this point
     244
     245     private static void checkConfigPermission() {
     246         final SecurityManager sm = System.getSecurityManager();
     247         if (sm != null) {
     248             sm.checkPermission(new RuntimePermission(Context.NASHORN_SET_CONFIG));
     249         }
     250     }
     251
     252     private static final List<String> names;
     253     private static final List<String> mimeTypes;
     254     private static final List<String> extensions;
     255
     256     static {
     257         names = immutableList(
     258                     "nashorn", "Nashorn",
     259                     "js", "JS",
     260                     "JavaScript", "javascript",
     261                     "ECMAScript", "ecmascript"
     262                 );
     263
     264         mimeTypes = immutableList(
     265                         "application/javascript",
     266                         "application/ecmascript",
     267                         "text/javascript",
     268                         "text/ecmascript"
     269                     );
     270
     271         extensions = immutableList("js");
     272     }
     273
     274     private static List<String> immutableList(final String... elements) {
     275         return Collections.unmodifiableList(Arrays.asList(elements));
     276     }
     277
     278     private static ClassLoader getAppClassLoader() {
     279         // Revisit: script engine implementation needs the capability to
     280         // find the class loader of the context in which the script engine
     281         // is running so that classes will be found and loaded properly
     282         final ClassLoader ccl = Thread.currentThread().getContextClassLoader();
     283         return (ccl == null)? NashornScriptEngineFactory.class.getClassLoader() : ccl;
     284     }
     285 }
    --- EOF ---