Implementing bug 281247: JDK compatibility is implemented through VMBridge class and its subclasses to avoid excessive reflection usage.

This commit is contained in:
igor%mir2.org 2005-02-07 07:34:32 +00:00
Родитель ebd1bd1951
Коммит bb1ee0458d
6 изменённых файлов: 362 добавлений и 212 удалений

Просмотреть файл

@ -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. <p>
*
* @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. <p>
*
* @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;

Просмотреть файл

@ -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.
}
}
}

Просмотреть файл

@ -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;

Просмотреть файл

@ -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.
* <p>
* 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 <tt>cx</tt> 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);
}

Просмотреть файл

@ -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;
}
}

Просмотреть файл

@ -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();
}
}