mirror of
https://github.com/gedoor/legado.git
synced 2024-07-06 23:47:49 +08:00
更新到 rhino-1.7.14-1
This commit is contained in:
parent
0155358081
commit
0df2c69539
Binary file not shown.
BIN
app/lib/rhino-1.7.14-1.jar
Normal file
BIN
app/lib/rhino-1.7.14-1.jar
Normal file
Binary file not shown.
110
app/src/main/java/com/script/AbstractScriptEngine.java
Normal file
110
app/src/main/java/com/script/AbstractScriptEngine.java
Normal file
@ -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
app/src/main/java/com/script/Bindings.java
Normal file
24
app/src/main/java/com/script/Bindings.java
Normal file
@ -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
app/src/main/java/com/script/Compilable.java
Normal file
12
app/src/main/java/com/script/Compilable.java
Normal file
@ -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
app/src/main/java/com/script/CompiledScript.java
Normal file
28
app/src/main/java/com/script/CompiledScript.java
Normal file
@ -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
app/src/main/java/com/script/Invocable.java
Normal file
14
app/src/main/java/com/script/Invocable.java
Normal file
@ -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
app/src/main/java/com/script/ScriptContext.java
Normal file
41
app/src/main/java/com/script/ScriptContext.java
Normal file
@ -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
app/src/main/java/com/script/ScriptEngine.java
Normal file
36
app/src/main/java/com/script/ScriptEngine.java
Normal file
@ -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
app/src/main/java/com/script/ScriptException.java
Normal file
67
app/src/main/java/com/script/ScriptException.java
Normal file
@ -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
app/src/main/java/com/script/SimpleBindings.java
Normal file
108
app/src/main/java/com/script/SimpleBindings.java
Normal file
@ -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
app/src/main/java/com/script/SimpleScriptContext.java
Normal file
167
app/src/main/java/com/script/SimpleScriptContext.java
Normal file
@ -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
app/src/main/java/com/script/rhino/ExternalScriptable.java
Normal file
480
app/src/main/java/com/script/rhino/ExternalScriptable.java
Normal file
@ -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
app/src/main/java/com/script/rhino/InterfaceImplementor.java
Normal file
118
app/src/main/java/com/script/rhino/InterfaceImplementor.java
Normal file
@ -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
app/src/main/java/com/script/rhino/JSAdapter.java
Normal file
343
app/src/main/java/com/script/rhino/JSAdapter.java
Normal file
@ -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
app/src/main/java/com/script/rhino/JavaAdapter.java
Normal file
114
app/src/main/java/com/script/rhino/JavaAdapter.java
Normal file
@ -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
app/src/main/java/com/script/rhino/RhinoClassShutter.java
Normal file
80
app/src/main/java/com/script/rhino/RhinoClassShutter.java
Normal file
@ -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
app/src/main/java/com/script/rhino/RhinoCompiledScript.java
Normal file
88
app/src/main/java/com/script/rhino/RhinoCompiledScript.java
Normal file
@ -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
app/src/main/java/com/script/rhino/RhinoScriptEngine.java
Normal file
460
app/src/main/java/com/script/rhino/RhinoScriptEngine.java
Normal file
@ -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
app/src/main/java/com/script/rhino/RhinoTopLevel.java
Normal file
177
app/src/main/java/com/script/rhino/RhinoTopLevel.java
Normal file
@ -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
app/src/main/java/com/script/rhino/RhinoWrapFactory.java
Normal file
165
app/src/main/java/com/script/rhino/RhinoWrapFactory.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ import android.annotation.SuppressLint
|
||||
import android.content.pm.PackageManager
|
||||
import android.provider.Settings
|
||||
import androidx.annotation.Keep
|
||||
import com.script.javascript.RhinoScriptEngine
|
||||
import com.script.rhino.RhinoScriptEngine
|
||||
import io.legado.app.BuildConfig
|
||||
import io.legado.app.utils.channel
|
||||
import splitties.init.appCtx
|
||||
|
Loading…
Reference in New Issue
Block a user