/*
* JEF - Copyright 2009-2010 Jiyi (mr.jiyi@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jef.script.javascript;
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.SimpleScriptContext;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.ImporterTopLevel;
import org.mozilla.javascript.LazilyLoadedCtor;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Synchronizer;
import org.mozilla.javascript.Wrapper;
/**
* This class serves as top level scope for Rhino. This class adds
* 3 top level functions (bindings, scope, sync) and two constructors
* (JSAdapter, JavaAdapter).
*
* @version 1.0
* @author A. Sundararajan
* @since 1.6
*/
public final class RhinoTopLevel extends ImporterTopLevel {
private static final long serialVersionUID = 1L;
// variables defined always to help Java access from JavaScript
private static final String builtinVariables =
"var com = Packages.com; \n" +
"var edu = Packages.edu; \n" +
"var javax = Packages.javax; \n" +
"var net = Packages.net; \n" +
"var org = Packages.org; \n";
RhinoTopLevel(Context cx, RhinoScriptEngine engine) {
super(cx);
this.engine = engine;
// initialize JSAdapter lazily. Reduces footprint & startup time.
new LazilyLoadedCtor(this, "JSAdapter",
"com.sun.script.javascript.JSAdapter",
false);
/*
* initialize JavaAdapter. We can't lazy initialize this because
* lazy initializer attempts to define a new property. But, JavaAdapter
* is an exisiting property that we overwrite.
*/
JavaAdapter.init(cx, this, false);
// add top level functions
String names[] = { "bindings", "scope", "sync" };
defineFunctionProperties(names, RhinoTopLevel.class,
ScriptableObject.DONTENUM);
// define built-in variables
cx.evaluateString(this, builtinVariables, "<builtin>", 1, null);
}
/**
* The bindings function takes a JavaScript scope object
* of type ExternalScriptable and returns the underlying Bindings
* instance.
*
* var page = scope(pageBindings);
* with (page) {
* // code that uses page scope
* }
* var b = bindings(page);
* // operate on bindings here.
*/
public static Object bindings(Context cx, Scriptable thisObj, Object[] args,
Function funObj) {
if (args.length == 1) {
Object arg = args[0];
if (arg instanceof Wrapper) {
arg = ((Wrapper)arg).unwrap();
}
if (arg instanceof ExternalScriptable) {
ScriptContext ctx = ((ExternalScriptable)arg).getContext();
Bindings bind = ctx.getBindings(ScriptContext.ENGINE_SCOPE);
return Context.javaToJS(bind,
ScriptableObject.getTopLevelScope(thisObj));
}
}
return Context.getUndefinedValue();
}
/**
* The scope function creates a new JavaScript scope object
* with given Bindings object as backing store. This can be used
* to create a script scope based on arbitrary Bindings instance.
* For example, in webapp scenario, a 'page' level Bindings instance
* may be wrapped as a scope and code can be run in JavaScripe 'with'
* statement:
*
* var page = scope(pageBindings);
* with (page) {
* // code that uses page scope
* }
*/
public static Object scope(Context cx, Scriptable thisObj, Object[] args,
Function funObj) {
if (args.length == 1) {
Object arg = args[0];
if (arg instanceof Wrapper) {
arg = ((Wrapper)arg).unwrap();
}
if (arg instanceof Bindings) {
ScriptContext ctx = new SimpleScriptContext();
ctx.setBindings((Bindings)arg, ScriptContext.ENGINE_SCOPE);
Scriptable res = new ExternalScriptable(ctx);
res.setPrototype(ScriptableObject.getObjectPrototype(thisObj));
res.setParentScope(ScriptableObject.getTopLevelScope(thisObj));
return res;
}
}
return Context.getUndefinedValue();
}
/**
* The sync function creates a synchronized function (in the sense
* of a Java synchronized method) from an existing function. The
* new function synchronizes on the <code>this</code> object of
* its invocation.
* js> var o = { f : sync(function(x) {
* print("entry");
* Packages.java.lang.Thread.sleep(x*1000);
* print("exit");
* })};
* js> thread(function() {o.f(5);});
* entry
* js> thread(function() {o.f(5);});
* js>
* exit
* entry
* exit
*/
public static Object sync(Context cx, Scriptable thisObj, Object[] args,
Function funObj) {
if (args.length == 1 && args[0] instanceof Function) {
return new Synchronizer((Function)args[0]);
} else {
throw Context.reportRuntimeError("wrong argument(s) for sync");
}
}
RhinoScriptEngine getScriptEngine() {
return engine;
}
private RhinoScriptEngine engine;
}