This commit is contained in:
kunfei 2023-03-20 16:50:58 +08:00
parent f53eca326b
commit add9fddfbd
12 changed files with 979 additions and 1449 deletions

View File

@ -5,6 +5,7 @@ import android.content.pm.PackageManager
import android.provider.Settings import android.provider.Settings
import androidx.annotation.Keep import androidx.annotation.Keep
import com.script.rhino.RhinoScriptEngine import com.script.rhino.RhinoScriptEngine
import io.legado.app.BuildConfig import io.legado.app.BuildConfig
import io.legado.app.utils.channel import io.legado.app.utils.channel
import splitties.init.appCtx import splitties.init.appCtx

View File

@ -1,5 +1,6 @@
plugins { plugins {
id 'com.android.library' id 'com.android.library'
id 'org.jetbrains.kotlin.android'
} }
android { android {

View File

@ -1,5 +1,6 @@
plugins { plugins {
id 'com.android.library' id 'com.android.library'
id 'org.jetbrains.kotlin.android'
} }
android { android {
@ -22,8 +23,8 @@ android {
} }
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_11
} }
lint { lint {
checkDependencies true checkDependencies true

View File

@ -22,24 +22,11 @@
* or visit www.oracle.com if you need additional information or have any * or visit www.oracle.com if you need additional information or have any
* questions. * questions.
*/ */
package com.script.rhino
package com.script.rhino; import com.script.ScriptContext
import org.mozilla.javascript.*
import com.script.Bindings; import org.mozilla.javascript.Function
import com.script.ScriptContext;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.NativeJavaClass;
import org.mozilla.javascript.ScriptRuntime;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Wrapper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/** /**
* ExternalScriptable is an implementation of Scriptable * ExternalScriptable is an implementation of Scriptable
@ -49,430 +36,252 @@ import java.util.Map;
* @author A. Sundararajan * @author A. Sundararajan
* @since 1.6 * @since 1.6
*/ */
internal class ExternalScriptable @JvmOverloads constructor(
context: ScriptContext?,
indexedProps: MutableMap<Any, Any> = HashMap()
) : Scriptable {
val context: ScriptContext
private val indexedProps: MutableMap<Any, Any>
private var prototype: Scriptable? = null
private var parent: Scriptable? = null
final class ExternalScriptable implements Scriptable { init {
/* Underlying ScriptContext that we use to store
* named variables of this scope.
*/
private ScriptContext context;
/* JavaScript allows variables to be named as numbers (indexed
* properties). This way arrays, objects (scopes) are treated uniformly.
* Note that JSR 223 API supports only String named variables and
* so we can't store these in Bindings. Also, JavaScript allows name
* of the property name to be even empty String! Again, JSR 223 API
* does not support empty name. So, we use the following fallback map
* to store such variables of this scope. This map is not exposed to
* JSR 223 API. We can just script objects "as is" and need not convert.
*/
private Map<Object, Object> indexedProps;
// my prototype
private Scriptable prototype;
// my parent scope, if any
private Scriptable parent;
ExternalScriptable(ScriptContext context) {
this(context, new HashMap<Object, Object>());
}
ExternalScriptable(ScriptContext context, Map<Object, Object> indexedProps) {
if (context == null) { if (context == null) {
throw new NullPointerException("context is null"); throw NullPointerException("context is null")
}
this.context = context;
this.indexedProps = indexedProps;
}
ScriptContext getContext() {
return context;
}
private boolean isEmpty(String name) {
return name.equals("");
}
/**
* Return the name of the class.
*/
public String getClassName() {
return "Global";
}
/**
* Returns the value of the named property or NOT_FOUND.
*
* If the property was created using defineProperty, the
* appropriate getter method is called.
*
* @param name the name of the property
* @param start the object in which the lookup began
* @return the value of the property (may be null), or NOT_FOUND
*/
public synchronized Object get(String name, Scriptable start) {
if (isEmpty(name)) {
if (indexedProps.containsKey(name)) {
return indexedProps.get(name);
} else {
return NOT_FOUND;
}
} else { } else {
synchronized (context) { this.context = context
int scope = context.getAttributesScope(name); this.indexedProps = indexedProps
if (scope != -1) { }
Object value = context.getAttribute(name, scope); }
return Context.javaToJS(value, this);
private fun isEmpty(name: String): Boolean {
return name == ""
}
override fun getClassName(): String {
return "Global"
}
@Synchronized
override fun get(name: String, start: Scriptable): Any {
return if (this.isEmpty(name)) {
indexedProps.getOrElse(name) { Scriptable.NOT_FOUND }
} else {
synchronized(context) {
val scope = context.getAttributesScope(name)
return if (scope != -1) {
val value = context.getAttribute(name, scope)
Context.javaToJS(value, this)
} else { } else {
return NOT_FOUND; Scriptable.NOT_FOUND
} }
} }
} }
} }
/** @Synchronized
* Returns the value of the indexed property or NOT_FOUND. override fun get(index: Int, start: Scriptable): Any {
* return indexedProps.getOrElse(index) { Scriptable.NOT_FOUND }
* @param index the numeric index for the property }
* @param start the object in which the lookup began
* @return the value of the property (may be null), or NOT_FOUND @Synchronized
*/ override fun has(name: String, start: Scriptable): Boolean {
public synchronized Object get(int index, Scriptable start) { return if (this.isEmpty(name)) {
if (indexedProps.containsKey(index)) { indexedProps.containsKey(name)
return indexedProps.get(index);
} else { } else {
return NOT_FOUND; synchronized(context) { return context.getAttributesScope(name) != -1 }
} }
} }
/** @Synchronized
* Returns true if the named property is defined. override fun has(index: Int, start: Scriptable): Boolean {
* return indexedProps.containsKey(index)
* @param name the name of the property
* @param start the object in which the lookup began
* @return true if and only if the property was found in the object
*/
public synchronized boolean has(String name, Scriptable start) {
if (isEmpty(name)) {
return indexedProps.containsKey(name);
} else {
synchronized (context) {
return context.getAttributesScope(name) != -1;
}
}
} }
/** override fun put(name: String, start: Scriptable, value: Any) {
* Returns true if the property index is defined. if (start === this) {
* synchronized(this) {
* @param index the numeric index for the property if (this.isEmpty(name)) {
* @param start the object in which the lookup began indexedProps.put(name, value)
* @return true if and only if the property was found in the object
*/
public synchronized boolean has(int index, Scriptable start) {
return indexedProps.containsKey(index);
}
/**
* Sets the value of the named property, creating it if need be.
*
* @param name the name of the property
* @param start the object whose property is being set
* @param value value to set the property to
*/
public void put(String name, Scriptable start, Object value) {
if (start == this) {
synchronized (this) {
if (isEmpty(name)) {
indexedProps.put(name, value);
} else { } else {
synchronized (context) { synchronized(context) {
int scope = context.getAttributesScope(name); var scope = context.getAttributesScope(name)
if (scope == -1) { if (scope == -1) {
scope = ScriptContext.ENGINE_SCOPE; scope = 100
} }
context.setAttribute(name, jsToJava(value), scope); context.setAttribute(name, jsToJava(value), scope)
} }
} }
} }
} else { } else {
start.put(name, start, value); start.put(name, start, value)
} }
} }
/** override fun put(index: Int, start: Scriptable, value: Any) {
* Sets the value of the indexed property, creating it if need be. if (start === this) {
* synchronized(this) { indexedProps.put(index, value) }
* @param index the numeric index for the property
* @param start the object whose property is being set
* @param value value to set the property to
*/
public void put(int index, Scriptable start, Object value) {
if (start == this) {
synchronized (this) {
indexedProps.put(index, value);
}
} else { } else {
start.put(index, start, value); start.put(index, start, value)
} }
} }
/** @Synchronized
* Removes a named property from the object. override fun delete(name: String) {
* if (this.isEmpty(name)) {
* If the property is not found, no action is taken. indexedProps.remove(name)
*
* @param name the name of the property
*/
public synchronized void delete(String name) {
if (isEmpty(name)) {
indexedProps.remove(name);
} else { } else {
synchronized (context) { synchronized(context) {
int scope = context.getAttributesScope(name); val scope = context.getAttributesScope(name)
if (scope != -1) { if (scope != -1) {
context.removeAttribute(name, scope); context.removeAttribute(name, scope)
} }
} }
} }
} }
/** override fun delete(index: Int) {
* Removes the indexed property from the object. indexedProps.remove(index)
*
* If the property is not found, no action is taken.
*
* @param index the numeric index for the property
*/
public void delete(int index) {
indexedProps.remove(index);
} }
/** override fun getPrototype(): Scriptable? {
* Get the prototype of the object. return prototype
* @return the prototype
*/
public Scriptable getPrototype() {
return prototype;
} }
/** override fun setPrototype(prototype: Scriptable?) {
* Set the prototype of the object. this.prototype = prototype
* @param prototype the prototype to set
*/
public void setPrototype(Scriptable prototype) {
this.prototype = prototype;
} }
/** override fun getParentScope(): Scriptable? {
* Get the parent scope of the object. return parent
* @return the parent scope
*/
public Scriptable getParentScope() {
return parent;
} }
/** override fun setParentScope(parent: Scriptable?) {
* Set the parent scope of the object. this.parent = parent
* @param parent the parent scope to set
*/
public void setParentScope(Scriptable parent) {
this.parent = parent;
} }
/** @Synchronized
* Get an array of property ids. override fun getIds(): Array<Any> {
* val keys = allKeys
* Not all property ids need be returned. Those properties val size = keys.size + indexedProps.size
* whose ids are not returned are considered non-enumerable. val res = arrayOfNulls<Any>(size)
* System.arraycopy(keys, 0, res, 0, keys.size)
* @return an array of Objects. Each entry in the array is either var i = keys.size
* a java.lang.String or a java.lang.Number var index: Any
*/ val var5: Iterator<*> = indexedProps.keys.iterator()
public synchronized Object[] getIds() { while (var5.hasNext()) {
String[] keys = getAllKeys(); index = var5.next()!!
int size = keys.length + indexedProps.size(); res[i++] = index
Object[] res = new Object[size];
System.arraycopy(keys, 0, res, 0, keys.length);
int i = keys.length;
// now add all indexed properties
for (Object index : indexedProps.keySet()) {
res[i++] = index;
} }
return res; @Suppress("UNCHECKED_CAST")
return res as Array<Any>
} }
/** override fun getDefaultValue(typeHint: Class<*>?): Any {
* Get the default value of the object with a given hint. for (i in 0..1) {
* The hints are String.class for type String, Number.class for type val tryToString: Boolean =
* Number, Scriptable.class for type Object, and Boolean.class for if (typeHint == ScriptRuntime.StringClass) {
* type Boolean. <p> i == 0
*
* A <code>hint</code> of null means "no hint".
*
* See ECMA 8.6.2.6.
*
* @param typeHint the type hint
* @return the default value
*/
public Object getDefaultValue(Class typeHint) {
for (int i=0; i < 2; i++) {
boolean tryToString;
if (typeHint == ScriptRuntime.StringClass) {
tryToString = (i == 0);
} else {
tryToString = (i == 1);
}
String methodName;
Object[] args;
if (tryToString) {
methodName = "toString";
args = ScriptRuntime.emptyArgs;
} else {
methodName = "valueOf";
args = new Object[1];
String hint;
if (typeHint == null) {
hint = "undefined";
} else if (typeHint == ScriptRuntime.StringClass) {
hint = "string";
} else if (typeHint == ScriptRuntime.ScriptableClass) {
hint = "object";
} else if (typeHint == ScriptRuntime.FunctionClass) {
hint = "function";
} else if (typeHint == ScriptRuntime.BooleanClass
|| typeHint == Boolean.TYPE)
{
hint = "boolean";
} else if (typeHint == ScriptRuntime.NumberClass ||
typeHint == ScriptRuntime.ByteClass ||
typeHint == Byte.TYPE ||
typeHint == ScriptRuntime.ShortClass ||
typeHint == Short.TYPE ||
typeHint == ScriptRuntime.IntegerClass ||
typeHint == Integer.TYPE ||
typeHint == ScriptRuntime.FloatClass ||
typeHint == Float.TYPE ||
typeHint == ScriptRuntime.DoubleClass ||
typeHint == Double.TYPE)
{
hint = "number";
} else { } else {
throw Context.reportRuntimeError( i == 1
"Invalid JavaScript value of type " +
typeHint);
} }
args[0] = hint; var methodName: String
var args: Array<Any?>
if (tryToString) {
methodName = "toString"
args = ScriptRuntime.emptyArgs
} else {
methodName = "valueOf"
args = arrayOfNulls(1)
val hint: String = if (typeHint == null) {
"undefined"
} else if (typeHint == ScriptRuntime.StringClass) {
"string"
} else if (typeHint == ScriptRuntime.ScriptableClass) {
"object"
} else if (typeHint == ScriptRuntime.FunctionClass) {
"function"
} else if (typeHint != ScriptRuntime.BooleanClass && typeHint != java.lang.Boolean.TYPE) {
if (typeHint != ScriptRuntime.NumberClass && typeHint != ScriptRuntime.ByteClass && typeHint != java.lang.Byte.TYPE && typeHint != ScriptRuntime.ShortClass && typeHint != java.lang.Short.TYPE && typeHint != ScriptRuntime.IntegerClass && typeHint != Integer.TYPE && typeHint != ScriptRuntime.FloatClass && typeHint != java.lang.Float.TYPE && typeHint != ScriptRuntime.DoubleClass && typeHint != java.lang.Double.TYPE) {
throw Context.reportRuntimeError("Invalid JavaScript value of type $typeHint")
}
"number"
} else {
"boolean"
}
args[0] = hint
} }
Object v = ScriptableObject.getProperty(this, methodName); var v = ScriptableObject.getProperty(this, methodName)
if (!(v instanceof Function)) if (v is Function) {
continue; val `fun` = v
Function fun = (Function) v; val cx = Context.enter()
Context cx = RhinoScriptEngine.enterContext(); v = try {
try { `fun`.call(cx, `fun`.parentScope, this, args)
v = fun.call(cx, fun.getParentScope(), this, args); } finally {
} finally { Context.exit()
cx.exit();
}
if (v != null) {
if (!(v instanceof Scriptable)) {
return v;
} }
if (typeHint == ScriptRuntime.ScriptableClass if (v != null) {
|| typeHint == ScriptRuntime.FunctionClass) if (v !is Scriptable) {
{ return v
return v; }
} if (typeHint == ScriptRuntime.ScriptableClass || typeHint == ScriptRuntime.FunctionClass) {
if (tryToString && v instanceof Wrapper) { return v
// Let a wrapped java.lang.String pass for a primitive }
// string. if (tryToString && v is Wrapper) {
Object u = ((Wrapper)v).unwrap(); val u = (v as Wrapper).unwrap()
if (u instanceof String) if (u is String) {
return u; return u
} }
}
}
// fall through to error
String arg = (typeHint == null) ? "undefined" : typeHint.getName();
throw Context.reportRuntimeError(
"Cannot find default value for object " + arg);
}
/**
* Implements the instanceof operator.
*
* @param instance The value that appeared on the LHS of the instanceof
* operator
* @return true if "this" appears in value's prototype chain
*
*/
public boolean hasInstance(Scriptable instance) {
// Default for JS objects (other than Function) is to do prototype
// chasing.
Scriptable proto = instance.getPrototype();
while (proto != null) {
if (proto.equals(this)) return true;
proto = proto.getPrototype();
}
return false;
}
private String[] getAllKeys() {
ArrayList<String> list = new ArrayList<String>();
synchronized (context) {
for (int scope : context.getScopes()) {
Bindings bindings = context.getBindings(scope);
if (bindings != null) {
list.ensureCapacity(bindings.size());
for (String key : bindings.keySet()) {
list.add(key);
} }
} }
} }
} }
String[] res = new String[list.size()]; val arg = if (typeHint == null) "undefined" else typeHint.name
list.toArray(res); throw Context.reportRuntimeError("找不到对象的默认值 $arg")
return res;
} }
/** override fun hasInstance(instance: Scriptable): Boolean {
* We convert script values to the nearest Java value. var proto = instance.prototype
* We unwrap wrapped Java objects so that access from while (proto != null) {
* Bindings.get() would return "workable" value for Java. if (proto == this) {
* But, at the same time, we need to make few special cases return true
* and hence the following function is used.
*/
private Object jsToJava(Object jsObj) {
if (jsObj instanceof Wrapper) {
Wrapper njb = (Wrapper) jsObj;
/* importClass feature of ImporterTopLevel puts
* NativeJavaClass in global scope. If we unwrap
* it, importClass won't work.
*/
if (njb instanceof NativeJavaClass) {
return njb;
} }
proto = proto.prototype
}
return false
}
/* script may use Java primitive wrapper type objects private val allKeys: Array<String>
* (such as java.lang.Integer, java.lang.Boolean etc) get() {
* explicitly. If we unwrap, then these script objects val list = ArrayList<String>()
* will become script primitive types. For example, synchronized(context) {
* val var3: Iterator<*> = context.scopes.iterator()
* var x = new java.lang.Double(3.0); print(typeof x); while (var3.hasNext()) {
* val scope = var3.next() as Int
* will print 'number'. We don't want that to happen. val bindings = context.getBindings(scope)
*/ if (bindings != null) {
Object obj = njb.unwrap(); list.ensureCapacity(bindings.size)
if (obj instanceof Number || obj instanceof String || val var6: Iterator<*> = bindings.keys.iterator()
obj instanceof Boolean || obj instanceof Character) { while (var6.hasNext()) {
// special type wrapped -- we just leave it as is. val key = var6.next() as String
return njb; list.add(key)
} else { }
// return unwrapped object for any other object. }
return obj; }
} }
} else { // not-a-Java-wrapper return list.toTypedArray()
return jsObj; }
private fun jsToJava(jsObj: Any): Any {
return if (jsObj is Wrapper) {
if (jsObj is NativeJavaClass) {
jsObj
} else {
val obj = jsObj.unwrap()
if (obj !is Number && obj !is String && obj !is Boolean && obj !is Char) obj else jsObj
}
} else {
jsObj
} }
} }
} }

