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 javax.script.ScriptEngine; 32 import javax.script.ScriptEngineFactory; 33 import jdk.nashorn.internal.runtime.Context; 34 import 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 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().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 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 }