Sfoglia il codice sorgente

更新到 rhino-1.7.14-1

kunfei 3 anni fa
parent
commit
671ea1ba16

BIN
app/lib/rhino-1.7.13-1.jar


BIN
app/lib/rhino-1.7.14-1.jar


+ 110 - 0
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.");
+    }
+}

+ 24 - 0
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<String, Object> {
+    @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<? extends String, ? extends Object> var1);
+
+    @Override
+    public Object remove(Object var1);
+}

+ 12 - 0
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;
+}

+ 28 - 0
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());
+    }
+}

+ 14 - 0
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> T getInterface(Class<T> var1);
+
+    public <T> T getInterface(Object var1, Class<T> var2);
+
+    public Object invokeFunction(String var1, Object ... var2) throws ScriptException, NoSuchMethodException;
+
+    public Object invokeMethod(Object var1, String var2, Object ... var3) throws ScriptException, NoSuchMethodException;
+}

+ 41 - 0
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<Integer> 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);
+}

+ 36 - 0
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);
+}

+ 67 - 0
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;
+    }
+}

+ 108 - 0
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<String, Object> map;
+
+    public SimpleBindings(Map<String, Object> m) {
+        if (m == null) {
+            throw new NullPointerException();
+        }
+        this.map = m;
+    }
+
+    public SimpleBindings() {
+        this(new HashMap<String, Object>());
+    }
+
+    @Override
+    public Object put(String name, Object value) {
+        this.checkKey(name);
+        return this.map.put(name, value);
+    }
+
+    @Override
+    public void putAll(Map<? extends String, ? extends Object> toMerge) {
+        if (toMerge == null) {
+            throw new NullPointerException("toMerge map is null");
+        }
+        for (Map.Entry<? extends String, ? extends Object> 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<Map.Entry<String, Object>> 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<String> 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<Object> 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");
+        }
+    }
+}

+ 167 - 0
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<Integer> scopes = new ArrayList<Integer>(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<Integer> getScopes() {
+        return scopes;
+    }
+
+    static {
+        scopes.add(100);
+        scopes.add(200);
+        scopes = Collections.unmodifiableList(scopes);
+    }
+}

+ 480 - 0
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<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) {
+            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. <p>
+     *
+     * 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 {
+                    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<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()];
+        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;
+        }
+    }
+}

+ 118 - 0
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<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)
+            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;
+    }
+}

+ 343 - 0
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__";
+}

+ 114 - 0
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;
+}

+ 80 - 0
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<String, Boolean> protectedClasses;
+    private static RhinoClassShutter theInstance;
+
+    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) {
+            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;
+    }
+}

+ 88 - 0
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;
+    }
+
+}

+ 460 - 0
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 <code>ScriptEngine</code> 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<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 {
+            topLevel = new RhinoTopLevel(cx, this);
+        } finally {
+            cx.exit();
+        }
+
+        indexedProps = new HashMap<Object, Object>();
+
+        //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 ? "<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 {
+                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> T getInterface(Class<T> clasz) {
+        try {
+            return implementor.getInterface(null, clasz);
+        } 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) {
+                fileName = "<Unknown Source>";
+            }
+
+            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"));
+    }
+    */
+}

+ 177 - 0
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 <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;
+}

+ 165 - 0
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);
+        }
+    }
+}

+ 1 - 1
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.content.pm.PackageManager
 import android.provider.Settings
 import android.provider.Settings
 import androidx.annotation.Keep
 import androidx.annotation.Keep
-import com.script.javascript.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