View File

@ -22,97 +22,75 @@
* or visit www.oracle.com if you need additional information or have any * or visit www.oracle.com if you need additional information or have any
* questions. * questions.
*/ */
package com.script.rhino
package com.script.rhino; import com.script.Invocable
import com.script.ScriptException
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.lang.reflect.Proxy
import java.security.AccessControlContext
import java.security.AccessController
import java.security.PrivilegedExceptionAction
import com.script.Invocable; /**
import com.script.ScriptException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
/*
* java.lang.reflect.Proxy based interface implementor. This is meant * java.lang.reflect.Proxy based interface implementor. This is meant
* to be used to implement Invocable.getInterface. * to be used to implement Invocable.getInterface.
* *
* @author Mike Grogan * @author Mike Grogan
* @since 1.6 * @since 1.6
*/ */
public class InterfaceImplementor { open class InterfaceImplementor(private val engine: Invocable) {
@Throws(ScriptException::class)
private Invocable engine; fun <T> getInterface(thiz: Any?, iface: Class<T>?): T? {
return if (iface != null && iface.isInterface) {
/** Creates a new instance of Invocable */ if (!isImplemented(thiz, iface)) {
public InterfaceImplementor(Invocable engine) { null
this.engine = engine; } else {
} val accCtxt = AccessController.getContext()
iface.cast(
private final class InterfaceImplementorInvocationHandler Proxy.newProxyInstance(
implements InvocationHandler { iface.classLoader,
private Object thiz; arrayOf<Class<*>>(iface),
private AccessControlContext accCtxt; InterfaceImplementorInvocationHandler(thiz, accCtxt)
)
public InterfaceImplementorInvocationHandler(Object thiz, )
AccessControlContext accCtxt) { }
this.thiz = thiz; } else {
this.accCtxt = accCtxt; throw IllegalArgumentException("interface Class expected")
}
public Object invoke(Object proxy , Method method, Object[] args)
throws java.lang.Throwable {
// give chance to convert input args
args = convertArguments(method, args);
Object result;
final Method m = method;
final Object[] a = args;
result = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
public Object run() throws Exception {
if (thiz == null) {
return engine.invokeFunction(m.getName(), a);
} else {
return engine.invokeMethod(thiz, m.getName(), a);
}
}
}, accCtxt);
// give chance to convert the method result
return convertResult(method, result);
} }
} }
public <T> T getInterface(Object thiz, Class<T> iface) protected open fun isImplemented(thiz: Any?, iface: Class<*>): Boolean {
throws ScriptException { return true
if (iface == null || !iface.isInterface()) { }
throw new IllegalArgumentException("interface Class expected");
@Throws(ScriptException::class)
protected open fun convertResult(method: Method?, res: Any): Any {
return res
}
@Throws(ScriptException::class)
protected fun convertArguments(method: Method?, args: Array<Any>): Array<Any> {
return args
}
private inner class InterfaceImplementorInvocationHandler(
private val thiz: Any?,
private val accCtxt: AccessControlContext
) : InvocationHandler {
@Throws(Throwable::class)
override fun invoke(proxy: Any, method: Method, args: Array<Any>): Any {
val finalArgs = convertArguments(method, args)
val result = AccessController.doPrivileged(PrivilegedExceptionAction {
if (thiz == null) engine.invokeFunction(
method.name,
*finalArgs
) else engine.invokeMethod(
thiz, method.name, *finalArgs
)
} as PrivilegedExceptionAction<Any>, accCtxt)
return convertResult(method, result)
} }
if (! isImplemented(thiz, iface)) {
return null;
}
AccessControlContext accCtxt = AccessController.getContext();
return iface.cast(Proxy.newProxyInstance(iface.getClassLoader(),
new Class[]{iface},
new InterfaceImplementorInvocationHandler(thiz, accCtxt)));
}
protected boolean isImplemented(Object thiz, Class<?> iface) {
return true;
}
// called to convert method result after invoke
protected Object convertResult(Method method, Object res)
throws ScriptException {
// default is identity conversion
return res;
}
// called to convert method arguments before invoke
protected Object[] convertArguments(Method method, Object[] args)
throws ScriptException {
// default is identity conversion
return args;
} }
} }

