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