diff --git a/js/rhino/src/org/mozilla/javascript/Context.java b/js/rhino/src/org/mozilla/javascript/Context.java index d9522ed0528..10e592daabb 100644 --- a/js/rhino/src/org/mozilla/javascript/Context.java +++ b/js/rhino/src/org/mozilla/javascript/Context.java @@ -274,6 +274,24 @@ public class Context optimizationLevel = codegenClass != null ? 0 : -1; } + /** + * Get the current Context. + * + * The current Context is per-thread; this method looks up + * the Context associated with the current thread.

+ * + * @return the Context associated with the current thread, or + * null if no context is associated with the current + * thread. + * @see org.mozilla.javascript.Context#enter() + * @see org.mozilla.javascript.Context#exit() + */ + public static Context getCurrentContext() + { + Object helper = VMBridge.instance.getThreadContextHelper(); + return VMBridge.instance.getContext(helper); + } + /** * Get a context associated with the current thread, creating * one if need be. @@ -339,14 +357,8 @@ public class Context */ public static Context enter(Context cx) { - Object[] storage = getThreadContextStorage(); - Context old; - if (storage != null) { - old = (Context)storage[0]; - } else { - old = getCurrentContext_jdk11(); - } - + Object helper = VMBridge.instance.getThreadContextHelper(); + Context old = VMBridge.instance.getContext(helper); if (old != null) { if (cx != null && cx != old && cx.enterCount != 0) { // The suplied context must be the context for @@ -378,11 +390,7 @@ public class Context } if (old == null) { - if (storage != null) { - storage[0] = cx; - } else { - setThreadContext_jdk11(cx); - } + VMBridge.instance.setContext(helper, cx); } ++cx.enterCount; @@ -406,13 +414,8 @@ public class Context */ public static void exit() { - Object[] storage = getThreadContextStorage(); - Context cx; - if (storage != null) { - cx = (Context)storage[0]; - } else { - cx = getCurrentContext_jdk11(); - } + Object helper = VMBridge.instance.getThreadContextHelper(); + Context cx = VMBridge.instance.getContext(helper); if (cx == null) { throw new IllegalStateException( "Calling Context.exit without previous Context.enter"); @@ -426,14 +429,7 @@ public class Context if (cx.sealed) onSealedMutation(); --cx.enterCount; if (cx.enterCount == 0) { - if (storage != null) { - storage[0] = null; - } else { - setThreadContext_jdk11(null); - } - } - - if (cx.enterCount == 0) { + VMBridge.instance.setContext(helper, null); ContextFactory.getGlobal().onContextReleased(cx); } } @@ -478,33 +474,31 @@ public class Context factory = ContextFactory.getGlobal(); } - Object[] storage = getThreadContextStorage(); - Context cx; - if (storage != null) { - cx = (Context)storage[0]; - } else { - cx = getCurrentContext_jdk11(); - } - + Object helper = VMBridge.instance.getThreadContextHelper(); + Context cx = VMBridge.instance.getContext(helper); if (cx != null) { + Object result; if (cx.factory != null) { - return callable.call(cx, scope, thisObj, args); + result = callable.call(cx, scope, thisObj, args); } else { - // Context was associated with the thread via Context.enter + // Context was associated with the thread via Context.enter, + // set factory to make Context.enter/exit to be no-op + // during call cx.factory = factory; try { - return callable.call(cx, scope, thisObj, args); + result = callable.call(cx, scope, thisObj, args); } finally { cx.factory = null; } } + return result; } - cx = prepareNewContext(factory, storage); + cx = prepareNewContext(factory, helper); try { return callable.call(cx, scope, thisObj, args); } finally { - releaseContext(storage, cx); + releaseContext(helper, cx); } } @@ -513,13 +507,8 @@ public class Context */ static Object call(ContextFactory factory, ContextAction action) { - Object[] storage = getThreadContextStorage(); - Context cx; - if (storage != null) { - cx = (Context)storage[0]; - } else { - cx = getCurrentContext_jdk11(); - } + Object helper = VMBridge.instance.getThreadContextHelper(); + Context cx = VMBridge.instance.getContext(helper); if (cx != null) { if (cx.factory != null) { @@ -534,16 +523,16 @@ public class Context } } - cx = prepareNewContext(factory, storage); + cx = prepareNewContext(factory, helper); try { return action.run(cx); } finally { - releaseContext(storage, cx); + releaseContext(helper, cx); } } private static Context prepareNewContext(ContextFactory factory, - Object[] storage) + Object contextHelper) { Context cx = factory.makeContext(); if (cx.factory != null || cx.enterCount != 0) { @@ -554,22 +543,13 @@ public class Context if (factory.isSealed() && !cx.isSealed()) { cx.seal(null); } - if (storage != null) { - storage[0] = cx; - } else { - setThreadContext_jdk11(cx); - } - + VMBridge.instance.setContext(contextHelper, cx); return cx; } - private static void releaseContext(Object[] storage, Context cx) + private static void releaseContext(Object contextHelper, Context cx) { - if (storage != null) { - storage[0] = null; - } else { - setThreadContext_jdk11(null); - } + VMBridge.instance.setContext(contextHelper, null); try { cx.factory.onContextReleased(cx); } finally { @@ -616,60 +596,6 @@ public class Context ContextFactory.getGlobal().addListener(listener); } - /** - * Get the current Context. - * - * The current Context is per-thread; this method looks up - * the Context associated with the current thread.

- * - * @return the Context associated with the current thread, or - * null if no context is associated with the current - * thread. - * @see org.mozilla.javascript.Context#enter - * @see org.mozilla.javascript.Context#exit - */ - public static Context getCurrentContext() - { - Object[] storage = getThreadContextStorage(); - if (storage != null) { - return (Context)storage[0]; - } - return getCurrentContext_jdk11(); - } - - private static Object[] getThreadContextStorage() - { - if (threadLocalCx != null) { - try { - Object[] storage - = (Object[])threadLocalGet.invoke(threadLocalCx, null); - if (storage == null) { - storage = new Object[1]; - threadLocalSet.invoke(threadLocalCx, - new Object[] { storage }); - } - return storage; - } catch (Exception ex) { } - } - return null; - } - - private static Context getCurrentContext_jdk11() - { - Thread t = Thread.currentThread(); - return (Context) threadContexts.get(t); - } - - private static void setThreadContext_jdk11(Context cx) - { - Thread t = Thread.currentThread(); - if (cx != null) { - threadContexts.put(t, cx); - } else { - threadContexts.remove(t); - } - } - /** * Return {@link ContextFactory} instance used to create this Context * or the result of {@link ContextFactory#getGlobal()} if no factory @@ -2173,21 +2099,15 @@ public class Context // in any case or JVM class loading is severely broken Class cxClass = this.getClass(); ClassLoader loader = cxClass.getClassLoader(); - if (method_getContextClassLoader != null) { - Thread thread = Thread.currentThread(); - ClassLoader threadLoader = null; - try { - threadLoader = (ClassLoader)method_getContextClassLoader. - invoke(thread, ScriptRuntime.emptyArgs); - } catch (Exception ex) { } - if (threadLoader != null && threadLoader != loader) { - if (testIfCanUseLoader(threadLoader, cxClass)) { - // Thread.getContextClassLoader is not cached since - // its caching prevents it from GC which may lead to - // a memory leak and hides updates to - // Thread.getContextClassLoader - return threadLoader; - } + ClassLoader threadLoader + = VMBridge.instance.getCurrentThreadClassLoader(); + if (threadLoader != null && threadLoader != loader) { + if (testIfCanUseLoader(threadLoader, cxClass)) { + // Thread.getContextClassLoader is not cached since + // its caching prevents it from GC which may lead to + // a memory leak and hides updates to + // Thread.getContextClassLoader + return threadLoader; } } applicationClassLoader = loader; @@ -2469,41 +2389,6 @@ public class Context activationNames.remove(name); } - private static Hashtable threadContexts = new Hashtable(11); - private static Object threadLocalCx; - private static Method threadLocalGet; - private static Method threadLocalSet; - - static { - Class cl = Kit.classOrNull("java.lang.ThreadLocal"); - if (cl != null) { - try { - threadLocalGet = cl.getMethod("get", null); - threadLocalSet = cl.getMethod("set", - new Class[] { ScriptRuntime.ObjectClass }); - threadLocalCx = cl.newInstance(); - } catch (Exception ex) { } - } - } - - // We'd like to use "Thread.getContextClassLoader", but - // that's only available on Java2. - private static Method method_getContextClassLoader; - - static { - // Don't use "Thread.class": that performs the lookup - // in the class initializer, which doesn't allow us to - // catch possible security exceptions. - Class threadClass = Kit.classOrNull("java.lang.Thread"); - if (threadClass != null) { - try { - method_getContextClassLoader = - threadClass.getDeclaredMethod("getContextClassLoader", - new Class[0]); - } catch (Exception ex) { } - } - } - private static String implementationVersion; private ContextFactory factory; diff --git a/js/rhino/src/org/mozilla/javascript/MemberBox.java b/js/rhino/src/org/mozilla/javascript/MemberBox.java index 3d28a5b3754..32841978d30 100644 --- a/js/rhino/src/org/mozilla/javascript/MemberBox.java +++ b/js/rhino/src/org/mozilla/javascript/MemberBox.java @@ -145,7 +145,7 @@ final class MemberBox implements Serializable memberObject = accessible; method = accessible; } else { - if (!tryToMakeAccessible(method)) { + if (!VMBridge.instance.tryToMakeAccessible(method)) { throw Context.throwAsScriptRuntimeEx(ex); } } @@ -166,7 +166,7 @@ final class MemberBox implements Serializable try { return ctor.newInstance(args); } catch (IllegalAccessException ex) { - if (!tryToMakeAccessible(ctor)) { + if (!VMBridge.instance.tryToMakeAccessible(ctor)) { throw Context.throwAsScriptRuntimeEx(ex); } } @@ -218,26 +218,6 @@ final class MemberBox implements Serializable return null; } - private static boolean tryToMakeAccessible(Member member) - { - /** - * Due to a bug in Sun's VM, public methods in private - * classes are not accessible by default (Sun Bug #4071593). - * We have to explicitly set the method accessible - * via method.setAccessible(true) but we have to use - * reflection because the setAccessible() in Method is - * not available under jdk 1.1. - */ - if (method_setAccessible != null) { - try { - Object[] args_wrapper = { Boolean.TRUE }; - method_setAccessible.invoke(member, args_wrapper); - return true; - } catch (Exception ex) { } - } - return false; - } - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { @@ -367,17 +347,5 @@ final class MemberBox implements Serializable private transient Member memberObject; transient Class[] argTypes; - - private static Method method_setAccessible; - - static { - try { - Class MethodClass = Class.forName("java.lang.reflect.Method"); - method_setAccessible = MethodClass.getMethod( - "setAccessible", new Class[] { Boolean.TYPE }); - } catch (Exception ex) { - // Assume any exceptions means the method does not exist. - } - } } diff --git a/js/rhino/src/org/mozilla/javascript/ScriptRuntime.java b/js/rhino/src/org/mozilla/javascript/ScriptRuntime.java index c48eef104aa..188fe6602e6 100644 --- a/js/rhino/src/org/mozilla/javascript/ScriptRuntime.java +++ b/js/rhino/src/org/mozilla/javascript/ScriptRuntime.java @@ -89,18 +89,19 @@ public class ScriptRuntime { ObjectClass = Kit.classOrNull("java.lang.Object"), ShortClass = Kit.classOrNull("java.lang.Short"), StringClass = Kit.classOrNull("java.lang.String"), - - SerializableClass = Kit.classOrNull("java.io.Serializable"), - DateClass = Kit.classOrNull("java.util.Date"); public final static Class - ContextClass = Kit.classOrNull("org.mozilla.javascript.Context"), - ContextFactoryClass = Kit.classOrNull("org.mozilla.javascript.ContextFactory"), - FunctionClass = Kit.classOrNull("org.mozilla.javascript.Function"), - ScriptableClass = Kit.classOrNull("org.mozilla.javascript.Scriptable"), - ScriptableObjectClass = Kit.classOrNull("org.mozilla.javascript.ScriptableObject"), - UndefinedClass = Kit.classOrNull("org.mozilla.javascript.Undefined"); + ContextClass + = Kit.classOrNull("org.mozilla.javascript.Context"), + ContextFactoryClass + = Kit.classOrNull("org.mozilla.javascript.ContextFactory"), + FunctionClass + = Kit.classOrNull("org.mozilla.javascript.Function"), + ScriptableClass + = Kit.classOrNull("org.mozilla.javascript.Scriptable"), + ScriptableObjectClass + = Kit.classOrNull("org.mozilla.javascript.ScriptableObject"); private static final String XML_INIT_CLASS = "org.mozilla.javascript.xmlimpl.XMLLibImpl"; @@ -672,7 +673,7 @@ public class ScriptRuntime { return "[object " + obj.getClassName() + ']'; } - public static String toString(Object[] args, int index) + public static String toString(Object[] args, int index) { return (index < args.length) ? toString(args[index]) : "undefined"; } @@ -2419,7 +2420,7 @@ public class ScriptRuntime { } } - private static Object toPrimitive(Object val) + private static Object toPrimitive(Object val) { if (!(val instanceof Scriptable)) { return val; diff --git a/js/rhino/src/org/mozilla/javascript/VMBridge.java b/js/rhino/src/org/mozilla/javascript/VMBridge.java new file mode 100644 index 00000000000..b7b51c586f3 --- /dev/null +++ b/js/rhino/src/org/mozilla/javascript/VMBridge.java @@ -0,0 +1,115 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape 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/NPL/ + * + * 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 Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Igor Bukanov, igor@mir2.org + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU Public License (the "GPL"), in which case the + * provisions of the GPL are applicable instead of those above. + * If you wish to allow use of your version of this file only + * under the terms of the GPL and not to allow others to use your + * version of this file under the NPL, indicate your decision by + * deleting the provisions above and replace them with the notice + * and other provisions required by the GPL. If you do not delete + * the provisions above, a recipient may use your version of this + * file under either the NPL or the GPL. + */ + +// API class + +package org.mozilla.javascript; + +public abstract class VMBridge +{ + + static final VMBridge instance = makeInstance(); + + private static VMBridge makeInstance() + { + for (int i = 0; i != 3; ++i) { + String className; + if (i == 0) { + className = "org.mozilla.javascript.VMBridge_custom"; + } else if (i == 1) { + className = "org.mozilla.javascript.jdk13.VMBridge_jdk13"; + } else { + className = "org.mozilla.javascript.jdk11.VMBridge_jdk11"; + } + Class cl = Kit.classOrNull(className); + if (cl != null) { + VMBridge bridge = (VMBridge)Kit.newInstanceOrNull(cl); + if (bridge != null) { + return bridge; + } + } + } + throw new IllegalStateException("Failed to create VMBridge instance"); + } + + /** + * Return a helper object to optimize {@link Context} access. + *

+ * The runtime will pass the resulting helper object to the subsequent + * calls to {@link #getContext(Object contextHelper)} and + * {@link #setContext(Object contextHelper, Context cx)} methods. + * In this way the implementation can use the helper to cache + * information about current thread to make {@link Context} access faster. + */ + protected abstract Object getThreadContextHelper(); + + /** + * Get {@link Context} instance associated with the current thread + * or null if none. + * + * @param contextHelper The result of {@link getThreadContextHelper()} + * called from the current thread. + */ + protected abstract Context getContext(Object contextHelper); + + /** + * Associate {@link Context} instance with the current thread or remove + * the current association if cx is null. + * + * @param contextHelper The result of {@link getThreadContextHelper()} + * called from the current thread. + */ + protected abstract void setContext(Object contextHelper, Context cx); + + /** + * Return the ClassLoader instance associated with the current thread. + */ + protected abstract ClassLoader getCurrentThreadClassLoader(); + + /** + * In many JVMSs, public methods in private + * classes are not accessible by default (Sun Bug #4071593). + * VMBridge instance should try to workaround that via, for example, + * calling method.setAccessible(true) when it is available. + * The implementation is responsible to catch all possible exceptions + * like SecurityException if the workaround is not available. + * + * @return true if it was possible to make method accessible + * or false otherwise. + */ + protected abstract boolean tryToMakeAccessible(Object accessibleObject); + +} diff --git a/js/rhino/src/org/mozilla/javascript/jdk11/VMBridge_jdk11.java b/js/rhino/src/org/mozilla/javascript/jdk11/VMBridge_jdk11.java new file mode 100644 index 00000000000..df25b14732a --- /dev/null +++ b/js/rhino/src/org/mozilla/javascript/jdk11/VMBridge_jdk11.java @@ -0,0 +1,80 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape 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/NPL/ + * + * 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 Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Igor Bukanov, igor@mir2.org + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU Public License (the "GPL"), in which case the + * provisions of the GPL are applicable instead of those above. + * If you wish to allow use of your version of this file only + * under the terms of the GPL and not to allow others to use your + * version of this file under the NPL, indicate your decision by + * deleting the provisions above and replace them with the notice + * and other provisions required by the GPL. If you do not delete + * the provisions above, a recipient may use your version of this + * file under either the NPL or the GPL. + */ + +// API class + +package org.mozilla.javascript.jdk11; + +import java.util.Hashtable; + +import org.mozilla.javascript.*; + +public class VMBridge_jdk11 extends VMBridge +{ + private Hashtable threadsWithContext = new Hashtable(); + + protected Object getThreadContextHelper() + { + return Thread.currentThread(); + } + + protected Context getContext(Object contextHelper) + { + Thread t = (Thread)contextHelper; + return (Context)threadsWithContext.get(t); + } + + protected void setContext(Object contextHelper, Context cx) + { + Thread t = (Thread)contextHelper; + if (cx == null) { + // Allow to garbage collect thread reference + threadsWithContext.remove(t); + } else { + threadsWithContext.put(t, cx); + } + } + + protected ClassLoader getCurrentThreadClassLoader() + { + return null; + } + + protected boolean tryToMakeAccessible(Object accessibleObject) + { + return false; + } +} diff --git a/js/rhino/src/org/mozilla/javascript/jdk13/VMBridge_jdk13.java b/js/rhino/src/org/mozilla/javascript/jdk13/VMBridge_jdk13.java new file mode 100644 index 00000000000..0d77a368872 --- /dev/null +++ b/js/rhino/src/org/mozilla/javascript/jdk13/VMBridge_jdk13.java @@ -0,0 +1,101 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape 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/NPL/ + * + * 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 Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Igor Bukanov, igor@mir2.org + * Attila Szegedi, szegedia@freemail.hu + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU Public License (the "GPL"), in which case the + * provisions of the GPL are applicable instead of those above. + * If you wish to allow use of your version of this file only + * under the terms of the GPL and not to allow others to use your + * version of this file under the NPL, indicate your decision by + * deleting the provisions above and replace them with the notice + * and other provisions required by the GPL. If you do not delete + * the provisions above, a recipient may use your version of this + * file under either the NPL or the GPL. + */ + +// API class + +package org.mozilla.javascript.jdk13; + +import java.lang.reflect.AccessibleObject; + +import org.mozilla.javascript.*; + +public class VMBridge_jdk13 extends VMBridge +{ + private ThreadLocal contextLocal = new ThreadLocal(); + + protected Object getThreadContextHelper() + { + // To make subsequent batch calls to getContext/setContext faster + // associate permanently one element array with contextLocal + // so getContext/setContext would need just to read/write the first + // array element. + // Note that it is necessary to use Object[], not Context[] to allow + // garbage collection of Rhino classes. For details see comments + // by Attila Szegedi in + // https://bugzilla.mozilla.org/show_bug.cgi?id=281067#c5 + + Object[] storage = (Object[])contextLocal.get(); + if (storage == null) { + storage = new Object[1]; + contextLocal.set(storage); + } + return storage; + } + + protected Context getContext(Object contextHelper) + { + Object[] storage = (Object[])contextHelper; + return (Context)storage[0]; + } + + protected void setContext(Object contextHelper, Context cx) + { + Object[] storage = (Object[])contextHelper; + storage[0] = cx; + } + + protected ClassLoader getCurrentThreadClassLoader() + { + return Thread.currentThread().getContextClassLoader(); + } + + protected boolean tryToMakeAccessible(Object accessibleObject) + { + if (!(accessibleObject instanceof AccessibleObject)) { + return false; + } + AccessibleObject accessible = (AccessibleObject)accessibleObject; + if (accessible.isAccessible()) { + return true; + } + try { + accessible.setAccessible(true); + } catch (Exception ex) { } + + return accessible.isAccessible(); + } +}