View File

@ -22,16 +22,10 @@
* or visit www.oracle.com if you need additional information or have any * or visit www.oracle.com if you need additional information or have any
* questions. * questions.
*/ */
package com.script.rhino
package com.script.rhino; import org.mozilla.javascript.*
import org.mozilla.javascript.Function
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.NativeArray;
import org.mozilla.javascript.NativeJavaArray;
import org.mozilla.javascript.RhinoException;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
/** /**
* JSAdapter is java.lang.reflect.Proxy equivalent for JavaScript. JSAdapter * JSAdapter is java.lang.reflect.Proxy equivalent for JavaScript. JSAdapter
@ -40,21 +34,21 @@ import org.mozilla.javascript.ScriptableObject;
* *
* Example: * Example:
* *
* var y = { * var y = {
* __get__ : function (name) { ... } * __get__ : function (name) { ... }
* __has__ : function (name) { ... } * __has__ : function (name) { ... }
* __put__ : function (name, value) {...} * __put__ : function (name, value) {...}
* __delete__ : function (name) { ... } * __delete__ : function (name) { ... }
* __getIds__ : function () { ... } * __getIds__ : function () { ... }
* }; * };
* *
* var x = new JSAdapter(y); * var x = new JSAdapter(y);
* *
* x.i; // calls y.__get__ * x.i; // calls y.__get__
* i in x; // calls y.__has__ * i in x; // calls y.__has__
* x.p = 10; // calls y.__put__ * x.p = 10; // calls y.__put__
* delete x.p; // calls y.__delete__ * delete x.p; // calls y.__delete__
* for (i in x) { print(i); } // calls y.__getIds__ * for (i in x) { print(i); } // calls y.__getIds__
* *
* If a special JavaScript method is not found in the adaptee, then JSAdapter * If a special JavaScript method is not found in the adaptee, then JSAdapter
* forwards the property access to the adaptee itself. * forwards the property access to the adaptee itself.
@ -74,270 +68,239 @@ import org.mozilla.javascript.ScriptableObject;
* @author A. Sundararajan * @author A. Sundararajan
* @since 1.6 * @since 1.6
*/ */
public final class JSAdapter implements Scriptable, Function { class JSAdapter private constructor(val adaptee: Scriptable) : Scriptable, Function {
private JSAdapter(Scriptable obj) { private var prototype: Scriptable? = null
setAdaptee(obj); private var parent: Scriptable? = null
private var isPrototype = false
override fun getClassName(): String {
return "JSAdapter"
} }
// initializer to setup JSAdapter prototype in the given scope override fun get(name: String, start: Scriptable): Any {
public static void init(Context cx, Scriptable scope, boolean sealed) val func = getAdapteeFunction(GET_PROP)
throws RhinoException { return if (func != null) {
JSAdapter obj = new JSAdapter(cx.newObject(scope)); this.call(func, arrayOf(name))
obj.setParentScope(scope);
obj.setPrototype(getFunctionPrototype(scope));
obj.isPrototype = true;
ScriptableObject.defineProperty(scope, "JSAdapter", obj,
ScriptableObject.DONTENUM);
}
public String getClassName() {
return "JSAdapter";
}
public Object get(String name, Scriptable start) {
Function func = getAdapteeFunction(GET_PROP);
if (func != null) {
return call(func, new Object[] { name });
} else { } else {
start = getAdaptee(); adaptee[name, adaptee]
return start.get(name, start);
} }
} }
public Object get(int index, Scriptable start) { override fun get(index: Int, start: Scriptable): Any {
Function func = getAdapteeFunction(GET_PROP); val func = getAdapteeFunction(GET_PROP)
if (func != null) { return if (func != null) {
return call(func, new Object[] {index}); this.call(func, arrayOf(index))
} else { } else {
start = getAdaptee(); adaptee[index, adaptee]
return start.get(index, start);
} }
} }
public boolean has(String name, Scriptable start) { override fun has(name: String, start: Scriptable): Boolean {
Function func = getAdapteeFunction(HAS_PROP); val func = getAdapteeFunction(HAS_PROP)
if (func != null) { return if (func != null) {
Object res = call(func, new Object[] { name }); val res = this.call(func, arrayOf(name))
return Context.toBoolean(res); Context.toBoolean(res)
} else { } else {
start = getAdaptee(); adaptee.has(name, adaptee)
return start.has(name, start);
} }
} }
public boolean has(int index, Scriptable start) { override fun has(index: Int, start: Scriptable): Boolean {
Function func = getAdapteeFunction(HAS_PROP); val func = getAdapteeFunction(HAS_PROP)
if (func != null) { return if (func != null) {
Object res = call(func, new Object[] {index}); val res = this.call(func, arrayOf(index))
return Context.toBoolean(res); Context.toBoolean(res)
} else { } else {
start = getAdaptee(); adaptee.has(index, adaptee)
return start.has(index, start);
} }
} }
public void put(String name, Scriptable start, Object value) { override fun put(name: String, start: Scriptable, value: Any) {
if (start == this) { if (start === this) {
Function func = getAdapteeFunction(PUT_PROP); val func = getAdapteeFunction(PUT_PROP)
if (func != null) { if (func != null) {
call(func, new Object[] { name, value }); this.call(func, arrayOf(name, value))
} else { } else {
start = getAdaptee(); adaptee.put(name, adaptee, value)
start.put(name, start, value);
} }
} else { } else {
start.put(name, start, value); start.put(name, start, value)
} }
} }
public void put(int index, Scriptable start, Object value) { override fun put(index: Int, start: Scriptable, value: Any) {
if (start == this) { if (start === this) {
Function func = getAdapteeFunction(PUT_PROP); val func = getAdapteeFunction(PUT_PROP)
if( func != null) { if (func != null) {
call(func, new Object[] {index, value }); this.call(func, arrayOf(index, value))
} else { } else {
start = getAdaptee(); adaptee.put(index, adaptee, value)
start.put(index, start, value);
} }
} else { } else {
start.put(index, start, value); start.put(index, start, value)
} }
} }
public void delete(String name) { override fun delete(name: String) {
Function func = getAdapteeFunction(DEL_PROP); val func = getAdapteeFunction(DEL_PROP)
if (func != null) { if (func != null) {
call(func, new Object[] { name }); this.call(func, arrayOf(name))
} else { } else {
getAdaptee().delete(name); adaptee.delete(name)
} }
} }
public void delete(int index) { override fun delete(index: Int) {
Function func = getAdapteeFunction(DEL_PROP); val func = getAdapteeFunction(DEL_PROP)
if (func != null) { if (func != null) {
call(func, new Object[] {index}); this.call(func, arrayOf(index))
} else { } else {
getAdaptee().delete(index); adaptee.delete(index)
} }
} }
public Scriptable getPrototype() { override fun getPrototype(): Scriptable? {
return prototype; return prototype
} }
public void setPrototype(Scriptable prototype) { override fun setPrototype(prototype: Scriptable?) {
this.prototype = prototype; this.prototype = prototype
} }
public Scriptable getParentScope() { override fun getParentScope(): Scriptable? {
return parent; return parent
} }
public void setParentScope(Scriptable parent) { override fun setParentScope(parent: Scriptable?) {
this.parent = parent; this.parent = parent
} }
public Object[] getIds() { override fun getIds(): Array<Any?> {
Function func = getAdapteeFunction(GET_PROPIDS); val func = getAdapteeFunction(GET_PROPIDS)
if (func != null) { return if (func == null) {
Object val = call(func, new Object[0]); adaptee.ids
// in most cases, adaptee would return native JS array } else {
if (val instanceof NativeArray) { val val1 = this.call(func, arrayOfNulls(0))
NativeArray array = (NativeArray) val; val res: Array<Any?>
Object[] res = new Object[(int)array.getLength()]; when (val1) {
for (int index = 0; index < res.length; index++) { is NativeArray -> {
res[index] = mapToId(array.get(index, array)); res = arrayOfNulls(val1.length.toInt())
} for (index in res.indices) {
return res; res[index] = mapToId(val1[index, val1])
} else if (val instanceof NativeJavaArray) {
// may be attempt wrapped Java array
Object tmp = ((NativeJavaArray)val).unwrap();
Object[] res;
if (tmp.getClass() == Object[].class) {
Object[] array = (Object[]) tmp;
res = new Object[array.length];
for (int index = 0; index < array.length; index++) {
res[index] = mapToId(array[index]);
} }
} else { res
// just return an empty array }
res = Context.emptyArgs; !is NativeJavaArray -> {
Context.emptyArgs
}
else -> {
val tmp = val1.unwrap()
if (tmp.javaClass == Array<Any>::class.java) {
val array = tmp as Array<*>
res = arrayOfNulls(array.size)
for (index in array.indices) {
res[index] = mapToId(array[index])
}
} else {
res = Context.emptyArgs
}
res
} }
return res;
} else {
// some other return type, just return empty array
return Context.emptyArgs;
} }
} else {
return getAdaptee().getIds();
} }
} }
public boolean hasInstance(Scriptable scriptable) { override fun hasInstance(scriptable: Scriptable): Boolean {
if (scriptable instanceof JSAdapter) { return if (scriptable is JSAdapter) {
return true; true
} else { } else {
Scriptable proto = scriptable.getPrototype(); var proto = scriptable.prototype
while (proto != null) { while (proto != null) {
if (proto.equals(this)) return true; if (proto == this) {
proto = proto.getPrototype(); return true
}
proto = proto.prototype
} }
return false; false
} }
} }
public Object getDefaultValue(Class hint) { override fun getDefaultValue(hint: Class<*>?): Any {
return getAdaptee().getDefaultValue(hint); return adaptee.getDefaultValue(hint)
} }
public Object call(Context cx, Scriptable scope, Scriptable thisObj, @Throws(RhinoException::class)
Object[] args) override fun call(cx: Context, scope: Scriptable, thisObj: Scriptable, args: Array<Any>): Any {
throws RhinoException { return if (isPrototype) {
if (isPrototype) { construct(cx, scope, args)
return construct(cx, scope, args);
} else { } else {
Scriptable tmp = getAdaptee(); val tmp = adaptee
if (tmp instanceof Function) { if (tmp is Function) {
return ((Function)tmp).call(cx, scope, tmp, args); tmp.call(cx, scope, tmp, args)
} else { } else {
throw Context.reportRuntimeError("TypeError: not a function"); throw Context.reportRuntimeError("TypeError: not a function")
} }
} }
} }
public Scriptable construct(Context cx, Scriptable scope, Object[] args) @Throws(RhinoException::class)
throws RhinoException { override fun construct(cx: Context, scope: Scriptable, args: Array<Any>): Scriptable {
if (isPrototype) { val tmp: Scriptable?
Scriptable topLevel = ScriptableObject.getTopLevelScope(scope); return if (isPrototype) {
JSAdapter newObj; tmp = ScriptableObject.getTopLevelScope(scope)
if (args.length > 0) { if (args.size > 0) {
newObj = new JSAdapter(Context.toObject(args[0], topLevel)); JSAdapter(Context.toObject(args[0], tmp))
} else { } else {
throw Context.reportRuntimeError("JSAdapter requires adaptee"); throw Context.reportRuntimeError("JSAdapter requires adaptee")
} }
return newObj;
} else { } else {
Scriptable tmp = getAdaptee(); tmp = adaptee
if (tmp instanceof Function) { if (tmp is Function) {
return ((Function)tmp).construct(cx, scope, args); tmp.construct(cx, scope, args)
} else { } else {
throw Context.reportRuntimeError("TypeError: not a constructor"); throw Context.reportRuntimeError("TypeError: not a constructor")
} }
} }
} }
public Scriptable getAdaptee() { private fun mapToId(tmp: Any?): Any {
return adaptee; return if (tmp is Double) tmp.toInt() else Context.toString(tmp)
} }
public void setAdaptee(Scriptable adaptee) { private fun getAdapteeFunction(name: String): Function? {
if (adaptee == null) { val o = ScriptableObject.getProperty(adaptee, name)
throw new NullPointerException("adaptee can not be null"); return if (o is Function) o else null
}
this.adaptee = adaptee;
} }
//-- internals only below this point private fun call(func: Function, args: Array<Any?>): Any {
val cx = Context.getCurrentContext()
// map a property id. Property id can only be an Integer or String val thisObj = adaptee
private Object mapToId(Object tmp) { val scope = func.parentScope
if (tmp instanceof Double) { return try {
return ((Double) tmp).intValue(); func.call(cx, scope, thisObj, args)
} else { } catch (var7: RhinoException) {
return Context.toString(tmp); throw Context.reportRuntimeError(var7.message)
} }
} }
private static Scriptable getFunctionPrototype(Scriptable scope) { companion object {
return ScriptableObject.getFunctionPrototype(scope); private const val GET_PROP = "__get__"
} private const val HAS_PROP = "__has__"
private const val PUT_PROP = "__put__"
private const val DEL_PROP = "__delete__"
private const val GET_PROPIDS = "__getIds__"
private Function getAdapteeFunction(String name) { @Throws(RhinoException::class)
Object o = ScriptableObject.getProperty(getAdaptee(), name); fun init(cx: Context, scope: Scriptable, sealed: Boolean) {
return (o instanceof Function)? (Function)o : null; val obj = JSAdapter(cx.newObject(scope))
} obj.parentScope = scope
obj.setPrototype(getFunctionPrototype(scope))
obj.isPrototype = true
ScriptableObject.defineProperty(scope, "JSAdapter", obj, 2)
}
private Object call(Function func, Object[] args) { private fun getFunctionPrototype(scope: Scriptable): Scriptable {
Context cx = Context.getCurrentContext(); return ScriptableObject.getFunctionPrototype(scope)
Scriptable thisObj = getAdaptee();
Scriptable scope = func.getParentScope();
try {
return func.call(cx, scope, thisObj, args);
} catch (RhinoException re) {
throw Context.reportRuntimeError(re.getMessage());
} }
} }
}
private Scriptable prototype;
private Scriptable parent;
private Scriptable adaptee;
private boolean isPrototype;
// names of adaptee JavaScript functions
private static final String GET_PROP = "__get__";
private static final String HAS_PROP = "__has__";
private static final String PUT_PROP = "__put__";
private static final String DEL_PROP = "__delete__";
private static final String GET_PROPIDS = "__getIds__";
}

