1 /* 2 * Copyright (c) 2005, 2006, 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 com.sun.script.javascript; 27 import com.sun.script.util.*; 28 import javax.script.*; 29 import sun.org.mozilla.javascript.internal.*; 30 import java.lang.reflect.Method; 31 import java.io.*; 32 import java.util.*; 33 34 35 /** 36 * Implementation of <code>ScriptEngine</code> using the Mozilla Rhino 37 * interpreter. 38 * 39 * @author Mike Grogan 40 * @author A. Sundararajan 41 * @since 1.6 42 */ 43 public final class RhinoScriptEngine extends AbstractScriptEngine 44 implements Invocable, Compilable { 45 46 private static final boolean DEBUG = false; 47 48 /* Scope where standard JavaScript objects and our 49 * extensions to it are stored. Note that these are not 50 * user defined engine level global variables. These are 51 * variables have to be there on all compliant ECMAScript 52 * scopes. We put these standard objects in this top level. 53 */ 54 private RhinoTopLevel topLevel; 55 56 /* map used to store indexed properties in engine scope 57 * refer to comment on 'indexedProps' in ExternalScriptable.java. 58 */ 59 private Map<Object, Object> indexedProps; 60 61 private ScriptEngineFactory factory; 62 private InterfaceImplementor implementor; 63 64 private static final int languageVersion = getLanguageVersion(); 65 private static final int optimizationLevel = getOptimizationLevel(); 66 static { 67 ContextFactory.initGlobal(new ContextFactory() { 68 protected Context makeContext() { 69 Context cx = super.makeContext(); 70 cx.setLanguageVersion(languageVersion); 71 cx.setOptimizationLevel(optimizationLevel); 72 cx.setClassShutter(RhinoClassShutter.getInstance()); 73 cx.setWrapFactory(RhinoWrapFactory.getInstance()); 74 return cx; 75 } 76 }); 77 } 78 79 private static final String RHINO_JS_VERSION = "rhino.js.version"; 80 private static int getLanguageVersion() { 81 int version; 82 String tmp = java.security.AccessController.doPrivileged( 83 new sun.security.action.GetPropertyAction(RHINO_JS_VERSION)); 84 if (tmp != null) { 85 version = Integer.parseInt((String)tmp); 86 } else { 87 version = Context.VERSION_1_8; 88 } 89 return version; 90 } 91 92 private static final String RHINO_OPT_LEVEL = "rhino.opt.level"; 93 private static int getOptimizationLevel() { 94 int optLevel = -1; 95 // disable optimizer under security manager, for now. 96 if (System.getSecurityManager() == null) { 97 optLevel = Integer.getInteger(RHINO_OPT_LEVEL, -1); 98 } 99 return optLevel; 100 } 101 102 /** 103 * Creates a new instance of RhinoScriptEngine 104 */ 105 public RhinoScriptEngine() { 106 107 Context cx = enterContext(); 108 try { 109 topLevel = new RhinoTopLevel(cx, this); 110 } finally { 111 cx.exit(); 112 } 113 114 indexedProps = new HashMap<Object, Object>(); 115 116 //construct object used to implement getInterface 117 implementor = new InterfaceImplementor(this) { 118 protected boolean isImplemented(Object thiz, Class<?> iface) { 119 Context cx = enterContext(); 120 try { 121 if (thiz != null && !(thiz instanceof Scriptable)) { 122 thiz = cx.toObject(thiz, topLevel); 123 } 124 Scriptable engineScope = getRuntimeScope(context); 125 Scriptable localScope = (thiz != null)? (Scriptable) thiz : 126 engineScope; 127 for (Method method : iface.getMethods()) { 128 // ignore methods of java.lang.Object class 129 if (method.getDeclaringClass() == Object.class) { 130 continue; 131 } 132 Object obj = ScriptableObject.getProperty(localScope, method.getName()); 133 if (! (obj instanceof Function)) { 134 return false; 135 } 136 } 137 return true; 138 } finally { 139 cx.exit(); 140 } 141 } 142 143 protected Object convertResult(Method method, Object res) 144 throws ScriptException { 145 Class desiredType = method.getReturnType(); 146 if (desiredType == Void.TYPE) { 147 return null; 148 } else { 149 return Context.jsToJava(res, desiredType); 150 } 151 } 152 }; 153 } 154 155 public Object eval(Reader reader, ScriptContext ctxt) 156 throws ScriptException { 157 Object ret; 158 159 Context cx = enterContext(); 160 try { 161 Scriptable scope = getRuntimeScope(ctxt); 162 String filename = (String) get(ScriptEngine.FILENAME); 163 filename = filename == null ? "<Unknown source>" : filename; 164 165 ret = cx.evaluateReader(scope, reader, filename , 1, null); 166 } catch (RhinoException re) { 167 if (DEBUG) re.printStackTrace(); 168 int line = (line = re.lineNumber()) == 0 ? -1 : line; 169 String msg; 170 if (re instanceof JavaScriptException) { 171 msg = String.valueOf(((JavaScriptException)re).getValue()); 172 } else { 173 msg = re.toString(); 174 } 175 ScriptException se = new ScriptException(msg, re.sourceName(), line); 176 se.initCause(re); 177 throw se; 178 } catch (IOException ee) { 179 throw new ScriptException(ee); 180 } finally { 181 cx.exit(); 182 } 183 184 return unwrapReturnValue(ret); 185 } 186 187 public Object eval(String script, ScriptContext ctxt) throws ScriptException { 188 if (script == null) { 189 throw new NullPointerException("null script"); 190 } 191 return eval(new StringReader(script) , ctxt); 192 } 193 194 public ScriptEngineFactory getFactory() { 195 if (factory != null) { 196 return factory; 197 } else { 198 return new RhinoScriptEngineFactory(); 199 } 200 } 201 202 public Bindings createBindings() { 203 return new SimpleBindings(); 204 } 205 206 //Invocable methods 207 public Object invokeFunction(String name, Object... args) 208 throws ScriptException, NoSuchMethodException { 209 return invoke(null, name, args); 210 } 211 212 public Object invokeMethod(Object thiz, String name, Object... args) 213 throws ScriptException, NoSuchMethodException { 214 if (thiz == null) { 215 throw new IllegalArgumentException("script object can not be null"); 216 } 217 return invoke(thiz, name, args); 218 } 219 220 private Object invoke(Object thiz, String name, Object... args) 221 throws ScriptException, NoSuchMethodException { 222 Context cx = enterContext(); 223 try { 224 if (name == null) { 225 throw new NullPointerException("method name is null"); 226 } 227 228 if (thiz != null && !(thiz instanceof Scriptable)) { 229 thiz = cx.toObject(thiz, topLevel); 230 } 231 232 Scriptable engineScope = getRuntimeScope(context); 233 Scriptable localScope = (thiz != null)? (Scriptable) thiz : 234 engineScope; 235 Object obj = ScriptableObject.getProperty(localScope, name); 236 if (! (obj instanceof Function)) { 237 throw new NoSuchMethodException("no such method: " + name); 238 } 239 240 Function func = (Function) obj; 241 Scriptable scope = func.getParentScope(); 242 if (scope == null) { 243 scope = engineScope; 244 } 245 Object result = func.call(cx, scope, localScope, 246 wrapArguments(args)); 247 return unwrapReturnValue(result); 248 } catch (RhinoException re) { 249 if (DEBUG) re.printStackTrace(); 250 int line = (line = re.lineNumber()) == 0 ? -1 : line; 251 ScriptException se = new ScriptException(re.toString(), re.sourceName(), line); 252 se.initCause(re); 253 throw se; 254 } finally { 255 cx.exit(); 256 } 257 } 258 259 public <T> T getInterface(Class<T> clasz) { 260 try { 261 return implementor.getInterface(null, clasz); 262 } catch (ScriptException e) { 263 return null; 264 } 265 } 266 267 public <T> T getInterface(Object thiz, Class<T> clasz) { 268 if (thiz == null) { 269 throw new IllegalArgumentException("script object can not be null"); 270 } 271 272 try { 273 return implementor.getInterface(thiz, clasz); 274 } catch (ScriptException e) { 275 return null; 276 } 277 } 278 279 private static final String printSource = 280 "function print(str, newline) { \n" + 281 " if (typeof(str) == 'undefined') { \n" + 282 " str = 'undefined'; \n" + 283 " } else if (str == null) { \n" + 284 " str = 'null'; \n" + 285 " } \n" + 286 " var out = context.getWriter(); \n" + 287 " if (!(out instanceof java.io.PrintWriter))\n" + 288 " out = new java.io.PrintWriter(out); \n" + 289 " out.print(String(str)); \n" + 290 " if (newline) out.print('\\n'); \n" + 291 " out.flush(); \n" + 292 "}\n" + 293 "function println(str) { \n" + 294 " print(str, true); \n" + 295 "}"; 296 297 Scriptable getRuntimeScope(ScriptContext ctxt) { 298 if (ctxt == null) { 299 throw new NullPointerException("null script context"); 300 } 301 302 // we create a scope for the given ScriptContext 303 Scriptable newScope = new ExternalScriptable(ctxt, indexedProps); 304 305 // Set the prototype of newScope to be 'topLevel' so that 306 // JavaScript standard objects are visible from the scope. 307 newScope.setPrototype(topLevel); 308 309 // define "context" variable in the new scope 310 newScope.put("context", newScope, ctxt); 311 312 // define "print", "println" functions in the new scope 313 Context cx = enterContext(); 314 try { 315 cx.evaluateString(newScope, printSource, "print", 1, null); 316 } finally { 317 cx.exit(); 318 } 319 return newScope; 320 } 321 322 323 //Compilable methods 324 public CompiledScript compile(String script) throws ScriptException { 325 return compile(new StringReader(script)); 326 } 327 328 public CompiledScript compile(java.io.Reader script) throws ScriptException { 329 CompiledScript ret = null; 330 Context cx = enterContext(); 331 332 try { 333 String fileName = (String) get(ScriptEngine.FILENAME); 334 if (fileName == null) { 335 fileName = "<Unknown Source>"; 336 } 337 338 Scriptable scope = getRuntimeScope(context); 339 Script scr = cx.compileReader(scope, script, fileName, 1, null); 340 ret = new RhinoCompiledScript(this, scr); 341 } catch (Exception e) { 342 if (DEBUG) e.printStackTrace(); 343 throw new ScriptException(e); 344 } finally { 345 cx.exit(); 346 } 347 return ret; 348 } 349 350 351 //package-private helpers 352 353 static Context enterContext() { 354 // call this always so that initializer of this class runs 355 // and initializes custom wrap factory and class shutter. 356 return Context.enter(); 357 } 358 359 void setEngineFactory(ScriptEngineFactory fac) { 360 factory = fac; 361 } 362 363 Object[] wrapArguments(Object[] args) { 364 if (args == null) { 365 return Context.emptyArgs; 366 } 367 Object[] res = new Object[args.length]; 368 for (int i = 0; i < res.length; i++) { 369 res[i] = Context.javaToJS(args[i], topLevel); 370 } 371 return res; 372 } 373 374 Object unwrapReturnValue(Object result) { 375 if (result instanceof Wrapper) { 376 result = ( (Wrapper) result).unwrap(); 377 } 378 379 return result instanceof Undefined ? null : result; 380 } 381 382 /* 383 public static void main(String[] args) throws Exception { 384 if (args.length == 0) { 385 System.out.println("No file specified"); 386 return; 387 } 388 389 InputStreamReader r = new InputStreamReader(new FileInputStream(args[0])); 390 ScriptEngine engine = new RhinoScriptEngine(); 391 392 engine.put("x", "y"); 393 engine.put(ScriptEngine.FILENAME, args[0]); 394 engine.eval(r); 395 System.out.println(engine.get("x")); 396 } 397 */ 398 }