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