View File

@ -22,31 +22,26 @@
* or visit www.oracle.com if you need additional information or have any * or visit www.oracle.com if you need additional information or have any
* questions. * questions.
*/ */
package com.script.rhino
package com.script.rhino; import com.script.Invocable
import org.mozilla.javascript.*
import com.script.Invocable; import org.mozilla.javascript.Function
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.RhinoException;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Wrapper;
/** /**
* This class implements Rhino-like JavaAdapter to help implement a Java * This class implements Rhino-like JavaAdapter to help implement a Java
* interface in JavaScript. We support this using Invocable.getInterface. * interface in JavaScript. We support this using Invocable.getInterface.
* Using this JavaAdapter, script author could write: * Using this JavaAdapter, script author could write:
* *
* var r = new java.lang.Runnable() {
* run: function() { script... }
* };
* *
* r.run(); * var r = new java.lang.Runnable() {
* new java.lang.Thread(r).start(); * run: function() { script... }
* };
*
*
* r.run();
* new java.lang.Thread(r).start();
*
* *
* Note that Rhino's JavaAdapter support allows extending a Java class and/or * Note that Rhino's JavaAdapter support allows extending a Java class and/or
* implementing one or more interfaces. This JavaAdapter implementation does * implementing one or more interfaces. This JavaAdapter implementation does
@ -55,60 +50,54 @@ import org.mozilla.javascript.Wrapper;
* @author A. Sundararajan * @author A. Sundararajan
* @since 1.6 * @since 1.6
*/ */
final class JavaAdapter extends ScriptableObject implements Function { internal class JavaAdapter private constructor(private val engine: Invocable) : ScriptableObject(),
private JavaAdapter(Invocable engine) { Function {
this.engine = engine; override fun getClassName(): String {
return "JavaAdapter"
} }
static void init(Context cx, Scriptable scope, boolean sealed) @Throws(RhinoException::class)
throws RhinoException { override fun call(cx: Context, scope: Scriptable, thisObj: Scriptable, args: Array<Any>): Any {
RhinoTopLevel topLevel = (RhinoTopLevel) scope; return construct(cx, scope, args)
Invocable engine = topLevel.getScriptEngine();
JavaAdapter obj = new JavaAdapter(engine);
obj.setParentScope(scope);
obj.setPrototype(getFunctionPrototype(scope));
/*
* Note that we can't use defineProperty. A property of this
* name is already defined in Context.initStandardObjects. We
* simply overwrite the property value!
*/
ScriptableObject.putProperty(topLevel, "JavaAdapter", obj);
} }
public String getClassName() { @Throws(RhinoException::class)
return "JavaAdapter"; override fun construct(cx: Context, scope: Scriptable, args: Array<Any>): Scriptable {
} return if (args.size == 2) {
var clazz: Class<*>? = null
public Object call(Context cx, Scriptable scope, Scriptable thisObj, val obj1 = args[0]
Object[] args) throws RhinoException { if (obj1 is Wrapper) {
return construct(cx, scope, args); val o = obj1.unwrap()
} if (o is Class<*> && o.isInterface) {
clazz = o
public Scriptable construct(Context cx, Scriptable scope, Object[] args)
throws RhinoException {
if (args.length == 2) {
Class<?> clazz = null;
Object obj1 = args[0];
if (obj1 instanceof Wrapper) {
Object o = ((Wrapper)obj1).unwrap();
if (o instanceof Class && ((Class)o).isInterface()) {
clazz = (Class) o;
}
} else if (obj1 instanceof Class) {
if (((Class)obj1).isInterface()) {
clazz = (Class) obj1;
} }
} else if (obj1 is Class<*> && obj1.isInterface) {
clazz = obj1
} }
if (clazz == null) { if (clazz == null) {
throw Context.reportRuntimeError("JavaAdapter: first arg should be interface Class"); throw Context.reportRuntimeError("JavaAdapter: first arg should be interface Class")
} else {
val topLevel = getTopLevelScope(scope)
Context.toObject(
engine.getInterface(args[1], clazz),
topLevel
)
} }
Scriptable topLevel = ScriptableObject.getTopLevelScope(scope);
return cx.toObject(engine.getInterface(args[1], clazz), topLevel);
} else { } else {
throw Context.reportRuntimeError("JavaAdapter requires two arguments"); throw Context.reportRuntimeError("JavaAdapter requires two arguments")
} }
} }
private Invocable engine; companion object {
} @JvmStatic
@Throws(RhinoException::class)
fun init(cx: Context?, scope: Scriptable, sealed: Boolean) {
val topLevel = scope as RhinoTopLevel
val engine: Invocable = topLevel.scriptEngine
val obj = JavaAdapter(engine)
obj.parentScope = scope
obj.prototype = getFunctionPrototype(scope)
putProperty(topLevel, "JavaAdapter", obj)
}
}
}

