diff --git a/js/rhino/src/org/mozilla/javascript/Context.java b/js/rhino/src/org/mozilla/javascript/Context.java index 51e3c5afb87..124e83ee4c1 100644 --- a/js/rhino/src/org/mozilla/javascript/Context.java +++ b/js/rhino/src/org/mozilla/javascript/Context.java @@ -86,7 +86,7 @@ import org.mozilla.javascript.debug.*; public class Context { /** - * Language versions + * Language versions. * * All integral values are reserved for future version numbers. */ @@ -217,30 +217,6 @@ 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. - *

- * 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. @@ -306,10 +282,6 @@ public class Context */ 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) { @@ -320,9 +292,6 @@ 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) { @@ -335,20 +304,20 @@ public class Context cx = old; } else { if (cx == null) { - cx = new Context(); + cx = ContextFactory.getGlobal().makeContext(); } else { if (cx.sealed) onSealedMutation(); } - if (cx.enterCount != 0) Kit.codeBug(); + if (cx.enterCount != 0 || cx.factory != null) { + throw new IllegalStateException(); + } if (!cx.creationEventWasSent) { cx.creationEventWasSent = true; - cx.runListeners(CONTEXT_CREATED_EVENT); + ContextFactory.getGlobal().onContextCreated(cx); } } - cx.runListeners(CONTEXT_ENTER_EVENT); - if (old == null) { if (storage != null) { storage[0] = cx; @@ -390,12 +359,9 @@ public class Context "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.enterCount < 1) Kit.codeBug(); if (cx.sealed) onSealedMutation(); --cx.enterCount; if (cx.enterCount == 0) { @@ -406,9 +372,8 @@ public class Context } } - cx.runListeners(CONTEXT_EXIT_EVENT); if (cx.enterCount == 0) { - cx.runListeners(CONTEXT_RELEASED_EVENT); + ContextFactory.getGlobal().onContextReleased(cx); } } @@ -416,15 +381,16 @@ public class Context * Call {@link ContextAction#run(Context cx)} * using the Context instance associated with the current thread. * If no Context is associated with the thread, then - * new Context() will be called to construct - * new Context instance. The instance will be temporary associated - * with the thread during call to {@link ContextAction#run(Context)}. + * ContextFactory.getGlobal().makeContext() will be called to + * construct new Context instance. The instance will be temporary + * associated with the thread during call to + * {@link ContextAction#run(Context)}. * * @return The result of {@link ContextAction#run(Context)}. */ public static Object call(ContextAction action) { - return call(null, action); + return call(ContextFactory.getGlobal(), action); } /** @@ -438,8 +404,8 @@ public class Context * with the thread during call to {@link ContextAction#run(Context)}. *

* It is allowed to use null for factory argument - * in which case new Context() will be used to create - * new context instances. + * in which case ContextFactory.getGlobal().makeContext() will be + * used to create new context instances. * * @see ContextFactory#call(ContextAction) */ @@ -448,6 +414,9 @@ public class Context Object[] args) throws JavaScriptException { + if (factory == null) { + factory = ContextFactory.getGlobal(); + } Context[] storage = getThreadContextStorage(); Context cx; if (storage != null) { @@ -457,14 +426,14 @@ public class Context } if (cx != null) { - if (cx.enteredUsingCall) { + if (cx.factory != null) { return callable.call(cx, scope, thisObj, args); } else { - cx.enteredUsingCall = true; + cx.factory = factory; try { return callable.call(cx, scope, thisObj, args); } finally { - cx.enteredUsingCall = false; + cx.factory = null; } } } @@ -492,14 +461,14 @@ public class Context } if (cx != null) { - if (cx.enteredUsingCall) { + if (cx.factory != null) { return action.run(cx); } else { - cx.enteredUsingCall = true; + cx.factory = factory; try { return action.run(cx); } finally { - cx.enteredUsingCall = false; + cx.factory = null; } } } @@ -515,134 +484,69 @@ public class Context 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.makeContext(); - if (cx.factory != factory) { - throw new IllegalStateException("factory.makeContext() did not use proper Context constructor"); - } - if (cx.enterCount != 0) { throw new IllegalStateException(); } + Context cx = factory.makeContext(); + if (cx.factory != null || cx.enterCount != 0) { + throw new IllegalStateException("factory.makeContext() returned Context instance already associated with some thread"); + } + cx.factory = factory; + factory.onContextCreated(cx); + if (factory.isSealed() && !cx.isSealed()) { + cx.seal(null); } - if (storage != null) { storage[0] = cx; } else { setThreadContext_jdk11(cx); } - 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); - cx.runListeners(CONTEXT_RELEASED_EVENT); - } else { - cx.factory.onContextExit(cx); + try { + cx.factory.onContextReleased(cx); + } finally { + cx.factory = null; } } /** - * 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() + * @deprecated Use + * {@link ContextFactory#addListener(ContextFactory.Listener)}. + * A simple way to upgrate the current code to new API is to replace + * Context.addContextListener(listener) with + * ContextFactory.getGlobal().addListener(listener). */ public static void addContextListener(ContextListener listener) { - boolean disabled; - synchronized (staticListenersLock) { - disabled = disabledContextListening; - if (!disabled) { - staticListeners = Kit.addListener(staticListeners, listener); - } - } - if (disabled) throw new IllegalStateException(); + ContextFactory.getGlobal().addListener(listener); } /** - * Remove a Context listener. - * @param listener the listener to remove. - * - * @see #addContextListener(ContextListener listener) - * @see #disableStaticContextListening() + * @deprecated Use + * {@link ContextFactory#removeListener(ContextFactory.Listener)}. + * A simple way to upgrate the current code to new API is to replace + * Context.removeContextListener(listener) with + * ContextFactory.getGlobal().removeListener(listener). */ public static void removeContextListener(ContextListener listener) { - synchronized (staticListenersLock) { - staticListeners = Kit.removeListener(staticListeners, listener); - } + ContextFactory.getGlobal().addListener(listener); } /** - * Disable notifications of listeners registered with - * {@link #addContextListener(ContextListener)} about Context events. - * All currently registered listeners will be removed and any subsequent - * call to {@link #addContextListener(ContextListener)} will throw an - * exception. - *

- * Embedding may use this method to prevent Context exposure to potentially - * untrusted code. + * @deprecated Use {@link ContextFactory#seal()} to seal + * appropriate ContextFactory(). */ public static void disableStaticContextListening() { - synchronized (staticListenersLock) { - disabledContextListening = true; - staticListeners = null; - } - } - - private static final int CONTEXT_CREATED_EVENT = 1; - private static final int CONTEXT_ENTER_EVENT = 2; - private static final int CONTEXT_EXIT_EVENT = 3; - private static final int CONTEXT_RELEASED_EVENT = 4; - - private void runListeners(int reason) - { - Object listeners = staticListeners; - for (int i = 0; ; ++i) { - ContextListener l; - l = (ContextListener)Kit.getListener(listeners, i); - if (l == null) - break; - switch (reason) { - case CONTEXT_CREATED_EVENT: - l.contextCreated(this); - break; - case CONTEXT_ENTER_EVENT: - l.contextEntered(this); - break; - case CONTEXT_EXIT_EVENT: - l.contextExited(this); - break; - case CONTEXT_RELEASED_EVENT: - l.contextReleased(this); - break; - default: - Kit.codeBug(); - } - } + ContextFactory.getGlobal().disableContextListening(); } /** @@ -2674,10 +2578,6 @@ public class Context } } - private static final Object staticListenersLock = new Object(); - private static volatile Object staticListeners; - private static boolean disabledContextListening; - private ContextFactory factory; private boolean sealed; private Object sealKey; @@ -2710,7 +2610,6 @@ public class Context Debugger debugger; private Object debuggerData; private int enterCount; - private boolean enteredUsingCall; private Object propertyListeners; private Hashtable hashtable; private ClassLoader applicationClassLoader; diff --git a/js/rhino/src/org/mozilla/javascript/ContextFactory.java b/js/rhino/src/org/mozilla/javascript/ContextFactory.java index 85a19378597..47deb1db9dc 100644 --- a/js/rhino/src/org/mozilla/javascript/ContextFactory.java +++ b/js/rhino/src/org/mozilla/javascript/ContextFactory.java @@ -42,6 +42,32 @@ package org.mozilla.javascript; public class ContextFactory { + /** + * Listener of {@link Context} creation and release events. + */ + public interface Listener + { + /** + * Notify about newly created {@link Context} object. + */ + public void contextCreated(Context cx); + + /** + * Notify that the specified {@link Context} instance is no longer + * associated with the current thread. + */ + public void contextReleased(Context cx); + } + + private static volatile boolean hasCustomGlobal; + private static ContextFactory global = new ContextFactory(); + + private volatile boolean sealed; + + private final Object listenersLock = new Object(); + private volatile Object listeners; + private boolean disabledListening; + /** * Create new {@link Context} instance to be associated with the current * thread. @@ -57,17 +83,112 @@ public class ContextFactory */ protected Context makeContext() { - return new Context(this); + return new Context(); + } + + protected void onContextCreated(Context cx) + { + Object listeners = this.listeners; + for (int i = 0; ; ++i) { + Listener l = (Listener)Kit.getListener(listeners, i); + if (l == null) + break; + l.contextCreated(cx); + } + } + + protected void onContextReleased(Context cx) + { + Object listeners = this.listeners; + for (int i = 0; ; ++i) { + Listener l = (Listener)Kit.getListener(listeners, i); + if (l == null) + break; + l.contextReleased(cx); + } + } + + public final void addListener(Listener listener) + { + checkNotSealed(); + synchronized (listenersLock) { + if (disabledListening) { + throw new IllegalStateException(); + } + listeners = Kit.addListener(listeners, listener); + } + } + + public final void removeListener(Listener listener) + { + checkNotSealed(); + synchronized (listenersLock) { + if (disabledListening) { + throw new IllegalStateException(); + } + listeners = Kit.removeListener(listeners, listener); + } } /** - * Perform cleanup action for {@link Context} instance. - * Rhino runtime calls the method to notify that {@link Context} - * instance created with {@link #makeContext()} - * is no longer associated with the current thread. + * The method is used only to imlement + * Context.disableStaticContextListening() */ - protected void onContextExit(Context cx) + final void disableContextListening() { + checkNotSealed(); + synchronized (listenersLock) { + disabledListening = true; + listeners = null; + } + } + + /** + * Checks if this is a sealed ContextFactory. + * @see #seal() + */ + public final boolean isSealed() + { + return sealed; + } + + /** + * Seal this ContextFactory so any attempt to modify it like to add or + * remove its listeners will throw an exception. + * @see #isSealed() + */ + public final void seal() + { + checkNotSealed(); + sealed = true; + } + + protected final void checkNotSealed() + { + if (sealed) throw new IllegalStateException(); + } + + /** + * Get default ContextFactory. + */ + public static ContextFactory getGlobal() + { + return global; + } + + /** + * Initialize default ContextFactory. The method can only be called once. + */ + public static void initGlobal(ContextFactory factory) + { + if (factory == null) { + throw new IllegalArgumentException(); + } + if (hasCustomGlobal) { + throw new IllegalStateException(); + } + hasCustomGlobal = true; + global = factory; } /** diff --git a/js/rhino/src/org/mozilla/javascript/ContextListener.java b/js/rhino/src/org/mozilla/javascript/ContextListener.java index b9df81d7909..08a19ef2a4f 100644 --- a/js/rhino/src/org/mozilla/javascript/ContextListener.java +++ b/js/rhino/src/org/mozilla/javascript/ContextListener.java @@ -38,16 +38,20 @@ package org.mozilla.javascript; /** - * Embeddings that wish to - * @see org.mozilla.javascript.Context#addContextListener + * @deprecated Embeddings that wish to customize newly created + * {@link Context} instances should implement + * {@link ContextFactory.Listener}. */ -public interface ContextListener { - - public void contextCreated(Context cx); +public interface ContextListener extends ContextFactory.Listener +{ + /** + * @deprecated Rhino runtime never calls the method. + */ public void contextEntered(Context cx); + /** + * @deprecated Rhino runtime never calls the method. + */ public void contextExited(Context cx); - - public void contextReleased(Context cx); } diff --git a/js/rhino/toolsrc/org/mozilla/javascript/tools/debugger/Dim.java b/js/rhino/toolsrc/org/mozilla/javascript/tools/debugger/Dim.java index b020d7cf0b7..314843fd8f8 100644 --- a/js/rhino/toolsrc/org/mozilla/javascript/tools/debugger/Dim.java +++ b/js/rhino/toolsrc/org/mozilla/javascript/tools/debugger/Dim.java @@ -419,19 +419,16 @@ class Dim { void enableForAllNewContexts() { - Context.addContextListener(new ContextListener() { - + ContextFactory.Listener l = new ContextFactory.Listener() + { public void contextCreated(Context cx) { initNewContext(cx); } - public void contextEntered(Context cx) { } - - public void contextExited(Context cx) { } - public void contextReleased(Context cx) { } - }); + }; + ContextFactory.getGlobal().addListener(l); } void initNewContext(Context cx)