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.Version; 34 import sun.reflect.Reflection; 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 @Override 139 public ScriptEngine getScriptEngine() { 140 return new NashornScriptEngine(this, getAppClassLoader()); 141 } 142 143 /** 144 * Create a new Script engine initialized by given class loader. 145 * 146 * @param appLoader class loader to be used as script "app" class loader. 147 * @return newly created script engine. 148 */ 149 public ScriptEngine getScriptEngine(final ClassLoader appLoader) { 150 return new NashornScriptEngine(this, appLoader); 151 } 152 153 /** 154 * Create a new Script engine initialized by given arguments. 155 * 156 * @param args arguments array passed to script engine. 157 * @return newly created script engine. 158 */ 159 public ScriptEngine getScriptEngine(final String[] args) { 160 return new NashornScriptEngine(this, args, getAppClassLoader()); 161 } 162 163 /** 164 * Create a new Script engine initialized by given arguments. 165 * 166 * @param args arguments array passed to script engine. 167 * @param appLoader class loader to be used as script "app" class loader. 168 * @return newly created script engine. 169 */ 170 public ScriptEngine getScriptEngine(final String[] args, final ClassLoader appLoader) { 171 return new NashornScriptEngine(this, args, appLoader); 172 } 173 174 // -- Internals only below this point 175 176 private static final List<String> names; 177 private static final List<String> mimeTypes; 178 private static final List<String> extensions; 179 180 static { 181 names = immutableList( 182 "nashorn", "Nashorn", 183 "js", "JS", 184 "JavaScript", "javascript", 185 "ECMAScript", "ecmascript" 186 ); 187 188 mimeTypes = immutableList( 189 "application/javascript", 190 "application/ecmascript", 191 "text/javascript", 192 "text/ecmascript" 193 ); 194 195 extensions = immutableList("js"); 196 } 197 198 private static List<String> immutableList(final String... elements) { 199 return Collections.unmodifiableList(Arrays.asList(elements)); 200 } 201 202 private static ClassLoader getAppClassLoader() { 203 if (System.getSecurityManager() == null) { 204 return Thread.currentThread().getContextClassLoader(); 205 } 206 207 // Try to determine the caller class loader. Use that if it can be 208 // found. If not, use the class loader of nashorn itself as the 209 // "application" class loader for scripts. 210 211 // User could have called ScriptEngineFactory.getScriptEngine() 212 // 213 // <caller> 214 // <factory.getScriptEngine()> 215 // <factory.getAppClassLoader()> 216 // <Reflection.getCallerClass()> 217 // 218 // or used one of the getEngineByABC methods of ScriptEngineManager. 219 // 220 // <caller> 221 // <ScriptEngineManager.getEngineByName()> 222 // <factory.getScriptEngine()> 223 // <factory.getAppClassLoader()> 224 // <Reflection.getCallerClass()> 225 226 // So, stack depth is 3 or 4 (recall it is zero based). We try 227 // stack depths 3, 4 and look for non-bootstrap caller. 228 Class<?> caller = null; 229 for (int depth = 3; depth < 5; depth++) { 230 caller = Reflection.getCallerClass(depth); 231 if (caller != null && caller.getClassLoader() != null) { 232 // found a non-bootstrap caller 233 break; 234 } 235 } 236 237 final ClassLoader ccl = (caller == null)? null : caller.getClassLoader(); 238 // if caller loader is null, then use nashorn's own loader 239 return (ccl == null)? NashornScriptEngineFactory.class.getClassLoader() : ccl; 240 } 241 } --- EOF ---