View File

@ -22,13 +22,9 @@
* or visit www.oracle.com if you need additional information or have any * or visit www.oracle.com if you need additional information or have any
* questions. * questions.
*/ */
package com.script.rhino
package com.script.rhino; import org.mozilla.javascript.ClassShutter
import org.mozilla.javascript.ClassShutter;
import java.util.HashMap;
import java.util.Map;
/** /**
* This class prevents script access to certain sensitive classes. * This class prevents script access to certain sensitive classes.
@ -38,43 +34,38 @@ import java.util.Map;
* @author A. Sundararajan * @author A. Sundararajan
* @since 1.6 * @since 1.6
*/ */
final class RhinoClassShutter implements ClassShutter { internal class RhinoClassShutter private constructor() : ClassShutter {
private static Map<String, Boolean> protectedClasses; override fun visibleToScripts(fullClassName: String): Boolean {
private static RhinoClassShutter theInstance; val sm = System.getSecurityManager()
private RhinoClassShutter() {
}
static synchronized ClassShutter getInstance() {
if (theInstance == null) {
theInstance = new RhinoClassShutter();
protectedClasses = new HashMap<String, Boolean>();
// For now, we just have AccessController. Allowing scripts
// to this class will allow it to execute doPrivileged in
// bootstrap context. We can add more classes for other reasons.
protectedClasses.put("java.lang.Class", Boolean.TRUE);
protectedClasses.put("java.lang.Runtime", Boolean.TRUE);
protectedClasses.put("java.io.File", Boolean.TRUE);
protectedClasses.put("java.security.AccessController", Boolean.TRUE);
}
return theInstance;
}
public boolean visibleToScripts(String fullClassName) {
// first do the security check.
SecurityManager sm = System.getSecurityManager();
if (sm != null) { if (sm != null) {
int i = fullClassName.lastIndexOf("."); val i = fullClassName.lastIndexOf(".")
if (i != -1) { if (i != -1) {
try { try {
sm.checkPackageAccess(fullClassName.substring(0, i)); sm.checkPackageAccess(fullClassName.substring(0, i))
} catch (SecurityException se) { } catch (var5: SecurityException) {
return false; return false
} }
} }
} }
// now, check is it a protected class. return protectedClasses[fullClassName] == null
return protectedClasses.get(fullClassName) == null;
} }
}
companion object {
@JvmStatic
val protectedClasses by lazy {
val protectedClasses = HashMap<Any, Any>()
protectedClasses["java.lang.Runtime"] = java.lang.Boolean.TRUE
protectedClasses["java.io.File"] = java.lang.Boolean.TRUE
protectedClasses["java.security.AccessController"] = java.lang.Boolean.TRUE
protectedClasses
}
@JvmStatic
val instance: ClassShutter by lazy {
val theInstance = RhinoClassShutter()
theInstance
}
}
}

View File

@ -22,20 +22,13 @@
* or visit www.oracle.com if you need additional information or have any * or visit www.oracle.com if you need additional information or have any
* questions. * questions.
*/ */
package com.script.rhino
package com.script.rhino; import com.script.CompiledScript
import com.script.ScriptContext
import com.script.CompiledScript; import com.script.ScriptEngine
import com.script.ScriptContext; import com.script.ScriptException
import com.script.ScriptEngine; import org.mozilla.javascript.*
import com.script.ScriptException;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.JavaScriptException;
import org.mozilla.javascript.RhinoException;
import org.mozilla.javascript.Script;
import org.mozilla.javascript.Scriptable;
/** /**
* Represents compiled JavaScript code. * Represents compiled JavaScript code.
@ -43,46 +36,36 @@ import org.mozilla.javascript.Scriptable;
* @author Mike Grogan * @author Mike Grogan
* @since 1.6 * @since 1.6
*/ */
final class RhinoCompiledScript extends CompiledScript { internal class RhinoCompiledScript(
private val engine: RhinoScriptEngine,
private val script: Script
) : CompiledScript() {
private com.script.rhino.RhinoScriptEngine engine; @Throws(ScriptException::class)
private Script script; override fun eval(context: ScriptContext): Any? {
val cx = Context.enter()
val result: Any?
RhinoCompiledScript(com.script.rhino.RhinoScriptEngine engine, Script script) {
this.engine = engine;
this.script = script;
}
public Object eval(ScriptContext context) throws ScriptException {
Object result = null;
Context cx = RhinoScriptEngine.enterContext();
try { try {
val scope = engine.getRuntimeScope(context)
Scriptable scope = engine.getRuntimeScope(context); val ret = script.exec(cx, scope)
Object ret = script.exec(cx, scope); result = engine.unwrapReturnValue(ret)
result = engine.unwrapReturnValue(ret); } catch (re: RhinoException) {
} catch (RhinoException re) { val line = if (re.lineNumber() == 0) -1 else re.lineNumber()
int line = (line = re.lineNumber()) == 0 ? -1 : line; val msg: String = if (re is JavaScriptException) {
String msg; re.value.toString()
if (re instanceof JavaScriptException) {
msg = String.valueOf(((JavaScriptException)re).getValue());
} else { } else {
msg = re.toString(); re.toString()
} }
ScriptException se = new ScriptException(msg, re.sourceName(), line); val se = ScriptException(msg, re.sourceName(), line)
se.initCause(re); se.initCause(re)
throw se; throw se
} finally { } finally {
Context.exit(); Context.exit()
} }
return result
return result;
} }
public ScriptEngine getEngine() { override fun getEngine(): ScriptEngine {
return engine; return engine
} }
}
}

View File

