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