* 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:
hns 2006-04-19 13:52:50 +00:00
parent 1186a277ae
commit 75ebaa75be
4 changed files with 53 additions and 72 deletions

View file

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

View file

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

View file

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

View file

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