@ -22,440 +22,358 @@
* or visit www.oracle.com if you need additional information or have any * or visit www.oracle.com if you need additional information or have any
* questions. * questions.
*/ */
package com.script.rhino
package com.script.rhino; import com.script.*
import com.script.rhino.RhinoClassShutter.Companion.instance
import org.intellij.lang.annotations.Language
import com.script.AbstractScriptEngine; import org.mozilla.javascript.*
import com.script.Bindings; import org.mozilla.javascript.Function
import com.script.Compilable; import java.io.IOException
import com.script.CompiledScript; import java.io.Reader
import com.script.Invocable; import java.io.StringReader
import com.script.ScriptContext; import java.lang.reflect.Method
import com.script.ScriptEngine; import java.security.*
import com.script.ScriptException;
import com.script.SimpleBindings;
import org.mozilla.javascript.Callable;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.JavaScriptException;
import org.mozilla.javascript.RhinoException;
import org.mozilla.javascript.Script;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Undefined;
import org.mozilla.javascript.Wrapper;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.Method;
import java.security.AccessControlContext;
import java.security.AccessControlException;
import java.security.AccessController;
import java.security.AllPermission;
import java.security.PrivilegedAction;
import java.util.HashMap;
import java.util.Map;
/** /**
* Implementation of <code>ScriptEngine</code> using the Mozilla Rhino * Implementation of `ScriptEngine` using the Mozilla Rhino
* interpreter. * interpreter.
* *
* @author Mike Grogan * @author Mike Grogan
* @author A. Sundararajan * @author A. Sundararajan
* @since 1.6 * @since 1.6
*/ */
public final class RhinoScriptEngine extends AbstractScriptEngine class RhinoScriptEngine : AbstractScriptEngine(), Invocable, Compilable {
implements Invocable, Compilable { var accessContext: AccessControlContext? = null
private var topLevel: RhinoTopLevel? = null
private val indexedProps: MutableMap<Any, Any>
private val implementor: InterfaceImplementor
private static final boolean DEBUG = false; @Throws(ScriptException::class)
override fun eval(reader: Reader, ctxt: ScriptContext): Any {
private AccessControlContext accCtxt; val cx = Context.enter()
val ret: Any
/* Scope where standard JavaScript objects and our
* extensions to it are stored. Note that these are not
* user defined engine level global variables. These are
* variables have to be there on all compliant ECMAScript
* scopes. We put these standard objects in this top level.
*/
private com.script.rhino.RhinoTopLevel topLevel;
/* map used to store indexed properties in engine scope
* refer to comment on 'indexedProps' in ExternalScriptable.java.
*/
private Map<Object, Object> indexedProps;
private InterfaceImplementor implementor;
private static final int languageVersion = getLanguageVersion();
private static final int optimizationLevel = getOptimizationLevel();
static {
ContextFactory.initGlobal(new ContextFactory() {
/**
* Create new Context instance to be associated with the current thread.
*/
@Override
protected Context makeContext() {
Context cx = super.makeContext();
cx.setLanguageVersion(languageVersion);
cx.setOptimizationLevel(optimizationLevel);
cx.setClassShutter(com.script.rhino.RhinoClassShutter.getInstance());
cx.setWrapFactory(com.script.rhino.RhinoWrapFactory.getInstance());
return cx;
}
/**
* Execute top call to script or function. When the runtime is about to
* execute a script or function that will create the first stack frame
* with scriptable code, it calls this method to perform the real call.
* In this way execution of any script happens inside this function.
*/
@Override
protected Object doTopCall(final Callable callable,
final Context cx, final Scriptable scope,
final Scriptable thisObj, final Object[] args) {
AccessControlContext accCtxt = null;
Scriptable global = ScriptableObject.getTopLevelScope(scope);
Scriptable globalProto = global.getPrototype();
if (globalProto instanceof com.script.rhino.RhinoTopLevel) {
accCtxt = ((com.script.rhino.RhinoTopLevel) globalProto).getAccessContext();
}
if (accCtxt != null) {
return AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
return superDoTopCall(callable, cx, scope, thisObj, args);
}
}, accCtxt);
} else {
return superDoTopCall(callable, cx, scope, thisObj, args);
}
}
private Object superDoTopCall(Callable callable,
Context cx, Scriptable scope,
Scriptable thisObj, Object[] args) {
return super.doTopCall(callable, cx, scope, thisObj, args);
}
});
}
private static final String RHINO_JS_VERSION = "rhino.js.version";
private static int getLanguageVersion() {
return Context.VERSION_1_8;
}
private static final String RHINO_OPT_LEVEL = "rhino.opt.level";
private static int getOptimizationLevel() {
int optLevel = -1;
// disable optimizer under security manager, for now.
if (System.getSecurityManager() == null) {
optLevel = Integer.getInteger(RHINO_OPT_LEVEL, -1);
}
return optLevel;
}
/**
* Creates a new instance of RhinoScriptEngine
*/
public RhinoScriptEngine() {
if (System.getSecurityManager() != null) {
try {
AccessController.checkPermission(new AllPermission());
} catch (AccessControlException ace) {
accCtxt = AccessController.getContext();
}
}
Context cx = enterContext();
try { try {
topLevel = new RhinoTopLevel(cx, this); val scope = getRuntimeScope(ctxt)
} finally { var filename = this["javax.script.filename"] as? String
cx.exit(); filename = filename ?: "<Unknown source>"
} ret = cx.evaluateReader(scope, reader, filename, 1, null as Any?)
} catch (re: RhinoException) {
indexedProps = new HashMap<Object, Object>(); val line = if (re.lineNumber() == 0) -1 else re.lineNumber()
val msg: String = if (re is JavaScriptException) {
//construct object used to implement getInterface re.value.toString()
implementor = new InterfaceImplementor(this) {
protected boolean isImplemented(Object thiz, Class<?> iface) {
Context cx = enterContext();
try {
if (thiz != null && !(thiz instanceof Scriptable)) {
thiz = cx.toObject(thiz, topLevel);
}
Scriptable engineScope = getRuntimeScope(context);
Scriptable localScope = (thiz != null) ? (Scriptable) thiz :
engineScope;
for (Method method : iface.getMethods()) {
// ignore methods of java.lang.Object class
if (method.getDeclaringClass() == Object.class) {
continue;
}
Object obj = ScriptableObject.getProperty(localScope, method.getName());
if (!(obj instanceof Function)) {
return false;
}
}
return true;
} finally {
cx.exit();
}
}
protected Object convertResult(Method method, Object res)
throws ScriptException {
Class desiredType = method.getReturnType();
if (desiredType == Void.TYPE) {
return null;
} else {
return Context.jsToJava(res, desiredType);
}
}
};
}
public Object eval(Reader reader, ScriptContext ctxt)
throws ScriptException {
Object ret;
Context cx = enterContext();
try {
Scriptable scope = getRuntimeScope(ctxt);
String filename = (String) get(ScriptEngine.FILENAME);
filename = filename == null ? "<Unknown source>" : filename;
ret = cx.evaluateReader(scope, reader, filename, 1, null);
} catch (RhinoException re) {
if (DEBUG) re.printStackTrace();
int line = (line = re.lineNumber()) == 0 ? -1 : line;
String msg;
if (re instanceof JavaScriptException) {
msg = String.valueOf(((JavaScriptException) re).getValue());
} else { } else {
msg = re.toString(); re.toString()
} }
ScriptException se = new ScriptException(msg, re.sourceName(), line); val se = ScriptException(msg, re.sourceName(), line)
se.initCause(re); se.initCause(re)
throw se; throw se
} catch (IOException ee) { } catch (var14: IOException) {
throw new ScriptException(ee); throw ScriptException(var14)
} finally { } finally {
cx.exit(); Context.exit()
} }
return unwrapReturnValue(ret)!!
return unwrapReturnValue(ret);
} }
public Object eval(String script, ScriptContext ctxt) throws ScriptException { @Throws(ScriptException::class)
if (script == null) { override fun eval(script: String?, ctxt: ScriptContext): Any {
throw new NullPointerException("null script"); return if (script == null) {
throw NullPointerException("null script")
} else {
this.eval(StringReader(script) as Reader, ctxt)
} }
return eval(new StringReader(script), ctxt);
} }
public Bindings createBindings() { override fun createBindings(): Bindings {
return new SimpleBindings(); return SimpleBindings()
} }
//Invocable methods @Throws(ScriptException::class, NoSuchMethodException::class)
public Object invokeFunction(String name, Object... args) override fun invokeFunction(name: String, vararg args: Any): Any {
throws ScriptException, NoSuchMethodException { return this.invoke(null as Any?, name, *args)
return invoke(null, name, args);
} }
public Object invokeMethod(Object thiz, String name, Object... args) @Throws(ScriptException::class, NoSuchMethodException::class)
throws ScriptException, NoSuchMethodException { override fun invokeMethod(thiz: Any?, name: String, vararg args: Any): Any {
if (thiz == null) { return if (thiz == null) {
throw new IllegalArgumentException("script object can not be null"); throw IllegalArgumentException("脚本对象不能为空")
} else {
this.invoke(thiz, name, *args)
} }
return invoke(thiz, name, args);
} }
private Object invoke(Object thiz, String name, Object... args) @Suppress("UNCHECKED_CAST")
throws ScriptException, NoSuchMethodException { @Throws(ScriptException::class, NoSuchMethodException::class)
Context cx = enterContext(); private operator fun invoke(thiz: Any?, name: String?, vararg args: Any?): Any {
var thiz1 = thiz
val cx = Context.enter()
val var11: Any
try { try {
if (name == null) { if (name == null) {
throw new NullPointerException("method name is null"); throw NullPointerException("方法名为空")
} }
if (thiz1 != null && thiz1 !is Scriptable) {
if (thiz != null && !(thiz instanceof Scriptable)) { thiz1 = Context.toObject(thiz1, topLevel)
thiz = cx.toObject(thiz, topLevel);
} }
val engineScope = getRuntimeScope(context)
Scriptable engineScope = getRuntimeScope(context); val localScope = if (thiz1 != null) thiz1 as Scriptable else engineScope
Scriptable localScope = (thiz != null) ? (Scriptable) thiz : val obj = ScriptableObject.getProperty(localScope, name) as? Function
engineScope; ?: throw NoSuchMethodException("no such method: $name")
Object obj = ScriptableObject.getProperty(localScope, name); val func = obj
if (!(obj instanceof Function)) { var scope = func.parentScope
throw new NoSuchMethodException("no such method: " + name);
}
Function func = (Function) obj;
Scriptable scope = func.getParentScope();
if (scope == null) { if (scope == null) {
scope = engineScope; scope = engineScope
} }
Object result = func.call(cx, scope, localScope, val result = func.call(cx, scope, localScope, wrapArguments(args as? Array<Any?>))
wrapArguments(args)); var11 = unwrapReturnValue(result)!!
return unwrapReturnValue(result); } catch (re: RhinoException) {
} catch (RhinoException re) { val line = if (re.lineNumber() == 0) -1 else re.lineNumber()
if (DEBUG) re.printStackTrace(); val se = ScriptException(re.toString(), re.sourceName(), line)
int line = (line = re.lineNumber()) == 0 ? -1 : line; se.initCause(re)
ScriptException se = new ScriptException(re.toString(), re.sourceName(), line); throw se
se.initCause(re);
throw se;
} finally { } finally {
cx.exit(); Context.exit()
}
return var11
}
override fun <T> getInterface(clasz: Class<T>): T? {
return try {
implementor.getInterface(null as Any?, clasz)
} catch (var3: ScriptException) {
null
} }
} }
public <T> T getInterface(Class<T> clasz) { override fun <T> getInterface(thiz: Any?, clasz: Class<T>): T? {
return if (thiz == null) {
throw IllegalArgumentException("脚本对象不能为空")
} else {
try {
implementor.getInterface(thiz, clasz)
} catch (var4: ScriptException) {
null
}
}
}
fun getRuntimeScope(ctxt: ScriptContext?): Scriptable {
return if (ctxt == null) {
throw NullPointerException("脚本context为空")
} else {
val newScope: Scriptable = ExternalScriptable(ctxt, indexedProps)
newScope.prototype = topLevel
newScope.put("context", newScope, ctxt)
val cx = Context.enter()
try {
@Language("js")
val js = """
function print(str, newline) {
if (typeof(str) == 'undefined') {
str = 'undefined';
} else if (str == null) {
str = 'null';
}
var out = context.getWriter();
if (!(out instanceof java.io.PrintWriter))
out = new java.io.PrintWriter(out);
out.print(String(str));
if (newline) out.print('\\n');
out.flush();
}
function println(str) {
print(str, true);
}
""".trimIndent()
cx.evaluateString(
newScope,
js,
"print",
1,
null as Any?
)
} finally {
Context.exit()
}
newScope
}
}
@Throws(ScriptException::class)
override fun compile(script: String): CompiledScript {
return this.compile(StringReader(script) as Reader)
}
@Throws(ScriptException::class)
override fun compile(script: Reader): CompiledScript {
val cx = Context.enter()
val ret: RhinoCompiledScript
try { try {
return implementor.getInterface(null, clasz); var fileName = this["javax.script.filename"] as? String
} catch (ScriptException e) {
return null;
}
}
public <T> T getInterface(Object thiz, Class<T> clasz) {
if (thiz == null) {
throw new IllegalArgumentException("script object can not be null");
}
try {
return implementor.getInterface(thiz, clasz);
} catch (ScriptException e) {
return null;
}
}
private static final String printSource =
"function print(str, newline) { \n" +
" if (typeof(str) == 'undefined') { \n" +
" str = 'undefined'; \n" +
" } else if (str == null) { \n" +
" str = 'null'; \n" +
" } \n" +
" var out = context.getWriter(); \n" +
" if (!(out instanceof java.io.PrintWriter))\n" +
" out = new java.io.PrintWriter(out); \n" +
" out.print(String(str)); \n" +
" if (newline) out.print('\\n'); \n" +
" out.flush(); \n" +
"}\n" +
"function println(str) { \n" +
" print(str, true); \n" +
"}";
Scriptable getRuntimeScope(ScriptContext ctxt) {
if (ctxt == null) {
throw new NullPointerException("null script context");
}
// we create a scope for the given ScriptContext
Scriptable newScope = new com.script.rhino.ExternalScriptable(ctxt, indexedProps);
// Set the prototype of newScope to be 'topLevel' so that
// JavaScript standard objects are visible from the scope.
newScope.setPrototype(topLevel);
// define "context" variable in the new scope
newScope.put("context", newScope, ctxt);
// define "print", "println" functions in the new scope
Context cx = enterContext();
try {
cx.evaluateString(newScope, printSource, "print", 1, null);
} finally {
cx.exit();
}
return newScope;
}
//Compilable methods
public CompiledScript compile(String script) throws ScriptException {
return compile(new StringReader(script));
}
public CompiledScript compile(java.io.Reader script) throws ScriptException {
CompiledScript ret = null;
Context cx = enterContext();
try {
String fileName = (String) get(ScriptEngine.FILENAME);
if (fileName == null) { if (fileName == null) {
fileName = "<Unknown Source>"; fileName = "<Unknown Source>"
}
val scr = cx.compileReader(script, fileName, 1, null as Any?)
ret = RhinoCompiledScript(this, scr)
} catch (var9: Exception) {
throw ScriptException(var9)
} finally {
Context.exit()
}
return ret
}
fun wrapArguments(args: Array<Any?>?): Array<Any?> {
return if (args == null) {
Context.emptyArgs
} else {
val res = arrayOfNulls<Any>(args.size)
for (i in res.indices) {
res[i] = Context.javaToJS(args[i], topLevel)
}
res
}
}
fun unwrapReturnValue(result: Any?): Any? {
var result1 = result
if (result1 is Wrapper) {
result1 = result1.unwrap()
}
return if (result1 is Undefined) null else result1
}
init {
if (System.getSecurityManager() != null) {
try {
AccessController.checkPermission(AllPermission())
} catch (var6: AccessControlException) {
accessContext = AccessController.getContext()
}
}
val cx = Context.enter()
try {
topLevel = RhinoTopLevel(cx, this)
} finally {
Context.exit()
}
indexedProps = HashMap()
implementor = object : InterfaceImplementor(this) {
override fun isImplemented(thiz: Any?, iface: Class<*>): Boolean {
var thiz1 = thiz
return try {
if (thiz1 != null && thiz1 !is Scriptable) {
thiz1 = Context.toObject(
thiz1,
topLevel
)
}
val engineScope =
getRuntimeScope(context)
val localScope =
if (thiz1 != null) thiz1 as Scriptable else engineScope
val var5 = iface.methods
val var6 = var5.size
for (var7 in 0 until var6) {
val method = var5[var7]
if (method.declaringClass != Any::class.java) {
val obj =
ScriptableObject.getProperty(localScope, method.name) as? Function
obj ?: return false
}
}
true
} finally {
Context.exit()
}
} }
Scriptable scope = getRuntimeScope(context); override fun convertResult(method: Method?, res: Any): Any {
@SuppressWarnings("deprecation") val desiredType = method!!.returnType
Script scr = cx.compileReader(scope, script, fileName, 1, null); return (if (desiredType == Void.TYPE) null else Context.jsToJava(
ret = new RhinoCompiledScript(this, scr); res,
} catch (Exception e) { desiredType
if (DEBUG) e.printStackTrace(); ))!!
throw new ScriptException(e); }
} finally {
cx.exit();
} }
return ret;
} }
companion object {
private const val DEBUG = false
//package-private helpers @Language("js")
private val printSource = """
function print(str, newline) {
if (typeof str == "undefined") {
str = "undefined";
} else if (str == null) {
str = "null";
}
var out = context.getWriter();
if (!(out instanceof java.io.PrintWriter))
out = new java.io.PrintWriter(out);
out.print(String(str));
if (newline) out.print("\\n");
out.flush();
}
function println(str) {
print(str, true);
}
""".trimIndent()
static Context enterContext() { init {
// call this always so that initializer of this class runs ContextFactory.initGlobal(object : ContextFactory() {
// and initializes custom wrap factory and class shutter. override fun makeContext(): Context {
return Context.enter(); val cx = super.makeContext()
} cx.languageVersion = 200
cx.optimizationLevel = -1
cx.setClassShutter(instance)
cx.wrapFactory = RhinoWrapFactory.instance
return cx
}
AccessControlContext getAccessContext() { override fun doTopCall(
return accCtxt; callable: Callable,
} cx: Context,
scope: Scriptable,
thisObj: Scriptable,
args: Array<Any>
): Any {
var accCtxt: AccessControlContext? = null
val global = ScriptableObject.getTopLevelScope(scope)
val globalProto = global.prototype
if (globalProto is RhinoTopLevel) {
accCtxt = globalProto.accessContext
}
return if (accCtxt != null) AccessController.doPrivileged(
PrivilegedAction {
superDoTopCall(
callable,
cx,
scope,
thisObj,
args
)
} as PrivilegedAction<Any>, accCtxt) else superDoTopCall(
callable,
cx,
scope,
thisObj,
args
)
}
Object[] wrapArguments(Object[] args) { private fun superDoTopCall(
if (args == null) { callable: Callable,
return Context.emptyArgs; cx: Context,
scope: Scriptable,
thisObj: Scriptable,
args: Array<Any>
): Any {
return super.doTopCall(callable, cx, scope, thisObj, args)
}
})
} }
Object[] res = new Object[args.length];
for (int i = 0; i < res.length; i++) {
res[i] = Context.javaToJS(args[i], topLevel);
}
return res;
} }
}
Object unwrapReturnValue(Object result) {
if (result instanceof Wrapper) {
result = ((Wrapper) result).unwrap();
}
return result instanceof Undefined ? null : result;
}
/*
public static void main(String[] args) throws Exception {
if (args.length == 0) {
System.out.println("No file specified");
return;
}
InputStreamReader r = new InputStreamReader(new FileInputStream(args[0]));
ScriptEngine engine = new RhinoScriptEngine();
engine.put("x", "y");
engine.put(ScriptEngine.FILENAME, args[0]);
engine.eval(r);
System.out.println(engine.get("x"));
}
*/
}

