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 }
--- EOF ---