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