View File

@ -22,24 +22,14 @@
* or visit www.oracle.com if you need additional information or have any * or visit www.oracle.com if you need additional information or have any
* questions. * questions.
*/ */
package com.script.rhino
package com.script.rhino; import com.script.Bindings
import com.script.ScriptContext
import com.script.Bindings; import com.script.SimpleScriptContext
import com.script.ScriptContext; import org.mozilla.javascript.*
import com.script.SimpleScriptContext; import org.mozilla.javascript.Function
import java.security.AccessControlContext
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;
import java.security.AccessControlContext;
/** /**
* This class serves as top level scope for Rhino. This class adds * This class serves as top level scope for Rhino. This class adds
@ -49,129 +39,69 @@ import java.security.AccessControlContext;
* @author A. Sundararajan * @author A. Sundararajan
* @since 1.6 * @since 1.6
*/ */
public final class RhinoTopLevel extends ImporterTopLevel { @Suppress("UNUSED_PARAMETER")
RhinoTopLevel(Context cx, RhinoScriptEngine engine) { class RhinoTopLevel internal constructor(cx: Context?, val scriptEngine: RhinoScriptEngine) :
// second boolean parameter to super constructor tells whether ImporterTopLevel(cx, System.getSecurityManager() != null) {
// to seal standard JavaScript objects or not. If security manager
// is present, we seal the standard objects.
super(cx, System.getSecurityManager() != null);
this.engine = engine;
// initialize JSAdapter lazily. Reduces footprint & startup time. init {
new LazilyLoadedCtor(this, "JSAdapter", LazilyLoadedCtor(this, "JSAdapter", "com.sun.script.javascript.JSAdapter", false)
"com.sun.script.javascript.JSAdapter", JavaAdapter.init(cx, this, false)
false); val names = arrayOf("bindings", "scope", "sync")
defineFunctionProperties(names, RhinoTopLevel::class.java, 2)
/*
* 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.
*/
com.script.rhino.JavaAdapter.init(cx, this, false);
// add top level functions
String names[] = { "bindings", "scope", "sync" };
defineFunctionProperties(names, RhinoTopLevel.class,
ScriptableObject.DONTENUM);
} }
/** val accessContext: AccessControlContext?
* The bindings function takes a JavaScript scope object get() = scriptEngine.accessContext
* of type ExternalScriptable and returns the underlying Bindings
* instance. companion object {
*
* var page = scope(pageBindings); @JvmStatic
* with (page) { fun bindings(
* // code that uses page scope cx: Context?,
* } thisObj: Scriptable?,
* var b = bindings(page); args: Array<Any?>,
* // operate on bindings here. funObj: Function?
*/ ): Any {
public static Object bindings(Context cx, Scriptable thisObj, Object[] args, if (args.size == 1) {
Function funObj) { var arg = args[0]
if (args.length == 1) { if (arg is Wrapper) {
Object arg = args[0]; arg = arg.unwrap()
if (arg instanceof Wrapper) { }
arg = ((Wrapper)arg).unwrap(); if (arg is ExternalScriptable) {
val ctx = arg.context
val bind = ctx.getBindings(100)
return Context.javaToJS(bind, getTopLevelScope(thisObj))
}
} }
if (arg instanceof com.script.rhino.ExternalScriptable) { return Context.getUndefinedValue()
ScriptContext ctx = ((com.script.rhino.ExternalScriptable)arg).getContext(); }
Bindings bind = ctx.getBindings(ScriptContext.ENGINE_SCOPE);
return Context.javaToJS(bind, @JvmStatic
ScriptableObject.getTopLevelScope(thisObj)); fun scope(cx: Context?, thisObj: Scriptable?, args: Array<Any?>, funObj: Function?): Any {
if (args.size == 1) {
var arg = args[0]
if (arg is Wrapper) {
arg = arg.unwrap()
}
if (arg is Bindings) {
val ctx: ScriptContext = SimpleScriptContext()
ctx.setBindings(arg as Bindings?, 100)
val res: Scriptable = ExternalScriptable(ctx)
res.prototype = getObjectPrototype(thisObj)
res.parentScope = getTopLevelScope(thisObj)
return res
}
}
return Context.getUndefinedValue()
}
@JvmStatic
fun sync(cx: Context?, thisObj: Scriptable?, args: Array<Any?>, funObj: Function?): Any {
return if (args.size == 1 && args[0] is Function) {
Synchronizer(args[0] as Function?)
} else {
throw Context.reportRuntimeError("wrong argument(s) for sync")
} }
} }
return cx.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 com.script.rhino.ExternalScriptable(ctx);
res.setPrototype(ScriptableObject.getObjectPrototype(thisObj));
res.setParentScope(ScriptableObject.getTopLevelScope(thisObj));
return res;
}
}
return cx.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.
* {@code
* 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;
}
AccessControlContext getAccessContext() {
return engine.getAccessContext();
}
private RhinoScriptEngine engine;
}

