* ALWAYS try to tetermine the proper thread scope dynamically in GlobalObject.get(),
otherwise we risk to run on the wrong thread scope for nested functions, which will result in buggy behaviour _only_ if the baked-in thread scope is currently active in another request (otherwise most of its properties will be unset and the lookup forwarded to the shared global object, which will proxy it to the proper thread scope). This means we do dynamic lookup in both the shared and the thread scopes, which means we can use the same class for both cases and dump DynamicGlobalObject. In return, dynamic lookup should be somewhat optimized, so we don't do double lookups for both thread and shared scope.
This commit is contained in:
parent
1186a277ae
commit
75ebaa75be
4 changed files with 53 additions and 72 deletions
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Helma License Notice
|
||||
*
|
||||
* The contents of this file are subject to the Helma License
|
||||
* Version 2.0 (the "License"). You may not use this file except in
|
||||
* compliance with the License. A copy of the License is available at
|
||||
* http://adele.helma.org/download/helma/license.txt
|
||||
*
|
||||
* Copyright 1998-2003 Helma Software. All Rights Reserved.
|
||||
*
|
||||
* $RCSfile$
|
||||
* $Author$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*/
|
||||
|
||||
package helma.scripting.rhino;
|
||||
|
||||
import helma.framework.core.Application;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
import org.mozilla.javascript.Context;
|
||||
|
||||
|
||||
/**
|
||||
* This class implements a global scope object that is a dynamic proxy
|
||||
* to a shared global scope and a per-thread dynamic scope.
|
||||
*/
|
||||
public class DynamicGlobalObject extends GlobalObject {
|
||||
|
||||
public DynamicGlobalObject(RhinoCore core, Application app) {
|
||||
super(core, app);
|
||||
}
|
||||
|
||||
public Object get(String s, Scriptable scriptable) {
|
||||
Context cx = Context.getCurrentContext();
|
||||
Scriptable scope = (Scriptable) cx.getThreadLocal("threadscope");
|
||||
if (scope != null) {
|
||||
Object obj = scope.get(s, scope);
|
||||
if (obj != null && obj != NOT_FOUND) {
|
||||
return obj;
|
||||
}
|
||||
// make thread scope accessible as "global"
|
||||
if ("global".equals(s)) {
|
||||
return scope;
|
||||
}
|
||||
}
|
||||
return super.get(s, scriptable);
|
||||
}
|
||||
|
||||
}
|
|
@ -39,6 +39,7 @@ import java.io.*;
|
|||
public class GlobalObject extends ImporterTopLevel implements PropertyRecorder {
|
||||
Application app;
|
||||
RhinoCore core;
|
||||
GlobalObject sharedGlobal = null;
|
||||
|
||||
// fields to implement PropertyRecorder
|
||||
private boolean isRecording = false;
|
||||
|
@ -50,9 +51,14 @@ public class GlobalObject extends ImporterTopLevel implements PropertyRecorder {
|
|||
* @param core ...
|
||||
* @param app ...
|
||||
*/
|
||||
public GlobalObject(RhinoCore core, Application app) {
|
||||
public GlobalObject(RhinoCore core, Application app, boolean perThread) {
|
||||
this.core = core;
|
||||
this.app = app;
|
||||
if (perThread) {
|
||||
sharedGlobal = core.global;
|
||||
setPrototype(sharedGlobal);
|
||||
setParentScope(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -92,21 +98,6 @@ public class GlobalObject extends ImporterTopLevel implements PropertyRecorder {
|
|||
return "GlobalObject";
|
||||
}
|
||||
|
||||
/**
|
||||
* Override ScriptableObject.get() to synchronize it.
|
||||
*
|
||||
* @param name
|
||||
* @param start
|
||||
* @return the property for the given name
|
||||
*/
|
||||
public synchronized Object get(String name, Scriptable start) {
|
||||
// register property for PropertyRecorder interface
|
||||
if (isRecording) {
|
||||
changedProperties.add(name);
|
||||
}
|
||||
return super.get(name, start);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override ScriptableObject.put() to implement PropertyRecorder interface
|
||||
* and to synchronize method.
|
||||
|
@ -123,6 +114,48 @@ public class GlobalObject extends ImporterTopLevel implements PropertyRecorder {
|
|||
super.put(name, start, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override ScriptableObject.get() to synchronize it, use the per-thread scope if possible,
|
||||
* and return the per-thread scope for "global".
|
||||
*
|
||||
* @param name
|
||||
* @param start
|
||||
* @return the property for the given name
|
||||
*/
|
||||
public synchronized Object get(String name, Scriptable start) {
|
||||
// register property for PropertyRecorder interface
|
||||
if (isRecording) {
|
||||
changedProperties.add(name);
|
||||
}
|
||||
Context cx = Context.getCurrentContext();
|
||||
GlobalObject scope = (GlobalObject) cx.getThreadLocal("threadscope");
|
||||
if (scope != null) {
|
||||
Object obj = scope.get(name);
|
||||
if (obj != null && obj != NOT_FOUND) {
|
||||
return obj;
|
||||
}
|
||||
// make thread scope accessible as "global"
|
||||
if ("global".equals(name)) {
|
||||
return scope;
|
||||
}
|
||||
}
|
||||
if (sharedGlobal != null) {
|
||||
return sharedGlobal.get(name);
|
||||
} else {
|
||||
return super.get(name, start);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Directly get a property, bypassing the extra stuff in get(String, Scriptable)
|
||||
*
|
||||
* @param name
|
||||
* @return the property for the given name
|
||||
*/
|
||||
protected synchronized Object get(String name) {
|
||||
return super.get(name, this);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
|
|
|
@ -42,7 +42,7 @@ public final class RhinoCore implements ScopeProvider {
|
|||
public final Application app;
|
||||
|
||||
// the global object
|
||||
DynamicGlobalObject global;
|
||||
GlobalObject global;
|
||||
|
||||
// caching table for JavaScript object wrappers
|
||||
CacheMap wrappercache;
|
||||
|
@ -104,7 +104,7 @@ public final class RhinoCore implements ScopeProvider {
|
|||
|
||||
try {
|
||||
// create global object
|
||||
global = new DynamicGlobalObject(this, app);
|
||||
global = new GlobalObject(this, app, false);
|
||||
// call the initStandardsObject in ImporterTopLevel so that
|
||||
// importClass() and importPackage() are set up.
|
||||
global.initStandardObjects(context, false);
|
||||
|
|
|
@ -49,7 +49,7 @@ public class RhinoEngine implements ScriptingEngine {
|
|||
Context context;
|
||||
|
||||
// the per-thread global object
|
||||
Scriptable global;
|
||||
GlobalObject global;
|
||||
|
||||
// the request evaluator instance owning this fesi evaluator
|
||||
RequestEvaluator reval;
|
||||
|
@ -87,9 +87,7 @@ public class RhinoEngine implements ScriptingEngine {
|
|||
context.setApplicationClassLoader(app.getClassLoader());
|
||||
|
||||
try {
|
||||
global = new GlobalObject(core, app);
|
||||
global.setPrototype(core.global);
|
||||
global.setParentScope(null);
|
||||
global = new GlobalObject(core, app, true);
|
||||
|
||||
extensionGlobals = new HashMap();
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue