diff --git a/app/lib/rhino-1.7.13-1.jar b/app/lib/rhino-1.7.13-1.jar deleted file mode 100644 index 1f490006e..000000000 Binary files a/app/lib/rhino-1.7.13-1.jar and /dev/null differ diff --git a/app/lib/rhino-1.7.14-1.jar b/app/lib/rhino-1.7.14-1.jar new file mode 100644 index 000000000..8dedf14ff Binary files /dev/null and b/app/lib/rhino-1.7.14-1.jar differ diff --git a/app/src/main/java/com/script/AbstractScriptEngine.java b/app/src/main/java/com/script/AbstractScriptEngine.java new file mode 100644 index 000000000..f71d92705 --- /dev/null +++ b/app/src/main/java/com/script/AbstractScriptEngine.java @@ -0,0 +1,110 @@ +/* + * Decompiled with CFR 0.152. + */ +package com.script; + +import java.io.Reader; + +public abstract class AbstractScriptEngine + implements ScriptEngine { + protected ScriptContext context = new SimpleScriptContext(); + + public AbstractScriptEngine() { + } + + public AbstractScriptEngine(Bindings n) { + this(); + if (n == null) { + throw new NullPointerException("n is null"); + } + this.context.setBindings(n, 100); + } + + @Override + public void setContext(ScriptContext ctxt) { + if (ctxt == null) { + throw new NullPointerException("null context"); + } + this.context = ctxt; + } + + @Override + public ScriptContext getContext() { + return this.context; + } + + @Override + public Bindings getBindings(int scope) { + if (scope == 200) { + return this.context.getBindings(200); + } + if (scope == 100) { + return this.context.getBindings(100); + } + throw new IllegalArgumentException("Invalid scope value."); + } + + @Override + public void setBindings(Bindings bindings, int scope) { + if (scope == 200) { + this.context.setBindings(bindings, 200); + } else if (scope == 100) { + this.context.setBindings(bindings, 100); + } else { + throw new IllegalArgumentException("Invalid scope value."); + } + } + + @Override + public void put(String key, Object value) { + Bindings nn = this.getBindings(100); + if (nn != null) { + nn.put(key, value); + } + } + + @Override + public Object get(String key) { + Bindings nn = this.getBindings(100); + if (nn != null) { + return nn.get(key); + } + return null; + } + + @Override + public Object eval(Reader reader, Bindings bindings) throws ScriptException { + return this.eval(reader, this.getScriptContext(bindings)); + } + + @Override + public Object eval(String script, Bindings bindings) throws ScriptException { + return this.eval(script, this.getScriptContext(bindings)); + } + + @Override + public Object eval(Reader reader) throws ScriptException { + return this.eval(reader, this.context); + } + + @Override + public Object eval(String script) throws ScriptException { + return this.eval(script, this.context); + } + + protected ScriptContext getScriptContext(Bindings nn) { + SimpleScriptContext ctxt = new SimpleScriptContext(); + Bindings gs = this.getBindings(200); + if (gs != null) { + ctxt.setBindings(gs, 200); + } + if (nn != null) { + ctxt.setBindings(nn, 100); + ctxt.setReader(this.context.getReader()); + ctxt.setWriter(this.context.getWriter()); + ctxt.setErrorWriter(this.context.getErrorWriter()); + return ctxt; + } + throw new NullPointerException("Engine scope Bindings may not be null."); + } +} diff --git a/app/src/main/java/com/script/Bindings.java b/app/src/main/java/com/script/Bindings.java new file mode 100644 index 000000000..2aa65a5be --- /dev/null +++ b/app/src/main/java/com/script/Bindings.java @@ -0,0 +1,24 @@ +/* + * Decompiled with CFR 0.152. + */ +package com.script; + +import java.util.Map; + +public interface Bindings + extends Map { + @Override + public boolean containsKey(Object var1); + + @Override + public Object get(Object var1); + + @Override + public Object put(String var1, Object var2); + + @Override + public void putAll(Map var1); + + @Override + public Object remove(Object var1); +} diff --git a/app/src/main/java/com/script/Compilable.java b/app/src/main/java/com/script/Compilable.java new file mode 100644 index 000000000..ca15270eb --- /dev/null +++ b/app/src/main/java/com/script/Compilable.java @@ -0,0 +1,12 @@ +/* + * Decompiled with CFR 0.152. + */ +package com.script; + +import java.io.Reader; + +public interface Compilable { + public CompiledScript compile(Reader var1) throws ScriptException; + + public CompiledScript compile(String var1) throws ScriptException; +} diff --git a/app/src/main/java/com/script/CompiledScript.java b/app/src/main/java/com/script/CompiledScript.java new file mode 100644 index 000000000..1cf0eff0c --- /dev/null +++ b/app/src/main/java/com/script/CompiledScript.java @@ -0,0 +1,28 @@ +/* + * Decompiled with CFR 0.152. + */ +package com.script; + +public abstract class CompiledScript { + public abstract Object eval(ScriptContext var1) throws ScriptException; + + public abstract ScriptEngine getEngine(); + + public Object eval(Bindings bindings) throws ScriptException { + ScriptContext ctxt = this.getEngine().getContext(); + if (bindings != null) { + SimpleScriptContext tempctxt = new SimpleScriptContext(); + tempctxt.setBindings(bindings, 100); + tempctxt.setBindings(ctxt.getBindings(200), 200); + tempctxt.setWriter(ctxt.getWriter()); + tempctxt.setReader(ctxt.getReader()); + tempctxt.setErrorWriter(ctxt.getErrorWriter()); + ctxt = tempctxt; + } + return this.eval(ctxt); + } + + public Object eval() throws ScriptException { + return this.eval(this.getEngine().getContext()); + } +} diff --git a/app/src/main/java/com/script/Invocable.java b/app/src/main/java/com/script/Invocable.java new file mode 100644 index 000000000..9e4084924 --- /dev/null +++ b/app/src/main/java/com/script/Invocable.java @@ -0,0 +1,14 @@ +/* + * Decompiled with CFR 0.152. + */ +package com.script; + +public interface Invocable { + public T getInterface(Class var1); + + public T getInterface(Object var1, Class var2); + + public Object invokeFunction(String var1, Object ... var2) throws ScriptException, NoSuchMethodException; + + public Object invokeMethod(Object var1, String var2, Object ... var3) throws ScriptException, NoSuchMethodException; +} diff --git a/app/src/main/java/com/script/ScriptContext.java b/app/src/main/java/com/script/ScriptContext.java new file mode 100644 index 000000000..a3604f7e5 --- /dev/null +++ b/app/src/main/java/com/script/ScriptContext.java @@ -0,0 +1,41 @@ +/* + * Decompiled with CFR 0.152. + */ +package com.script; + +import java.io.Reader; +import java.io.Writer; +import java.util.List; + +public interface ScriptContext { + public static final int ENGINE_SCOPE = 100; + public static final int GLOBAL_SCOPE = 200; + + public Object getAttribute(String var1); + + public Object getAttribute(String var1, int var2); + + public int getAttributesScope(String var1); + + public Bindings getBindings(int var1); + + public Writer getErrorWriter(); + + public Reader getReader(); + + public List getScopes(); + + public Writer getWriter(); + + public Object removeAttribute(String var1, int var2); + + public void setAttribute(String var1, Object var2, int var3); + + public void setBindings(Bindings var1, int var2); + + public void setErrorWriter(Writer var1); + + public void setReader(Reader var1); + + public void setWriter(Writer var1); +} diff --git a/app/src/main/java/com/script/ScriptEngine.java b/app/src/main/java/com/script/ScriptEngine.java new file mode 100644 index 000000000..d71328a50 --- /dev/null +++ b/app/src/main/java/com/script/ScriptEngine.java @@ -0,0 +1,36 @@ +/* + * Decompiled with CFR 0.152. + */ +package com.script; + +import java.io.Reader; + +public interface ScriptEngine { + public static final String FILENAME = "javax.script.filename"; + + public Bindings createBindings(); + + public Object eval(Reader var1) throws ScriptException; + + public Object eval(Reader var1, Bindings var2) throws ScriptException; + + public Object eval(Reader var1, ScriptContext var2) throws ScriptException; + + public Object eval(String var1) throws ScriptException; + + public Object eval(String var1, Bindings var2) throws ScriptException; + + public Object eval(String var1, ScriptContext var2) throws ScriptException; + + public Object get(String var1); + + public Bindings getBindings(int var1); + + public ScriptContext getContext(); + + public void put(String var1, Object var2); + + public void setBindings(Bindings var1, int var2); + + public void setContext(ScriptContext var1); +} diff --git a/app/src/main/java/com/script/ScriptException.java b/app/src/main/java/com/script/ScriptException.java new file mode 100644 index 000000000..abd9b48a9 --- /dev/null +++ b/app/src/main/java/com/script/ScriptException.java @@ -0,0 +1,67 @@ +/* + * Decompiled with CFR 0.152. + */ +package com.script; + +public class ScriptException + extends Exception { + private int columnNumber; + private String fileName; + private int lineNumber; + + public ScriptException(String s) { + super(s); + this.fileName = null; + this.lineNumber = -1; + this.columnNumber = -1; + } + + public ScriptException(Exception e) { + super(e); + this.fileName = null; + this.lineNumber = -1; + this.columnNumber = -1; + } + + public ScriptException(String message, String fileName2, int lineNumber2) { + super(message); + this.fileName = fileName2; + this.lineNumber = lineNumber2; + this.columnNumber = -1; + } + + public ScriptException(String message, String fileName2, int lineNumber2, int columnNumber2) { + super(message); + this.fileName = fileName2; + this.lineNumber = lineNumber2; + this.columnNumber = columnNumber2; + } + + @Override + public String getMessage() { + String ret = super.getMessage(); + if (this.fileName == null) { + return ret; + } + String ret2 = ret + " in " + this.fileName; + if (this.lineNumber != -1) { + ret2 = ret2 + " at line number " + this.lineNumber; + } + if (this.columnNumber != -1) { + return ret2 + " at column number " + this.columnNumber; + } + return ret2; + } + + public int getLineNumber() { + return this.lineNumber; + } + + public int getColumnNumber() { + return this.columnNumber; + } + + public String getFileName() { + return this.fileName; + } +} diff --git a/app/src/main/java/com/script/SimpleBindings.java b/app/src/main/java/com/script/SimpleBindings.java new file mode 100644 index 000000000..1c3c3fb48 --- /dev/null +++ b/app/src/main/java/com/script/SimpleBindings.java @@ -0,0 +1,108 @@ +/* + * Decompiled with CFR 0.152. + */ +package com.script; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class SimpleBindings + implements Bindings { + private Map map; + + public SimpleBindings(Map m) { + if (m == null) { + throw new NullPointerException(); + } + this.map = m; + } + + public SimpleBindings() { + this(new HashMap()); + } + + @Override + public Object put(String name, Object value) { + this.checkKey(name); + return this.map.put(name, value); + } + + @Override + public void putAll(Map toMerge) { + if (toMerge == null) { + throw new NullPointerException("toMerge map is null"); + } + for (Map.Entry entry : toMerge.entrySet()) { + String key = entry.getKey(); + this.checkKey(key); + this.put(key, entry.getValue()); + } + } + + @Override + public void clear() { + this.map.clear(); + } + + @Override + public boolean containsKey(Object key) { + this.checkKey(key); + return this.map.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return this.map.containsValue(value); + } + + @Override + public Set> entrySet() { + return this.map.entrySet(); + } + + @Override + public Object get(Object key) { + this.checkKey(key); + return this.map.get(key); + } + + @Override + public boolean isEmpty() { + return this.map.isEmpty(); + } + + @Override + public Set keySet() { + return this.map.keySet(); + } + + @Override + public Object remove(Object key) { + this.checkKey(key); + return this.map.remove(key); + } + + @Override + public int size() { + return this.map.size(); + } + + @Override + public Collection values() { + return this.map.values(); + } + + private void checkKey(Object key) { + if (key == null) { + throw new NullPointerException("key can not be null"); + } + if (!(key instanceof String)) { + throw new ClassCastException("key should be a String"); + } + if (key.equals("")) { + throw new IllegalArgumentException("key can not be empty"); + } + } +} diff --git a/app/src/main/java/com/script/SimpleScriptContext.java b/app/src/main/java/com/script/SimpleScriptContext.java new file mode 100644 index 000000000..028da4be4 --- /dev/null +++ b/app/src/main/java/com/script/SimpleScriptContext.java @@ -0,0 +1,167 @@ +/* + * Decompiled with CFR 0.152. + */ +package com.script; + +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class SimpleScriptContext + implements ScriptContext { + private static List scopes = new ArrayList(2); + protected Bindings engineScope = new SimpleBindings(); + protected Writer errorWriter = new PrintWriter(System.err, true); + protected Bindings globalScope = null; + protected Reader reader = new InputStreamReader(System.in); + protected Writer writer = new PrintWriter(System.out, true); + + @Override + public void setBindings(Bindings bindings, int scope) { + switch (scope) { + case 100: { + if (bindings == null) { + throw new NullPointerException("Engine scope cannot be null."); + } + this.engineScope = bindings; + return; + } + case 200: { + this.globalScope = bindings; + return; + } + } + throw new IllegalArgumentException("Invalid scope value."); + } + + @Override + public Object getAttribute(String name) { + if (this.engineScope.containsKey(name)) { + return this.getAttribute(name, 100); + } + if (this.globalScope == null || !this.globalScope.containsKey(name)) { + return null; + } + return this.getAttribute(name, 200); + } + + @Override + public Object getAttribute(String name, int scope) { + switch (scope) { + case 100: { + return this.engineScope.get(name); + } + case 200: { + if (this.globalScope != null) { + return this.globalScope.get(name); + } + return null; + } + } + throw new IllegalArgumentException("Illegal scope value."); + } + + @Override + public Object removeAttribute(String name, int scope) { + switch (scope) { + case 100: { + if (this.getBindings(100) != null) { + return this.getBindings(100).remove(name); + } + return null; + } + case 200: { + if (this.getBindings(200) != null) { + return this.getBindings(200).remove(name); + } + return null; + } + } + throw new IllegalArgumentException("Illegal scope value."); + } + + @Override + public void setAttribute(String name, Object value, int scope) { + switch (scope) { + case 100: { + this.engineScope.put(name, value); + return; + } + case 200: { + if (this.globalScope != null) { + this.globalScope.put(name, value); + return; + } + return; + } + } + throw new IllegalArgumentException("Illegal scope value."); + } + + @Override + public Writer getWriter() { + return this.writer; + } + + @Override + public Reader getReader() { + return this.reader; + } + + @Override + public void setReader(Reader reader2) { + this.reader = reader2; + } + + @Override + public void setWriter(Writer writer2) { + this.writer = writer2; + } + + @Override + public Writer getErrorWriter() { + return this.errorWriter; + } + + @Override + public void setErrorWriter(Writer writer2) { + this.errorWriter = writer2; + } + + @Override + public int getAttributesScope(String name) { + if (this.engineScope.containsKey(name)) { + return 100; + } + if (this.globalScope == null || !this.globalScope.containsKey(name)) { + return -1; + } + return 200; + } + + @Override + public Bindings getBindings(int scope) { + if (scope == 100) { + return this.engineScope; + } + if (scope == 200) { + return this.globalScope; + } + throw new IllegalArgumentException("Illegal scope value."); + } + + @Override + public List getScopes() { + return scopes; + } + + static { + scopes.add(100); + scopes.add(200); + scopes = Collections.unmodifiableList(scopes); + } +} diff --git a/app/src/main/java/com/script/rhino/ExternalScriptable.java b/app/src/main/java/com/script/rhino/ExternalScriptable.java new file mode 100644 index 000000000..66d286dbf --- /dev/null +++ b/app/src/main/java/com/script/rhino/ExternalScriptable.java @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.script.rhino; + +import com.script.Bindings; +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 + * backed by a JSR 223 ScriptContext instance. + * + * @author Mike Grogan + * @author A. Sundararajan + * @since 1.6 + */ + +final class ExternalScriptable implements Scriptable { + /* 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 indexedProps; + + // my prototype + private Scriptable prototype; + // my parent scope, if any + private Scriptable parent; + + ExternalScriptable(ScriptContext context) { + this(context, new HashMap()); + } + + ExternalScriptable(ScriptContext context, Map indexedProps) { + if (context == null) { + throw new 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 { + synchronized (context) { + int scope = context.getAttributesScope(name); + if (scope != -1) { + Object value = context.getAttribute(name, scope); + return Context.javaToJS(value, this); + } else { + return NOT_FOUND; + } + } + } + } + + /** + * Returns the value of the indexed property or 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 + */ + public synchronized Object get(int index, Scriptable start) { + Integer key = new Integer(index); + if (indexedProps.containsKey(index)) { + return indexedProps.get(key); + } else { + return NOT_FOUND; + } + } + + /** + * Returns true if the named property is defined. + * + * @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; + } + } + } + + /** + * Returns true if the property index is defined. + * + * @param index the numeric index for 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(int index, Scriptable start) { + Integer key = new Integer(index); + return indexedProps.containsKey(key); + } + + /** + * 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 { + synchronized (context) { + int scope = context.getAttributesScope(name); + if (scope == -1) { + scope = ScriptContext.ENGINE_SCOPE; + } + context.setAttribute(name, jsToJava(value), scope); + } + } + } + } else { + start.put(name, start, value); + } + } + + /** + * Sets the value of the indexed property, creating it if need be. + * + * @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(new Integer(index), value); + } + } else { + start.put(index, start, value); + } + } + + /** + * Removes a named property from the object. + * + * If the property is not found, no action is taken. + * + * @param name the name of the property + */ + public synchronized void delete(String name) { + if (isEmpty(name)) { + indexedProps.remove(name); + } else { + synchronized (context) { + int scope = context.getAttributesScope(name); + if (scope != -1) { + context.removeAttribute(name, scope); + } + } + } + } + + /** + * Removes the indexed property from the object. + * + * 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(new Integer(index)); + } + + /** + * Get the prototype of the object. + * @return the prototype + */ + public Scriptable getPrototype() { + return prototype; + } + + /** + * Set the prototype of the object. + * @param prototype the prototype to set + */ + public void setPrototype(Scriptable prototype) { + this.prototype = prototype; + } + + /** + * Get the parent scope of the object. + * @return the parent scope + */ + public Scriptable getParentScope() { + return parent; + } + + /** + * Set the parent scope of the object. + * @param parent the parent scope to set + */ + public void setParentScope(Scriptable parent) { + this.parent = parent; + } + + /** + * Get an array of property ids. + * + * Not all property ids need be returned. Those properties + * whose ids are not returned are considered non-enumerable. + * + * @return an array of Objects. Each entry in the array is either + * a java.lang.String or a java.lang.Number + */ + public synchronized Object[] getIds() { + String[] keys = getAllKeys(); + int size = keys.length + indexedProps.size(); + 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; + } + + /** + * Get the default value of the object with a given hint. + * The hints are String.class for type String, Number.class for type + * Number, Scriptable.class for type Object, and Boolean.class for + * type Boolean.

+ * + * A hint 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 { + throw Context.reportRuntimeError( + "Invalid JavaScript value of type " + + typeHint.toString()); + } + args[0] = hint; + } + Object v = ScriptableObject.getProperty(this, methodName); + if (!(v instanceof Function)) + continue; + Function fun = (Function) v; + Context cx = RhinoScriptEngine.enterContext(); + try { + v = fun.call(cx, fun.getParentScope(), this, args); + } finally { + cx.exit(); + } + if (v != null) { + if (!(v instanceof Scriptable)) { + return v; + } + if (typeHint == ScriptRuntime.ScriptableClass + || typeHint == ScriptRuntime.FunctionClass) + { + return v; + } + if (tryToString && v instanceof Wrapper) { + // Let a wrapped java.lang.String pass for a primitive + // string. + Object u = ((Wrapper)v).unwrap(); + if (u instanceof String) + 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 list = new ArrayList(); + 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()]; + list.toArray(res); + return res; + } + + /** + * We convert script values to the nearest Java value. + * We unwrap wrapped Java objects so that access from + * Bindings.get() would return "workable" value for Java. + * But, at the same time, we need to make few special cases + * 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; + } + + /* script may use Java primitive wrapper type objects + * (such as java.lang.Integer, java.lang.Boolean etc) + * explicitly. If we unwrap, then these script objects + * will become script primitive types. For example, + * + * var x = new java.lang.Double(3.0); print(typeof x); + * + * will print 'number'. We don't want that to happen. + */ + Object obj = njb.unwrap(); + if (obj instanceof Number || obj instanceof String || + obj instanceof Boolean || obj instanceof Character) { + // special type wrapped -- we just leave it as is. + return njb; + } else { + // return unwrapped object for any other object. + return obj; + } + } else { // not-a-Java-wrapper + return jsObj; + } + } +} diff --git a/app/src/main/java/com/script/rhino/InterfaceImplementor.java b/app/src/main/java/com/script/rhino/InterfaceImplementor.java new file mode 100644 index 000000000..adba80372 --- /dev/null +++ b/app/src/main/java/com/script/rhino/InterfaceImplementor.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +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; + + +/* + * java.lang.reflect.Proxy based interface implementor. This is meant + * to be used to implement Invocable.getInterface. + * + * @author Mike Grogan + * @since 1.6 + */ +public class InterfaceImplementor { + + private Invocable engine; + + /** Creates a new instance of Invocable */ + public InterfaceImplementor(Invocable engine) { + this.engine = engine; + } + + private final class InterfaceImplementorInvocationHandler + implements InvocationHandler { + private Object thiz; + private AccessControlContext accCtxt; + + public InterfaceImplementorInvocationHandler(Object thiz, + AccessControlContext accCtxt) { + this.thiz = thiz; + this.accCtxt = accCtxt; + } + + 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() { + 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 getInterface(Object thiz, Class iface) + throws ScriptException { + if (iface == null || !iface.isInterface()) { + throw new IllegalArgumentException("interface Class expected"); + } + 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; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/script/rhino/JSAdapter.java b/app/src/main/java/com/script/rhino/JSAdapter.java new file mode 100644 index 000000000..0cb47aafd --- /dev/null +++ b/app/src/main/java/com/script/rhino/JSAdapter.java @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.script.rhino; + +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 + * calls specially named JavaScript methods on an adaptee object when property + * access is attempted on it. + * + * Example: + * + * var y = { + * __get__ : function (name) { ... } + * __has__ : function (name) { ... } + * __put__ : function (name, value) {...} + * __delete__ : function (name) { ... } + * __getIds__ : function () { ... } + * }; + * + * var x = new JSAdapter(y); + * + * x.i; // calls y.__get__ + * i in x; // calls y.__has__ + * x.p = 10; // calls y.__put__ + * delete x.p; // calls y.__delete__ + * for (i in x) { print(i); } // calls y.__getIds__ + * + * If a special JavaScript method is not found in the adaptee, then JSAdapter + * forwards the property access to the adaptee itself. + * + * JavaScript caller of adapter object is isolated from the fact that + * the property access/mutation/deletion are really calls to + * JavaScript methods on adaptee. Use cases include 'smart' + * properties, property access tracing/debugging, encaptulation with + * easy client access - in short JavaScript becomes more "Self" like. + * + * Note that Rhino already supports special properties like __proto__ + * (to set, get prototype), __parent__ (to set, get parent scope). We + * follow the same double underscore nameing convention here. Similarly + * the name JSAdapter is derived from JavaAdapter -- which is a facility + * to extend, implement Java classes/interfaces by JavaScript. + * + * @author A. Sundararajan + * @since 1.6 + */ +public final class JSAdapter implements Scriptable, Function { + private JSAdapter(Scriptable obj) { + setAdaptee(obj); + } + + // initializer to setup JSAdapter prototype in the given scope + public static void init(Context cx, Scriptable scope, boolean sealed) + throws RhinoException { + JSAdapter obj = new JSAdapter(cx.newObject(scope)); + 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 { + start = getAdaptee(); + return start.get(name, start); + } + } + + public Object get(int index, Scriptable start) { + Function func = getAdapteeFunction(GET_PROP); + if (func != null) { + return call(func, new Object[] { new Integer(index) }); + } else { + start = getAdaptee(); + return start.get(index, start); + } + } + + public boolean has(String name, Scriptable start) { + Function func = getAdapteeFunction(HAS_PROP); + if (func != null) { + Object res = call(func, new Object[] { name }); + return Context.toBoolean(res); + } else { + start = getAdaptee(); + return start.has(name, start); + } + } + + public boolean has(int index, Scriptable start) { + Function func = getAdapteeFunction(HAS_PROP); + if (func != null) { + Object res = call(func, new Object[] { new Integer(index) }); + return Context.toBoolean(res); + } else { + start = getAdaptee(); + return start.has(index, start); + } + } + + public void put(String name, Scriptable start, Object value) { + if (start == this) { + Function func = getAdapteeFunction(PUT_PROP); + if (func != null) { + call(func, new Object[] { name, value }); + } else { + start = getAdaptee(); + start.put(name, start, value); + } + } else { + start.put(name, start, value); + } + } + + public void put(int index, Scriptable start, Object value) { + if (start == this) { + Function func = getAdapteeFunction(PUT_PROP); + if( func != null) { + call(func, new Object[] { new Integer(index), value }); + } else { + start = getAdaptee(); + start.put(index, start, value); + } + } else { + start.put(index, start, value); + } + } + + public void delete(String name) { + Function func = getAdapteeFunction(DEL_PROP); + if (func != null) { + call(func, new Object[] { name }); + } else { + getAdaptee().delete(name); + } + } + + public void delete(int index) { + Function func = getAdapteeFunction(DEL_PROP); + if (func != null) { + call(func, new Object[] { new Integer(index) }); + } else { + getAdaptee().delete(index); + } + } + + public Scriptable getPrototype() { + return prototype; + } + + public void setPrototype(Scriptable prototype) { + this.prototype = prototype; + } + + public Scriptable getParentScope() { + return parent; + } + + public void setParentScope(Scriptable parent) { + this.parent = parent; + } + + public Object[] getIds() { + Function func = getAdapteeFunction(GET_PROPIDS); + if (func != null) { + Object val = call(func, new Object[0]); + // in most cases, adaptee would return native JS array + if (val instanceof NativeArray) { + NativeArray array = (NativeArray) val; + Object[] res = new Object[(int)array.getLength()]; + for (int index = 0; index < res.length; index++) { + res[index] = mapToId(array.get(index, array)); + } + return res; + } 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 { + // just return an empty array + res = Context.emptyArgs; + } + return res; + } else { + // some other return type, just return empty array + return Context.emptyArgs; + } + } else { + return getAdaptee().getIds(); + } + } + + public boolean hasInstance(Scriptable scriptable) { + if (scriptable instanceof JSAdapter) { + return true; + } else { + Scriptable proto = scriptable.getPrototype(); + while (proto != null) { + if (proto.equals(this)) return true; + proto = proto.getPrototype(); + } + return false; + } + } + + public Object getDefaultValue(Class hint) { + return getAdaptee().getDefaultValue(hint); + } + + public Object call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args) + throws RhinoException { + if (isPrototype) { + return construct(cx, scope, args); + } else { + Scriptable tmp = getAdaptee(); + if (tmp instanceof Function) { + return ((Function)tmp).call(cx, scope, tmp, args); + } else { + throw Context.reportRuntimeError("TypeError: not a function"); + } + } + } + + public Scriptable construct(Context cx, Scriptable scope, Object[] args) + throws RhinoException { + if (isPrototype) { + Scriptable topLevel = ScriptableObject.getTopLevelScope(scope); + JSAdapter newObj; + if (args.length > 0) { + newObj = new JSAdapter(Context.toObject(args[0], topLevel)); + } else { + throw Context.reportRuntimeError("JSAdapter requires adaptee"); + } + return newObj; + } else { + Scriptable tmp = getAdaptee(); + if (tmp instanceof Function) { + return ((Function)tmp).construct(cx, scope, args); + } else { + throw Context.reportRuntimeError("TypeError: not a constructor"); + } + } + } + + public Scriptable getAdaptee() { + return adaptee; + } + + public void setAdaptee(Scriptable adaptee) { + if (adaptee == null) { + throw new NullPointerException("adaptee can not be null"); + } + this.adaptee = adaptee; + } + + //-- internals only below this point + + // map a property id. Property id can only be an Integer or String + private Object mapToId(Object tmp) { + if (tmp instanceof Double) { + return new Integer(((Double)tmp).intValue()); + } else { + return Context.toString(tmp); + } + } + + private static Scriptable getFunctionPrototype(Scriptable scope) { + return ScriptableObject.getFunctionPrototype(scope); + } + + private Function getAdapteeFunction(String name) { + Object o = ScriptableObject.getProperty(getAdaptee(), name); + return (o instanceof Function)? (Function)o : null; + } + + private Object call(Function func, Object[] args) { + Context cx = Context.getCurrentContext(); + 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__"; +} diff --git a/app/src/main/java/com/script/rhino/JavaAdapter.java b/app/src/main/java/com/script/rhino/JavaAdapter.java new file mode 100644 index 000000000..ecda70aef --- /dev/null +++ b/app/src/main/java/com/script/rhino/JavaAdapter.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.script.rhino; + +import com.script.Invocable; + +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 + * interface in JavaScript. We support this using Invocable.getInterface. + * Using this JavaAdapter, script author could write: + * + * var r = new java.lang.Runnable() { + * run: function() { script... } + * }; + * + * r.run(); + * new java.lang.Thread(r).start(); + * + * Note that Rhino's JavaAdapter support allows extending a Java class and/or + * implementing one or more interfaces. This JavaAdapter implementation does + * not support these. + * + * @author A. Sundararajan + * @since 1.6 + */ +final class JavaAdapter extends ScriptableObject implements Function { + private JavaAdapter(Invocable engine) { + this.engine = engine; + } + + static void init(Context cx, Scriptable scope, boolean sealed) + throws RhinoException { + RhinoTopLevel topLevel = (RhinoTopLevel) scope; + 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() { + return "JavaAdapter"; + } + + public Object call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args) throws RhinoException { + return construct(cx, scope, args); + } + + 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; + } + } + if (clazz == null) { + throw Context.reportRuntimeError("JavaAdapter: first arg should be interface Class"); + } + + Scriptable topLevel = ScriptableObject.getTopLevelScope(scope); + return cx.toObject(engine.getInterface(args[1], clazz), topLevel); + } else { + throw Context.reportRuntimeError("JavaAdapter requires two arguments"); + } + } + + private Invocable engine; +} diff --git a/app/src/main/java/com/script/rhino/RhinoClassShutter.java b/app/src/main/java/com/script/rhino/RhinoClassShutter.java new file mode 100644 index 000000000..7d9e3bce9 --- /dev/null +++ b/app/src/main/java/com/script/rhino/RhinoClassShutter.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.script.rhino; + +import org.mozilla.javascript.ClassShutter; + +import java.util.HashMap; +import java.util.Map; + +/** + * This class prevents script access to certain sensitive classes. + * Note that this class checks over and above SecurityManager. i.e., although + * a SecurityManager would pass, class shutter may still prevent access. + * + * @author A. Sundararajan + * @since 1.6 + */ +final class RhinoClassShutter implements ClassShutter { + private static Map protectedClasses; + private static RhinoClassShutter theInstance; + + private RhinoClassShutter() { + } + + static synchronized ClassShutter getInstance() { + if (theInstance == null) { + theInstance = new RhinoClassShutter(); + protectedClasses = new HashMap(); + + // 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) { + int i = fullClassName.lastIndexOf("."); + if (i != -1) { + try { + sm.checkPackageAccess(fullClassName.substring(0, i)); + } catch (SecurityException se) { + return false; + } + } + } + // now, check is it a protected class. + return protectedClasses.get(fullClassName) == null; + } +} diff --git a/app/src/main/java/com/script/rhino/RhinoCompiledScript.java b/app/src/main/java/com/script/rhino/RhinoCompiledScript.java new file mode 100644 index 000000000..0d3e9c759 --- /dev/null +++ b/app/src/main/java/com/script/rhino/RhinoCompiledScript.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.script.rhino; + +import com.script.CompiledScript; +import com.script.ScriptContext; +import com.script.ScriptEngine; +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. + * + * @author Mike Grogan + * @since 1.6 + */ +final class RhinoCompiledScript extends CompiledScript { + + private com.script.rhino.RhinoScriptEngine engine; + private Script script; + + + 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 { + + Scriptable scope = engine.getRuntimeScope(context); + Object ret = script.exec(cx, scope); + result = engine.unwrapReturnValue(ret); + } catch (RhinoException re) { + int line = (line = re.lineNumber()) == 0 ? -1 : line; + String msg; + if (re instanceof JavaScriptException) { + msg = String.valueOf(((JavaScriptException)re).getValue()); + } else { + msg = re.toString(); + } + ScriptException se = new ScriptException(msg, re.sourceName(), line); + se.initCause(re); + throw se; + } finally { + Context.exit(); + } + + return result; + } + + public ScriptEngine getEngine() { + return engine; + } + +} diff --git a/app/src/main/java/com/script/rhino/RhinoScriptEngine.java b/app/src/main/java/com/script/rhino/RhinoScriptEngine.java new file mode 100644 index 000000000..28eb31354 --- /dev/null +++ b/app/src/main/java/com/script/rhino/RhinoScriptEngine.java @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.script.rhino; + + +import com.script.AbstractScriptEngine; +import com.script.Bindings; +import com.script.Compilable; +import com.script.CompiledScript; +import com.script.Invocable; +import com.script.ScriptContext; +import com.script.ScriptEngine; +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 ScriptEngine using the Mozilla Rhino + * interpreter. + * + * @author Mike Grogan + * @author A. Sundararajan + * @since 1.6 + */ +public final class RhinoScriptEngine extends AbstractScriptEngine + implements Invocable, Compilable { + + private static final boolean DEBUG = false; + + private AccessControlContext accCtxt; + + /* 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 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() { + 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 { + topLevel = new RhinoTopLevel(cx, this); + } finally { + cx.exit(); + } + + indexedProps = new HashMap(); + + //construct object used to implement getInterface + 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 ? "" : 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 { + msg = re.toString(); + } + ScriptException se = new ScriptException(msg, re.sourceName(), line); + se.initCause(re); + throw se; + } catch (IOException ee) { + throw new ScriptException(ee); + } finally { + cx.exit(); + } + + return unwrapReturnValue(ret); + } + + public Object eval(String script, ScriptContext ctxt) throws ScriptException { + if (script == null) { + throw new NullPointerException("null script"); + } + return eval(new StringReader(script), ctxt); + } + + public Bindings createBindings() { + return new SimpleBindings(); + } + + //Invocable methods + public Object invokeFunction(String name, Object... args) + throws ScriptException, NoSuchMethodException { + return invoke(null, name, args); + } + + public Object invokeMethod(Object thiz, String name, Object... args) + throws ScriptException, NoSuchMethodException { + if (thiz == null) { + throw new IllegalArgumentException("script object can not be null"); + } + return invoke(thiz, name, args); + } + + private Object invoke(Object thiz, String name, Object... args) + throws ScriptException, NoSuchMethodException { + Context cx = enterContext(); + try { + if (name == null) { + throw new NullPointerException("method name is null"); + } + + if (thiz != null && !(thiz instanceof Scriptable)) { + thiz = cx.toObject(thiz, topLevel); + } + + Scriptable engineScope = getRuntimeScope(context); + Scriptable localScope = (thiz != null) ? (Scriptable) thiz : + engineScope; + Object obj = ScriptableObject.getProperty(localScope, name); + if (!(obj instanceof Function)) { + throw new NoSuchMethodException("no such method: " + name); + } + + Function func = (Function) obj; + Scriptable scope = func.getParentScope(); + if (scope == null) { + scope = engineScope; + } + Object result = func.call(cx, scope, localScope, + wrapArguments(args)); + return unwrapReturnValue(result); + } catch (RhinoException re) { + if (DEBUG) re.printStackTrace(); + int line = (line = re.lineNumber()) == 0 ? -1 : line; + ScriptException se = new ScriptException(re.toString(), re.sourceName(), line); + se.initCause(re); + throw se; + } finally { + cx.exit(); + } + } + + public T getInterface(Class clasz) { + try { + return implementor.getInterface(null, clasz); + } catch (ScriptException e) { + return null; + } + } + + public T getInterface(Object thiz, Class 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) { + fileName = ""; + } + + Scriptable scope = getRuntimeScope(context); + Script scr = cx.compileReader(scope, script, fileName, 1, null); + ret = new RhinoCompiledScript(this, scr); + } catch (Exception e) { + if (DEBUG) e.printStackTrace(); + throw new ScriptException(e); + } finally { + cx.exit(); + } + return ret; + } + + + //package-private helpers + + static Context enterContext() { + // call this always so that initializer of this class runs + // and initializes custom wrap factory and class shutter. + return Context.enter(); + } + + AccessControlContext getAccessContext() { + return accCtxt; + } + + Object[] wrapArguments(Object[] args) { + if (args == null) { + return Context.emptyArgs; + } + 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")); + } + */ +} diff --git a/app/src/main/java/com/script/rhino/RhinoTopLevel.java b/app/src/main/java/com/script/rhino/RhinoTopLevel.java new file mode 100644 index 000000000..c09eb71dd --- /dev/null +++ b/app/src/main/java/com/script/rhino/RhinoTopLevel.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.script.rhino; + +import com.script.Bindings; +import com.script.ScriptContext; +import com.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; + +import java.security.AccessControlContext; + + +/** + * This class serves as top level scope for Rhino. This class adds + * 3 top level functions (bindings, scope, sync) and two constructors + * (JSAdapter, JavaAdapter). + * + * @author A. Sundararajan + * @since 1.6 + */ +public final class RhinoTopLevel extends ImporterTopLevel { + RhinoTopLevel(Context cx, RhinoScriptEngine engine) { + // second boolean parameter to super constructor tells whether + // 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. + 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. + */ + com.script.rhino.JavaAdapter.init(cx, this, false); + + // add top level functions + String names[] = { "bindings", "scope", "sync" }; + defineFunctionProperties(names, RhinoTopLevel.class, + ScriptableObject.DONTENUM); + } + + /** + * 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 com.script.rhino.ExternalScriptable) { + ScriptContext ctx = ((com.script.rhino.ExternalScriptable)arg).getContext(); + Bindings bind = ctx.getBindings(ScriptContext.ENGINE_SCOPE); + return Context.javaToJS(bind, + ScriptableObject.getTopLevelScope(thisObj)); + } + } + 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 this 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; +} diff --git a/app/src/main/java/com/script/rhino/RhinoWrapFactory.java b/app/src/main/java/com/script/rhino/RhinoWrapFactory.java new file mode 100644 index 000000000..5e0e7b358 --- /dev/null +++ b/app/src/main/java/com/script/rhino/RhinoWrapFactory.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.script.rhino; + +import org.mozilla.javascript.ClassShutter; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.NativeJavaObject; +import org.mozilla.javascript.Scriptable; +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 + * engine interface and JavaScript engine classes are run as bootstrap + * classes. For example, java.lang.Class.forName method (when called without + * class loader) uses caller's class loader. This may be exploited by script + * authors to access classes otherwise not accessible. For example, + * classes in sun.* namespace are normally not accessible to untrusted + * code and hence should not be accessible to JavaScript run from + * untrusted code. + * + * @author A. Sundararajan + * @since 1.6 + */ +final class RhinoWrapFactory extends WrapFactory { + private RhinoWrapFactory() {} + private static RhinoWrapFactory theInstance; + + static synchronized WrapFactory getInstance() { + if (theInstance == null) { + theInstance = new RhinoWrapFactory(); + } + return theInstance; + } + + // We use instance of this class to wrap security sensitive + // Java object. Please refer below. + private static class RhinoJavaObject extends NativeJavaObject { + RhinoJavaObject(Scriptable scope, Object obj, Class type) { + // we pass 'null' to object. NativeJavaObject uses + // 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 { + String name = null; + if (javaObject instanceof Class) { + name = ((Class)javaObject).getName(); + } else if (javaObject instanceof Member) { + Member member = (Member) javaObject; + // Check member access. Don't allow reflective access to + // 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(); + } + // 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 (!classShutter.visibleToScripts(name)) { + return null; + } else { + return super.wrapAsJavaObject(cx, scope, 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 { + // dynamicType is always a class type and never an interface. + // find an accessible super class of the dynamic type. + while (dynamicType != null) { + dynamicType = dynamicType.getSuperclass(); + name = dynamicType.getName(); + if (classShutter.visibleToScripts(name)) { + type = dynamicType; break; + } + } + // 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); + } + } +} diff --git a/app/src/main/java/io/legado/app/constant/AppConst.kt b/app/src/main/java/io/legado/app/constant/AppConst.kt index f19ad27fd..04088ecb8 100644 --- a/app/src/main/java/io/legado/app/constant/AppConst.kt +++ b/app/src/main/java/io/legado/app/constant/AppConst.kt @@ -4,7 +4,7 @@ import android.annotation.SuppressLint import android.content.pm.PackageManager import android.provider.Settings import androidx.annotation.Keep -import com.script.javascript.RhinoScriptEngine +import com.script.rhino.RhinoScriptEngine import io.legado.app.BuildConfig import io.legado.app.utils.channel import splitties.init.appCtx