View File

@ -22,18 +22,14 @@
* or visit www.oracle.com if you need additional information or have any * or visit www.oracle.com if you need additional information or have any
* questions. * questions.
*/ */
package com.script.rhino
package com.script.rhino; import org.mozilla.javascript.Context
import org.mozilla.javascript.NativeJavaObject
import org.mozilla.javascript.ClassShutter; import org.mozilla.javascript.Scriptable
import org.mozilla.javascript.Context; import org.mozilla.javascript.WrapFactory
import org.mozilla.javascript.NativeJavaObject; import java.lang.reflect.Member
import org.mozilla.javascript.Scriptable; import java.lang.reflect.Modifier
import org.mozilla.javascript.WrapFactory;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
/** /**
* This wrap factory is used for security reasons. JSR 223 script * This wrap factory is used for security reasons. JSR 223 script
@ -48,118 +44,88 @@ import java.lang.reflect.Modifier;
* @author A. Sundararajan * @author A. Sundararajan
* @since 1.6 * @since 1.6
*/ */
final class RhinoWrapFactory extends WrapFactory { internal class RhinoWrapFactory private constructor() : WrapFactory() {
private RhinoWrapFactory() {}
private static RhinoWrapFactory theInstance;
static synchronized WrapFactory getInstance() { override fun wrapAsJavaObject(
if (theInstance == null) { cx: Context,
theInstance = new RhinoWrapFactory(); scope: Scriptable?,
} javaObject: Any,
return theInstance; staticType: Class<*>?
} ): Scriptable? {
scope?.delete("Packages")
// We use instance of this class to wrap security sensitive val sm = System.getSecurityManager()
// Java object. Please refer below. val classShutter = RhinoClassShutter.instance
private static class RhinoJavaObject extends NativeJavaObject { return if (javaObject is ClassLoader) {
RhinoJavaObject(Scriptable scope, Object obj, Class type) { sm?.checkPermission(RuntimePermission("getClassLoader"))
// we pass 'null' to object. NativeJavaObject uses super.wrapAsJavaObject(cx, scope, javaObject, staticType)
// passed 'type' to reflect fields and methods when
// object is null.
super(scope, null, type);
// Now, we set actual object. 'javaObject' is protected
// field of NativeJavaObject.
javaObject = obj;
}
@Override
public Object get(String name, Scriptable start) {
if (name.equals("getClass") || name.equals("exec")) {
return NOT_FOUND;
}
return super.get(name, start);
}
}
public Scriptable wrapAsJavaObject(Context cx, Scriptable scope,
Object javaObject, Class staticType) {
if (scope != null) {
scope.delete("Packages");
}
SecurityManager sm = System.getSecurityManager();
ClassShutter classShutter = RhinoClassShutter.getInstance();
if (javaObject instanceof ClassLoader) {
// Check with Security Manager whether we can expose a
// ClassLoader...
if (sm != null) {
sm.checkPermission(new RuntimePermission("getClassLoader"));
}
// if we fall through here, check permission succeeded.
return super.wrapAsJavaObject(cx, scope, javaObject, staticType);
} else { } else {
String name = null; var name: String? = null
if (javaObject instanceof Class) { if (javaObject is Class<*>) {
name = ((Class)javaObject).getName(); name = javaObject.name
} else if (javaObject instanceof Member) { } else if (javaObject is Member) {
Member member = (Member) javaObject; if (sm != null && !Modifier.isPublic(javaObject.modifiers)) {
// Check member access. Don't allow reflective access to return null
// non-public members. Note that we can't call checkMemberAccess
// because that expects exact stack depth!
if (sm != null && !Modifier.isPublic(member.getModifiers())) {
return null;
} }
name = member.getDeclaringClass().getName(); name = javaObject.declaringClass.name
} }
// Now, make sure that no ClassShutter prevented Class or Member
// of it is accessed reflectively. Note that ClassShutter may
// prevent access to a class, even though SecurityManager permit.
if (name != null) { if (name != null) {
if (!classShutter.visibleToScripts(name)) { if (!classShutter.visibleToScripts(name)) null else super.wrapAsJavaObject(
return null; cx,
} else { scope,
return super.wrapAsJavaObject(cx, scope, javaObject, staticType); javaObject,
} staticType
} )
}
// we have got some non-reflective object.
Class dynamicType = javaObject.getClass();
String name = dynamicType.getName();
if (!classShutter.visibleToScripts(name)) {
// Object of some sensitive class (such as sun.net.www.*
// objects returned from public method of java.net.URL class.
// We expose this object as though it is an object of some
// super class that is safe for access.
Class type = null;
// Whenever a Java Object is wrapped, we are passed with a
// staticType which is the type found from environment. For
// example, method return type known from signature. The dynamic
// type would be the actual Class of the actual returned object.
// If the staticType is an interface, we just use that type.
if (staticType != null && staticType.isInterface()) {
type = staticType;
} else { } else {
// dynamicType is always a class type and never an interface. var dynamicType: Class<*>? = javaObject.javaClass
// find an accessible super class of the dynamic type. name = dynamicType!!.name
while (dynamicType != null) { if (classShutter.visibleToScripts(name)) {
dynamicType = dynamicType.getSuperclass(); super.wrapAsJavaObject(cx, scope, javaObject, staticType)
name = dynamicType.getName(); } else {
if (classShutter.visibleToScripts(name)) { var type: Class<*>? = null
type = dynamicType; break; if (staticType != null && staticType.isInterface) {
type = staticType
} else {
while (dynamicType != null) {
dynamicType = dynamicType.superclass
name = dynamicType.name
if (classShutter.visibleToScripts(name)) {
type = dynamicType
break
}
}
assert(type != null) { "java.lang.Object 不可访问" }
} }
RhinoJavaObject(scope, javaObject, type)
} }
// atleast java.lang.Object has to be accessible. So, when
// we reach here, type variable should not be null.
assert type != null:
"even java.lang.Object is not accessible?";
} }
// create custom wrapper with the 'safe' type.
return new RhinoJavaObject(scope, javaObject, type);
} else {
return super.wrapAsJavaObject(cx, scope, javaObject, staticType);
} }
} }
}
private class RhinoJavaObject(
scope: Scriptable?,
obj: Any?,
type: Class<*>?
) : NativeJavaObject(scope, null as Any?, type) {
init {
javaObject = obj
}
override fun get(name: String, start: Scriptable): Any {
return if (name != "getClass" && name != "exec") super.get(name, start) else NOT_FOUND
}
}
companion object {
private var theInstance: RhinoWrapFactory? = null
@JvmStatic
@get:Synchronized
val instance: WrapFactory?
get() {
if (theInstance == null) {
theInstance = RhinoWrapFactory()
}
return theInstance
}
}
}