зеркало из https://github.com/mozilla/pjs.git
Introduction of ContextAction interface as a safe alternative for Context.enter/Context.exit. All usages of Context.enter/Context.exit in Rhino are replaced by
Context.call(new ContextAction() { public Object run(Context cx) { ... } }); or similar alternatives. It prevents bugs with missed Context.exit and provides room for optimization of Context association with current thread and simplify implementation of safe environment for execution of untrusted applets and scripts. In addition, Rhino now supports global SecurityController that take precedence over any per/Context controllers so the application can setup the controller once and for all.
This commit is contained in:
Родитель
92bb0cf55b
Коммит
a58e08501c
|
@ -4,6 +4,7 @@ apiClasses=\
|
|||
src/org/mozilla/javascript/ClassDefinitionException.java,\
|
||||
src/org/mozilla/javascript/ClassShutter.java,\
|
||||
src/org/mozilla/javascript/Context.java,\
|
||||
src/org/mozilla/javascript/ContextAction.java,\
|
||||
src/org/mozilla/javascript/ContextFactory.java,\
|
||||
src/org/mozilla/javascript/ContextListener.java,\
|
||||
src/org/mozilla/javascript/EcmaError.java,\
|
||||
|
|
|
@ -208,6 +208,7 @@ public class Context
|
|||
* it can be used to execute a script.
|
||||
*
|
||||
* @see #enter()
|
||||
* @see #call(ContextAction)
|
||||
*/
|
||||
public Context()
|
||||
{
|
||||
|
@ -215,6 +216,30 @@ public class Context
|
|||
optimizationLevel = codegenClass != null ? 0 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Context associated with the given factory.
|
||||
* The factory will be notified about context execution
|
||||
* or it will be used to create Context instances associated
|
||||
* with other threads.
|
||||
* <p>
|
||||
* To associate Context instances created with this constructor
|
||||
* with current execution thread
|
||||
* {@link ContextFactory#call(ContextAction)} must be used.
|
||||
* Using {@link #enter()} or {@link #exit()} methods will
|
||||
* throw an exception if the methods operate on instances
|
||||
* created with this constructor.
|
||||
*
|
||||
* @see ContextFactory#call(ContextAction)
|
||||
*/
|
||||
public Context(ContextFactory factory)
|
||||
{
|
||||
this();
|
||||
if (factory == null) {
|
||||
throw new IllegalArgumentException("Factory should not be null");
|
||||
}
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a context associated with the current thread, creating
|
||||
* one if need be.
|
||||
|
@ -239,9 +264,24 @@ public class Context
|
|||
* Context.exit();
|
||||
* }
|
||||
* </pre>
|
||||
* Instead of using <tt>enter()</tt>, <tt>exit()</tt> pair consider using
|
||||
* {@link #call(ContextAction)} which guarantees proper
|
||||
* association of Context instances with the current thread and is faster.
|
||||
* With this method the above example becomes:
|
||||
* <pre>
|
||||
* Context.call(new ContextAction() {
|
||||
* public Object run(Context cx) {
|
||||
* ...
|
||||
* cx.evaluateString(...);
|
||||
* return null;
|
||||
* }
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* @return a Context associated with the current thread
|
||||
* @see #getCurrentContext()
|
||||
* @see #exit()
|
||||
* @see #call(ContextAction)
|
||||
*/
|
||||
public static Context enter()
|
||||
{
|
||||
|
@ -258,9 +298,17 @@ public class Context
|
|||
* is not associated with any other thread.
|
||||
* @param cx a Context to associate with the thread if possible
|
||||
* @return a Context associated with the current thread
|
||||
*
|
||||
* @see #enter()
|
||||
* @see #call(ContextAction)
|
||||
* @see ContextFactory#call(ContextAction)
|
||||
*/
|
||||
public static Context enter(Context cx)
|
||||
{
|
||||
if (cx != null && cx.factory != null) {
|
||||
throw new IllegalStateException("Context.enter can not be used to associate with the execution thread Context instances created with Context(ContextFactory) constructor");
|
||||
}
|
||||
|
||||
Context[] storage = getThreadContextStorage();
|
||||
Context old;
|
||||
if (storage != null) {
|
||||
|
@ -270,6 +318,12 @@ public class Context
|
|||
}
|
||||
|
||||
if (old != null) {
|
||||
if (old.factory != null) {
|
||||
throw new IllegalStateException("Context.enter can not be used to recursively enter Context instances created with Context(ContextFactory) constructor");
|
||||
}
|
||||
if (old.enteredUsingCall) {
|
||||
throw new IllegalStateException("Context.enter can not be used to recursively enter Context instances already associated with the current thread using Context.call(ContextAction)");
|
||||
}
|
||||
if (cx != null && cx != old && cx.enterCount != 0) {
|
||||
// The suplied context must be the context for
|
||||
// the current thread if it is already entered
|
||||
|
@ -317,7 +371,9 @@ public class Context
|
|||
* it cannot be used to execute JavaScript until it is again associated
|
||||
* with a Context.
|
||||
*
|
||||
* @see org.mozilla.javascript.Context#enter
|
||||
* @see org.mozilla.javascript.Context#enter()
|
||||
* @see #call(ContextAction)
|
||||
* @see ContextFactory#call(ContextAction)
|
||||
*/
|
||||
public static void exit()
|
||||
{
|
||||
|
@ -332,6 +388,12 @@ public class Context
|
|||
throw new IllegalStateException(
|
||||
"Calling Context.exit without previous Context.enter");
|
||||
}
|
||||
if (cx.factory != null) {
|
||||
throw new IllegalStateException("Context.exit can not be used to exit context created with Context(ContextFactory) constructor");
|
||||
}
|
||||
if (cx.enteredUsingCall) {
|
||||
throw new IllegalStateException("Context.exit can not be used to exit context associated with the current thread using Context.call(ContextAction)");
|
||||
}
|
||||
if (Context.check && cx.enterCount < 1) Kit.codeBug();
|
||||
if (cx.sealed) onSealedMutation();
|
||||
--cx.enterCount;
|
||||
|
@ -350,12 +412,30 @@ public class Context
|
|||
}
|
||||
|
||||
/**
|
||||
* Return {@link ContextFactory} instance to use to create Context instances
|
||||
* for other execution threads.
|
||||
* Call {@link ContextAction#run(Context cx)}
|
||||
* using the Context instance associated with the current thread.
|
||||
* If no Context is associated with the thread, then
|
||||
* <tt>new Context()</tt> will be called to construct
|
||||
* new Context instance. The instance will be temporary associated
|
||||
* with the thread during call to {@link ContextAction#run(Context)}.
|
||||
*/
|
||||
protected ContextFactory factory()
|
||||
public static Object call(ContextAction action)
|
||||
{
|
||||
return ContextFactory.getDefault();
|
||||
return call(null, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use
|
||||
* {@link #call(ContextFactory factory, Callable callable,
|
||||
* Scriptable scope, Scriptable thisObj, Object[] args)}
|
||||
* instead to provide explicit factory to create Context if no Context
|
||||
* is associated with the current thread.
|
||||
*/
|
||||
public static Object call(Callable callable, Scriptable scope,
|
||||
Scriptable thisObj, Object[] args)
|
||||
throws JavaScriptException
|
||||
{
|
||||
return call(null, callable, scope, thisObj, args);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -363,23 +443,20 @@ public class Context
|
|||
* Callable#call(Context cx, Scriptable scope, Scriptable thisObj,
|
||||
* Object[] args)}
|
||||
* using the Context instance associated with the current thread.
|
||||
* If no Context is associated with the thread, then new Context object
|
||||
* will be temporary associated with the thread during call to
|
||||
* {@link Callable}.
|
||||
* If no Context is associated with the thread, then
|
||||
* {@link ContextFactory#newContext()} will be called to construct
|
||||
* new Context instance. The instance will be temporary associated
|
||||
* with the thread during call to {@link ContextAction#run(Context)}.
|
||||
* <p>
|
||||
* It is allowed to use null for <tt>factory</tt> argument
|
||||
* in which case <tt>new Context()</tt> will be used to create
|
||||
* new context instances.
|
||||
*
|
||||
* @see #enter()
|
||||
* @see #exit()
|
||||
* @see ContextFactory#call(ContextAction)
|
||||
*/
|
||||
public static Object call(Callable callable, Scriptable scope,
|
||||
Scriptable thisObj, Object[] args)
|
||||
throws JavaScriptException
|
||||
{
|
||||
return call(ContextFactory.getDefault(), callable, scope, thisObj,
|
||||
args);
|
||||
}
|
||||
|
||||
static Object call(ContextFactory f, Callable callable, Scriptable scope,
|
||||
Scriptable thisObj, Object[] args)
|
||||
public static Object call(ContextFactory factory, Callable callable,
|
||||
Scriptable scope, Scriptable thisObj,
|
||||
Object[] args)
|
||||
throws JavaScriptException
|
||||
{
|
||||
Context[] storage = getThreadContextStorage();
|
||||
|
@ -391,42 +468,117 @@ public class Context
|
|||
}
|
||||
|
||||
if (cx != null) {
|
||||
if (cx.enteredUsingCall) {
|
||||
return callable.call(cx, scope, thisObj, args);
|
||||
} else {
|
||||
cx.enteredUsingCall = true;
|
||||
try {
|
||||
return callable.call(cx, scope, thisObj, args);
|
||||
} finally {
|
||||
cx.enteredUsingCall = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cx = f.newContext();
|
||||
cx = prepareNewContext(factory, storage);
|
||||
try {
|
||||
return callable.call(cx, scope, thisObj, args);
|
||||
} finally {
|
||||
releaseContext(storage, cx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The method implements {@links ContextFactory#call(ContextAction)} logic.
|
||||
*/
|
||||
static Object call(ContextFactory factory, ContextAction action)
|
||||
throws JavaScriptException
|
||||
{
|
||||
Context[] storage = getThreadContextStorage();
|
||||
Context cx;
|
||||
if (storage != null) {
|
||||
cx = storage[0];
|
||||
} else {
|
||||
cx = getCurrentContext_jdk11();
|
||||
}
|
||||
|
||||
if (cx != null) {
|
||||
if (cx.enteredUsingCall) {
|
||||
return action.run(cx);
|
||||
} else {
|
||||
cx.enteredUsingCall = true;
|
||||
try {
|
||||
return action.run(cx);
|
||||
} finally {
|
||||
cx.enteredUsingCall = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cx = prepareNewContext(factory, storage);
|
||||
try {
|
||||
return action.run(cx);
|
||||
} finally {
|
||||
releaseContext(storage, cx);
|
||||
}
|
||||
}
|
||||
|
||||
private static Context prepareNewContext(ContextFactory factory,
|
||||
Context[] storage)
|
||||
{
|
||||
Context cx;
|
||||
if (factory == null) {
|
||||
cx = new Context();
|
||||
if (!cx.creationEventWasSent) {
|
||||
cx.creationEventWasSent = true;
|
||||
cx.runListeners(CONTEXT_CREATED_EVENT);
|
||||
}
|
||||
cx.runListeners(CONTEXT_ENTER_EVENT);
|
||||
} else {
|
||||
cx = factory.newContext();
|
||||
if (cx.factory != factory) {
|
||||
throw new IllegalStateException("factory.newContext() did not use proper Context constructor");
|
||||
}
|
||||
if (cx.enterCount != 0) { throw new IllegalStateException(); }
|
||||
}
|
||||
|
||||
if (storage != null) {
|
||||
storage[0] = cx;
|
||||
} else {
|
||||
setThreadContext_jdk11(cx);
|
||||
}
|
||||
++cx.enterCount;
|
||||
try {
|
||||
return callable.call(cx, scope, thisObj, args);
|
||||
} finally {
|
||||
--cx.enterCount;
|
||||
if (cx.enterCount == 0) {
|
||||
cx.enterCount = 1;
|
||||
cx.enteredUsingCall = true;
|
||||
return cx;
|
||||
}
|
||||
|
||||
private static void releaseContext(Context[] storage, Context cx)
|
||||
{
|
||||
if (cx.enterCount != 1) throw new IllegalStateException();
|
||||
cx.enteredUsingCall = false;
|
||||
cx.enterCount = 0;
|
||||
if (storage != null) {
|
||||
storage[0] = null;
|
||||
} else {
|
||||
setThreadContext_jdk11(null);
|
||||
}
|
||||
}
|
||||
if (cx.factory == null) {
|
||||
cx.runListeners(CONTEXT_EXIT_EVENT);
|
||||
if (cx.enterCount == 0) {
|
||||
cx.runListeners(CONTEXT_RELEASED_EVENT);
|
||||
}
|
||||
} else {
|
||||
cx.factory.onContextExit(cx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Context listener.
|
||||
* Add a Context listener. The listener will receive notifications about
|
||||
* Context events for Context instances created with the default
|
||||
* constructor {@link #Context()}. Rhino runtime never notifies listeners
|
||||
* added with this method for Context instances created with factory
|
||||
* constructor {@link #Context(ContextFactory)}.
|
||||
*
|
||||
* @see #removeContextListener(ContextListener listener)
|
||||
* @see #disableStaticContextListening()
|
||||
*/
|
||||
public static void addContextListener(ContextListener listener)
|
||||
{
|
||||
|
@ -443,6 +595,9 @@ public class Context
|
|||
/**
|
||||
* Remove a Context listener.
|
||||
* @param listener the listener to remove.
|
||||
*
|
||||
* @see #addContextListener(ContextListener listener)
|
||||
* @see #disableStaticContextListening()
|
||||
*/
|
||||
public static void removeContextListener(ContextListener listener)
|
||||
{
|
||||
|
@ -555,6 +710,14 @@ public class Context
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@link ContextFactory} instance used to create this Context.
|
||||
*/
|
||||
public ContextFactory getFactory()
|
||||
{
|
||||
return factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this is a sealed Context. A sealed Context instance does not
|
||||
* allow to modify any of its properties and will throw an exception
|
||||
|
@ -1782,11 +1945,14 @@ public class Context
|
|||
|
||||
/**
|
||||
* Set the security controller for this context.
|
||||
* <p> SecurityController may only be set if it is currently null.
|
||||
* <p> SecurityController may only be set if it is currently null
|
||||
* and {@link #hasGlobalSecurityController()} is <tt>false</tt>.
|
||||
* Otherwise a SecurityException is thrown.
|
||||
* @param controller a SecurityController object
|
||||
* @throws SecurityException if there is already a SecurityController
|
||||
* object for this Context
|
||||
* @see #setGlobalSecurityController(SecurityController controller)
|
||||
* @see #hasGlobalSecurityController()
|
||||
*/
|
||||
public final void setSecurityController(SecurityController controller)
|
||||
{
|
||||
|
@ -1796,9 +1962,38 @@ public class Context
|
|||
throw new SecurityException("Can not overwrite existing " +
|
||||
"SecurityController object");
|
||||
}
|
||||
if (globalSecurityController != null) {
|
||||
throw new SecurityException("Can not overwrite existing " +
|
||||
"global SecurityController object");
|
||||
}
|
||||
securityController = controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if global {@link SecurityController} was already installed.
|
||||
* @see #setGlobalSecurityController(SecurityController controller)
|
||||
*/
|
||||
public static boolean hasGlobalSecurityController()
|
||||
{
|
||||
return globalSecurityController != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set global {@link SecurityController} that will be used for all
|
||||
* security-related operations overriding any per Context security
|
||||
* settings. The method can only be called once.
|
||||
* @see #hasGlobalSecurityController()
|
||||
*/
|
||||
public final void setGlobalSecurityController(SecurityController controller)
|
||||
{
|
||||
if (controller == null) throw new IllegalArgumentException();
|
||||
if (globalSecurityController != null) {
|
||||
throw new SecurityException("Cannot overwrite existing " +
|
||||
"SecurityController object");
|
||||
}
|
||||
globalSecurityController = controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the LiveConnect access filter for this context.
|
||||
* <p> {@link ClassShutter} may only be set if it is currently null.
|
||||
|
@ -2441,6 +2636,9 @@ public class Context
|
|||
// Should not be public
|
||||
SecurityController getSecurityController()
|
||||
{
|
||||
if (globalSecurityController != null) {
|
||||
return globalSecurityController;
|
||||
}
|
||||
return securityController;
|
||||
}
|
||||
|
||||
|
@ -2530,6 +2728,7 @@ public class Context
|
|||
private static volatile Object staticListeners;
|
||||
private static boolean disabledContextListening;
|
||||
|
||||
private ContextFactory factory;
|
||||
private boolean sealed;
|
||||
private Object sealKey;
|
||||
|
||||
|
@ -2547,6 +2746,7 @@ public class Context
|
|||
|
||||
int version;
|
||||
|
||||
private static SecurityController globalSecurityController;
|
||||
private SecurityController securityController;
|
||||
private ClassShutter classShutter;
|
||||
private ErrorReporter errorReporter;
|
||||
|
@ -2561,6 +2761,7 @@ public class Context
|
|||
Debugger debugger;
|
||||
private Object debuggerData;
|
||||
private int enterCount;
|
||||
private boolean enteredUsingCall;
|
||||
private Object propertyListeners;
|
||||
private Hashtable hashtable;
|
||||
private ClassLoader applicationClassLoader;
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is an interface abstraction for code requiring Context
|
||||
* instances.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* RUnit Software AS.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2004
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s): Igor Bukanov, igor@fastmail.fm
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
// API class
|
||||
|
||||
package org.mozilla.javascript;
|
||||
|
||||
/**
|
||||
* Interface to represent arbitrary action that requires to have Context
|
||||
* object associated with the current thread for its execution.
|
||||
*/
|
||||
public interface ContextAction
|
||||
{
|
||||
/**
|
||||
* Execute action using the supplied Context instance.
|
||||
* When Rhino runtime calls the method, <tt>cx</tt> will be associated
|
||||
* with the current thread as active context.
|
||||
*
|
||||
* @see Context#call(ContextAction)
|
||||
* @see ContextFactory#call(ContextAction)
|
||||
*/
|
||||
public Object run(Context cx);
|
||||
}
|
||||
|
|
@ -40,26 +40,53 @@
|
|||
|
||||
package org.mozilla.javascript;
|
||||
|
||||
|
||||
public class ContextFactory
|
||||
{
|
||||
/**
|
||||
* Create new {@link Context} instance to be associated with the current
|
||||
* thread.
|
||||
* This is a callback method used by Rhino to create {@link Context}
|
||||
* instance when it is necessary to associate one with the current
|
||||
* execution thread. <tt>newContext()</tt> is allowed to call
|
||||
* {@link Context#seal(Object)} on the result to prevent
|
||||
* {@link Context} changes by hostile scripts or applets.
|
||||
* <p>
|
||||
* The method must call {@link Context#Context(ContextFactory)}
|
||||
* constructor using <tt>this</tt> as ContextFactory argument
|
||||
* when creating Context instances or its subclasses.
|
||||
*
|
||||
*/
|
||||
protected Context newContext()
|
||||
{
|
||||
return new Context();
|
||||
return new Context(this);
|
||||
}
|
||||
|
||||
public final Object call(Callable callable, Scriptable scope,
|
||||
Scriptable thisObj, Object[] args)
|
||||
/**
|
||||
* Perform cleanup action for {@link Context} instance.
|
||||
* Rhino runtime calls the method to notify that {@link Context}
|
||||
* instance created with {@link #newContext()}
|
||||
* is no longer associated with the current thread.
|
||||
*/
|
||||
protected void onContextExit(Context cx)
|
||||
{
|
||||
return Context.call(this, callable, scope, thisObj, args);
|
||||
}
|
||||
|
||||
public static ContextFactory getDefault()
|
||||
/**
|
||||
* Call {@link ContextAction#run(Context cx)}
|
||||
* using the {@link Context} instance associated with the current thread.
|
||||
* If no Context is associated with the thread, then
|
||||
* {@link #newContext()} will be called to construct
|
||||
* new Context instance. The instance will be temporary associated
|
||||
* with the thread during call to {@link ContextAction#run(Context)}.
|
||||
*
|
||||
* @see ContextFactory#call(ContextAction)
|
||||
* @see Context#call(ContextFactory factory, Callable callable,
|
||||
* Scriptable scope, Scriptable thisObj,
|
||||
* Object[] args)
|
||||
*/
|
||||
public final Object call(ContextAction action)
|
||||
{
|
||||
return defaultFactory;
|
||||
}
|
||||
|
||||
private static ContextFactory defaultFactory = new ContextFactory();
|
||||
|
||||
return Context.call(this, action);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ public final class JavaAdapter
|
|||
{
|
||||
Scriptable scope = function.getParentScope();
|
||||
Scriptable thisObj = scope;
|
||||
return contextFactory.call(this, scope, thisObj, args);
|
||||
return Context.call(contextFactory, this, scope, thisObj, args);
|
||||
}
|
||||
|
||||
public Object call(Context cx, Scriptable scope, Scriptable thisObj,
|
||||
|
@ -604,15 +604,16 @@ public final class JavaAdapter
|
|||
|
||||
public static ContextFactory currentFactory()
|
||||
{
|
||||
return Context.getContext().factory();
|
||||
return Context.getContext().getFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method which dynamically binds a Context to the current thread,
|
||||
* if none already exists.
|
||||
*/
|
||||
public static Object callMethod(Scriptable scope, Scriptable thisObj,
|
||||
Function f, Object[] args, long argsToWrap,
|
||||
public static Object callMethod(Scriptable scope, final Scriptable thisObj,
|
||||
final Function f, final Object[] args,
|
||||
final long argsToWrap,
|
||||
ContextFactory factory)
|
||||
{
|
||||
if (f == null) {
|
||||
|
@ -620,16 +621,21 @@ public final class JavaAdapter
|
|||
return Undefined.instance;
|
||||
}
|
||||
scope = ScriptableObject.getTopLevelScope(scope);
|
||||
if (argsToWrap == 0) {
|
||||
return Context.call(factory, f, scope, thisObj, args);
|
||||
}
|
||||
|
||||
Context cx = Context.getCurrentContext();
|
||||
if (cx != null) {
|
||||
return doCall(cx, scope, thisObj, f, args, argsToWrap);
|
||||
} else {
|
||||
cx = Context.enter(factory.newContext());
|
||||
try {
|
||||
return doCall(cx, scope, thisObj, f, args, argsToWrap);
|
||||
} finally {
|
||||
Context.exit();
|
||||
final Scriptable finalScope = scope;
|
||||
return factory.call(new ContextAction() {
|
||||
public Object run(Context cx)
|
||||
{
|
||||
return doCall(cx, finalScope, thisObj, f, args, argsToWrap);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -650,6 +656,18 @@ public final class JavaAdapter
|
|||
return f.call(cx, scope, thisObj, args);
|
||||
}
|
||||
|
||||
public static Scriptable runScript(final Script script)
|
||||
{
|
||||
return (Scriptable)Context.call(new ContextAction() {
|
||||
public Object run(Context cx)
|
||||
{
|
||||
ScriptableObject global = ScriptRuntime.getGlobal(cx);
|
||||
script.exec(cx, global);
|
||||
return global;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void generateCtor(ClassFileWriter cfw, String adapterName,
|
||||
String superName)
|
||||
{
|
||||
|
@ -736,7 +754,7 @@ public final class JavaAdapter
|
|||
|
||||
// Run script and save resulting scope
|
||||
cfw.addInvoke(ByteCode.INVOKESTATIC,
|
||||
"org/mozilla/javascript/ScriptRuntime",
|
||||
"org/mozilla/javascript/JavaAdapter",
|
||||
"runScript",
|
||||
"(Lorg/mozilla/javascript/Script;"
|
||||
+")Lorg/mozilla/javascript/Scriptable;");
|
||||
|
|
|
@ -2092,7 +2092,7 @@ public class ScriptRuntime {
|
|||
// Statements
|
||||
// ------------------
|
||||
|
||||
private static ScriptableObject getGlobal(Context cx) {
|
||||
public static ScriptableObject getGlobal(Context cx) {
|
||||
final String GLOBAL_CLASS = "org.mozilla.javascript.tools.shell.Global";
|
||||
Class globalClass = Kit.classOrNull(GLOBAL_CLASS);
|
||||
if (globalClass != null) {
|
||||
|
@ -2114,26 +2114,6 @@ public class ScriptRuntime {
|
|||
return new ImporterTopLevel(cx);
|
||||
}
|
||||
|
||||
public static void main(Script script, String[] args)
|
||||
throws JavaScriptException
|
||||
{
|
||||
Context cx = Context.enter();
|
||||
try {
|
||||
ScriptableObject global = getGlobal(cx);
|
||||
|
||||
// get the command line arguments and define "arguments"
|
||||
// array in the top-level object
|
||||
Object[] argsCopy = new Object[args.length];
|
||||
System.arraycopy(args, 0, argsCopy, 0, args.length);
|
||||
Scriptable argsObj = cx.newArray(global, argsCopy);
|
||||
global.defineProperty("arguments", argsObj,
|
||||
ScriptableObject.DONTENUM);
|
||||
script.exec(cx, global);
|
||||
} finally {
|
||||
Context.exit();
|
||||
}
|
||||
}
|
||||
|
||||
public static void initScript(Context cx, final Scriptable scope,
|
||||
NativeFunction funObj,
|
||||
Scriptable thisObj,
|
||||
|
@ -2168,17 +2148,6 @@ public class ScriptRuntime {
|
|||
}
|
||||
}
|
||||
|
||||
public static Scriptable runScript(Script script) {
|
||||
Context cx = Context.enter();
|
||||
ScriptableObject global = getGlobal(cx);
|
||||
try {
|
||||
script.exec(cx, global);
|
||||
} finally {
|
||||
Context.exit();
|
||||
}
|
||||
return global;
|
||||
}
|
||||
|
||||
public static Scriptable initVarObj(Context cx, Scriptable scope,
|
||||
NativeFunction funObj,
|
||||
Scriptable thisObj, Object[] args)
|
||||
|
|
|
@ -1505,15 +1505,32 @@ public abstract class ScriptableObject implements Scriptable, Serializable,
|
|||
|
||||
/**
|
||||
* Call a method of an object.
|
||||
* <p>
|
||||
* The method requires to have active Context associated with
|
||||
* the current thread.
|
||||
* @param obj the JavaScript object
|
||||
* @param methodName the name of the function property
|
||||
* @param args the arguments for the call
|
||||
* @exception JavaScriptException thrown if there were errors in the call
|
||||
*
|
||||
* @see Context#getCurrentContext()
|
||||
*/
|
||||
public static Object callMethod(Scriptable obj, String methodName,
|
||||
Object[] args)
|
||||
throws JavaScriptException
|
||||
{
|
||||
return callMethod(Context.getContext(), obj, methodName, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a method of an object.
|
||||
* The method requires to have active Context associated with
|
||||
* the current thread.
|
||||
* @param cx the Context object associated with the current thread.
|
||||
* @param obj the JavaScript object
|
||||
* @param methodName the name of the function property
|
||||
* @param args the arguments for the call
|
||||
*/
|
||||
public static Object callMethod(Context cx, Scriptable obj,
|
||||
String methodName,
|
||||
Object[] args)
|
||||
{
|
||||
Object funObj = getProperty(obj, methodName);
|
||||
if (!(funObj instanceof Function)) {
|
||||
|
@ -1522,7 +1539,7 @@ public abstract class ScriptableObject implements Scriptable, Serializable,
|
|||
ScriptRuntime.toString(obj)+'.'+methodName);
|
||||
}
|
||||
Function fun = (Function)funObj;
|
||||
return Context.call(fun, getTopLevelScope(obj), obj, args);
|
||||
return fun.call(cx, getTopLevelScope(obj), obj, args);
|
||||
}
|
||||
|
||||
private static Scriptable getBase(Scriptable obj, String name) {
|
||||
|
|
|
@ -459,7 +459,7 @@ public class Codegen extends Interpreter
|
|||
// load 'args'
|
||||
cfw.add(ByteCode.ALOAD_0);
|
||||
cfw.addInvoke(ByteCode.INVOKESTATIC,
|
||||
"org/mozilla/javascript/ScriptRuntime",
|
||||
"org/mozilla/javascript/optimizer/OptRuntime",
|
||||
"main",
|
||||
"(Lorg/mozilla/javascript/Script;[Ljava/lang/String;)V");
|
||||
cfw.add(ByteCode.RETURN);
|
||||
|
|
|
@ -257,4 +257,25 @@ public final class OptRuntime extends ScriptRuntime
|
|||
return newArrayLiteral(objects, skipIndexces, cx, scope);
|
||||
}
|
||||
|
||||
public static void main(final Script script, final String[] args)
|
||||
throws JavaScriptException
|
||||
{
|
||||
Context.call(new ContextAction() {
|
||||
public Object run(Context cx)
|
||||
{
|
||||
ScriptableObject global = getGlobal(cx);
|
||||
|
||||
// get the command line arguments and define "arguments"
|
||||
// array in the top-level object
|
||||
Object[] argsCopy = new Object[args.length];
|
||||
System.arraycopy(args, 0, argsCopy, 0, args.length);
|
||||
Scriptable argsObj = cx.newArray(global, argsCopy);
|
||||
global.defineProperty("arguments", argsObj,
|
||||
ScriptableObject.DONTENUM);
|
||||
script.exec(cx, global);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -706,20 +706,27 @@ class Dim {
|
|||
|
||||
/* end Debugger interface */
|
||||
|
||||
void compileScript(String url, String text)
|
||||
private Object withContext(ContextAction action)
|
||||
{
|
||||
Context cx = Context.enter();
|
||||
try {
|
||||
cx.compileString(text, url, 1, null);
|
||||
} finally {
|
||||
Context.exit();
|
||||
}
|
||||
return Context.call(action);
|
||||
}
|
||||
|
||||
void evalScript(String url, String text)
|
||||
void compileScript(final String url, final String text)
|
||||
{
|
||||
withContext(new ContextAction() {
|
||||
public Object run(Context cx)
|
||||
{
|
||||
cx.compileString(text, url, 1, null);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void evalScript(final String url, final String text)
|
||||
{
|
||||
withContext(new ContextAction() {
|
||||
public Object run(Context cx)
|
||||
{
|
||||
Context cx = Context.enter();
|
||||
try {
|
||||
Scriptable scope = null;
|
||||
if (scopeProvider != null) {
|
||||
scope = scopeProvider.getScope();
|
||||
|
@ -728,9 +735,9 @@ class Dim {
|
|||
scope = new ImporterTopLevel(cx);
|
||||
}
|
||||
cx.evaluateString(scope, text, url, 1, null);
|
||||
} finally {
|
||||
Context.exit();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void contextSwitch (int frameIndex) {
|
||||
|
@ -887,14 +894,19 @@ class Dim {
|
|||
}
|
||||
}
|
||||
|
||||
boolean stringIsCompilableUnit(String expr) {
|
||||
Context cx = Context.enter();
|
||||
boolean result = cx.stringIsCompilableUnit(expr);
|
||||
cx.exit();
|
||||
return result;
|
||||
boolean stringIsCompilableUnit(final String expr)
|
||||
{
|
||||
Boolean result = (Boolean)withContext(new ContextAction() {
|
||||
public Object run(Context cx)
|
||||
{
|
||||
return cx.stringIsCompilableUnit(expr)
|
||||
? Boolean.TRUE : Boolean.FALSE;
|
||||
}
|
||||
});
|
||||
return result.booleanValue();
|
||||
}
|
||||
|
||||
String objectToString(Object object)
|
||||
String objectToString(final Object object)
|
||||
{
|
||||
if (object == Undefined.instance) {
|
||||
return "undefined";
|
||||
|
@ -905,12 +917,13 @@ class Dim {
|
|||
if (object instanceof NativeCall) {
|
||||
return "[object Call]";
|
||||
}
|
||||
Context cx = Context.enter();
|
||||
try {
|
||||
String result = (String)withContext(new ContextAction() {
|
||||
public Object run(Context cx)
|
||||
{
|
||||
return Context.toString(object);
|
||||
} finally {
|
||||
Context.exit();
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
Object getObjectProperty(Object object, Object id)
|
||||
|
@ -946,18 +959,17 @@ class Dim {
|
|||
if (!(object instanceof Scriptable) || object == Undefined.instance) {
|
||||
return Context.emptyArgs;
|
||||
}
|
||||
Scriptable scriptable = (Scriptable)object;
|
||||
Object[] ids;
|
||||
Context cx = Context.enter();
|
||||
try {
|
||||
final Scriptable scriptable = (Scriptable)object;
|
||||
Object[] ids = (Object[])withContext(new ContextAction() {
|
||||
public Object run(Context cx)
|
||||
{
|
||||
if (scriptable instanceof DebuggableObject) {
|
||||
ids = ((DebuggableObject)scriptable).getAllIds();
|
||||
return ((DebuggableObject)scriptable).getAllIds();
|
||||
} else {
|
||||
ids = scriptable.getIds();
|
||||
return scriptable.getIds();
|
||||
}
|
||||
} finally {
|
||||
Context.exit();
|
||||
}
|
||||
});
|
||||
Scriptable proto = scriptable.getPrototype();
|
||||
Scriptable parent = scriptable.getParentScope();
|
||||
int extra = 0;
|
||||
|
|
|
@ -922,7 +922,7 @@ public class Global extends ImporterTopLevel {
|
|||
}
|
||||
|
||||
|
||||
class Runner implements Runnable {
|
||||
class Runner implements Runnable, ContextAction {
|
||||
|
||||
Runner(Scriptable scope, Function func, Object[] args) {
|
||||
this.scope = scope;
|
||||
|
@ -935,16 +935,17 @@ class Runner implements Runnable {
|
|||
s = script;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
Context cx = Main.enterContext();
|
||||
try {
|
||||
if (f != null)
|
||||
f.call(cx, scope, scope, args);
|
||||
else
|
||||
s.exec(cx, scope);
|
||||
} finally {
|
||||
Context.exit();
|
||||
public void run()
|
||||
{
|
||||
Main.withContext(this);
|
||||
}
|
||||
|
||||
public Object run(Context cx)
|
||||
{
|
||||
if (f != null)
|
||||
return f.call(cx, scope, scope, args);
|
||||
else
|
||||
return s.exec(cx, scope);
|
||||
}
|
||||
|
||||
private Scriptable scope;
|
||||
|
|
|
@ -83,23 +83,25 @@ public class Main {
|
|||
/**
|
||||
* Execute the given arguments, but don't System.exit at the end.
|
||||
*/
|
||||
public static int exec(String args[]) {
|
||||
public static int exec(final String origArgs[]) {
|
||||
|
||||
for (int i=0; i < args.length; i++) {
|
||||
String arg = args[i];
|
||||
for (int i=0; i < origArgs.length; i++) {
|
||||
String arg = origArgs[i];
|
||||
if (arg.equals("-sealedlib")) {
|
||||
sealedStdLib = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Context cx = enterContext();
|
||||
withContext(new ContextAction() {
|
||||
public Object run(Context cx)
|
||||
{
|
||||
// Create the top-level scope object.
|
||||
global = getGlobal();
|
||||
errorReporter = new ToolErrorReporter(false, global.getErr());
|
||||
cx.setErrorReporter(errorReporter);
|
||||
|
||||
args = processOptions(cx, args);
|
||||
String[] args = processOptions(cx, origArgs);
|
||||
|
||||
if (processStdin)
|
||||
fileList.addElement(null);
|
||||
|
@ -117,28 +119,40 @@ public class Main {
|
|||
processSource(cx, (String) fileList.elementAt(i));
|
||||
}
|
||||
|
||||
cx.exit();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
public static Global getGlobal() {
|
||||
if (global == null) {
|
||||
try {
|
||||
global = new Global(enterContext());
|
||||
}
|
||||
finally {
|
||||
Context.exit();
|
||||
withContext(new ContextAction() {
|
||||
public Object run(Context cx)
|
||||
{
|
||||
global = new Global(cx);
|
||||
return global;
|
||||
}
|
||||
});
|
||||
}
|
||||
return global;
|
||||
}
|
||||
|
||||
static Context enterContext() {
|
||||
Context cx = new Context();
|
||||
if (securityImpl != null) {
|
||||
static Object withContext(final ContextAction action) {
|
||||
ContextAction wrap;
|
||||
if (securityImpl == null) {
|
||||
wrap = action;
|
||||
} else {
|
||||
wrap = new ContextAction() {
|
||||
public Object run(Context cx)
|
||||
{
|
||||
cx.setSecurityController(securityImpl);
|
||||
return action.run(cx);
|
||||
}
|
||||
return Context.enter(cx);
|
||||
};
|
||||
}
|
||||
return Context.call(action);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Загрузка…
Ссылка в новой задаче