Refactored code compilation so that code is compiled against a temporary prototype object
rather than the actual live prototype. Moved much compilation code into two new methods in TypeInfo called prepareCompilation() and commitCompilation. The first one sets up the temporary prototype object to compile against, the latter copies over new properties to the live prototype and removes properties that are no longer present. Also, some field names have been shortened.
This commit is contained in:
parent
90ef29fb1b
commit
9b9028df72
1 changed files with 110 additions and 62 deletions
|
@ -164,7 +164,7 @@ public final class RhinoCore {
|
|||
TypeInfo type = (TypeInfo) prototypes.get(lowerCaseName);
|
||||
|
||||
// check if the prototype info exists already
|
||||
ScriptableObject op = (type == null) ? null : type.objectPrototype;
|
||||
ScriptableObject op = (type == null) ? null : type.objProto;
|
||||
|
||||
// if prototype info doesn't exist (i.e. is a standard prototype
|
||||
// built by HopExtension), create it.
|
||||
|
@ -201,8 +201,8 @@ public final class RhinoCore {
|
|||
*/
|
||||
private synchronized void evaluatePrototype(TypeInfo type) {
|
||||
|
||||
Scriptable op = type.objectPrototype;
|
||||
Prototype prototype = type.frameworkPrototype;
|
||||
type.prepareCompilation();
|
||||
Prototype prototype = type.frameworkProto;
|
||||
|
||||
// set the parent prototype in case it hasn't been done before
|
||||
// or it has changed...
|
||||
|
@ -228,47 +228,7 @@ public final class RhinoCore {
|
|||
evaluate(type, code);
|
||||
}
|
||||
|
||||
// loop through function properties defined for this proto and
|
||||
// remove those which are left over from the previous generation
|
||||
// and haven't been renewed in this pass.
|
||||
Set oldFunctions = type.compiledFunctions;
|
||||
Set newFunctions = new HashSet();
|
||||
Object[] keys = ((ScriptableObject) op).getAllIds();
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
String key = keys[i].toString();
|
||||
if (type.predefinedProperties.contains(key)) {
|
||||
// don't mess with properties we didn't set
|
||||
continue;
|
||||
}
|
||||
Object prop = op.get(key, op);
|
||||
if (oldFunctions.contains(prop)) {
|
||||
// if this is a function compiled from script, it's from the
|
||||
// old generation and wasn't renewed -- delete it.
|
||||
// System.err.println("DELETING OLD FUNC: "+key);
|
||||
try {
|
||||
((ScriptableObject) op).setAttributes(key, 0);
|
||||
op.delete(key);
|
||||
} catch (Exception px) {
|
||||
System.err.println("Error unsetting property "+key+" on "+prototype);
|
||||
}
|
||||
} else {
|
||||
newFunctions.add(prop);
|
||||
}
|
||||
}
|
||||
|
||||
type.compiledFunctions = newFunctions;
|
||||
type.lastUpdate = prototype.getLastUpdate();
|
||||
|
||||
// If this prototype defines a postCompile() function, call it
|
||||
Context cx = Context.getCurrentContext();
|
||||
try {
|
||||
Object fObj = ScriptableObject.getProperty(op, "onCodeUpdate");
|
||||
if (fObj instanceof Function) {
|
||||
((Function) fObj).call(cx, global, op, new Object[0]);
|
||||
}
|
||||
} catch (Exception x) {
|
||||
app.logError("Exception in "+prototype.getName()+".onCodeUpdate(): " + x, x);
|
||||
}
|
||||
type.commitCompilation();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -379,15 +339,15 @@ public final class RhinoCore {
|
|||
*/
|
||||
private void updatePrototype(TypeInfo type, HashSet checked) {
|
||||
// first, remember prototype as updated
|
||||
checked.add(type.frameworkPrototype);
|
||||
checked.add(type.frameworkProto);
|
||||
|
||||
if (type.parentType != null &&
|
||||
!checked.contains(type.parentType.frameworkPrototype)) {
|
||||
!checked.contains(type.parentType.frameworkProto)) {
|
||||
updatePrototype(type.getParentType(), checked);
|
||||
}
|
||||
|
||||
// let the type manager scan the prototype's directory
|
||||
app.typemgr.updatePrototype(type.frameworkPrototype);
|
||||
app.typemgr.updatePrototype(type.frameworkProto);
|
||||
|
||||
// and re-evaluate if necessary
|
||||
if (type.needsUpdate()) {
|
||||
|
@ -408,7 +368,7 @@ public final class RhinoCore {
|
|||
if (type != null && type.hasError()) {
|
||||
throw new EvaluatorException(type.getError());
|
||||
}
|
||||
return type == null ? null : type.objectPrototype;
|
||||
return type == null ? null : type.objProto;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -419,7 +379,7 @@ public final class RhinoCore {
|
|||
*/
|
||||
public Scriptable getPrototype(String protoName) {
|
||||
TypeInfo type = getPrototypeInfo(protoName);
|
||||
return type == null ? null : type.objectPrototype;
|
||||
return type == null ? null : type.objProto;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -438,7 +398,7 @@ public final class RhinoCore {
|
|||
// otherwise, it has already been evaluated for this request by updatePrototypes(),
|
||||
// which is called before a request is handled.
|
||||
if ((type != null) && (type.lastUpdate == -1)) {
|
||||
app.typemgr.updatePrototype(type.frameworkPrototype);
|
||||
app.typemgr.updatePrototype(type.frameworkProto);
|
||||
|
||||
if (type.needsUpdate()) {
|
||||
evaluatePrototype(type);
|
||||
|
@ -832,7 +792,7 @@ public final class RhinoCore {
|
|||
// get the current context
|
||||
Context cx = Context.getCurrentContext();
|
||||
|
||||
Scriptable op = type.objectPrototype;
|
||||
Scriptable op = type.tmpObjProto;
|
||||
|
||||
// do the update, evaluating the file
|
||||
// Script script = cx.compileReader(reader, sourceName, firstline, null);
|
||||
|
@ -850,7 +810,7 @@ public final class RhinoCore {
|
|||
if (type.error == null || e instanceof EcmaError) {
|
||||
type.error = e.toString();
|
||||
}
|
||||
if ("global".equals(type.frameworkPrototype.getLowerCaseName())) {
|
||||
if ("global".equals(type.frameworkProto.getLowerCaseName())) {
|
||||
globalError = type.error;
|
||||
}
|
||||
wrappercache.clear();
|
||||
|
@ -883,10 +843,13 @@ public final class RhinoCore {
|
|||
class TypeInfo {
|
||||
|
||||
// the framework prototype object
|
||||
Prototype frameworkPrototype;
|
||||
Prototype frameworkProto;
|
||||
|
||||
// the JavaScript prototype for this type
|
||||
ScriptableObject objectPrototype;
|
||||
ScriptableObject objProto;
|
||||
|
||||
// temporary JS prototype used for compilation
|
||||
ScriptableObject tmpObjProto;
|
||||
|
||||
// timestamp of last update. This is -1 so even an empty prototype directory
|
||||
// (with lastUpdate == 0) gets evaluated at least once, which is necessary
|
||||
|
@ -897,7 +860,7 @@ public final class RhinoCore {
|
|||
TypeInfo parentType;
|
||||
|
||||
// a set of property values that were defined in last script compliation
|
||||
Set compiledFunctions;
|
||||
Set compiledProperties;
|
||||
|
||||
// a set of property keys that were present before first script compilation
|
||||
final Set predefinedProperties;
|
||||
|
@ -905,9 +868,9 @@ public final class RhinoCore {
|
|||
String error;
|
||||
|
||||
public TypeInfo(Prototype proto, ScriptableObject op) {
|
||||
frameworkPrototype = proto;
|
||||
objectPrototype = op;
|
||||
compiledFunctions = new HashSet(0);
|
||||
frameworkProto = proto;
|
||||
objProto = op;
|
||||
compiledProperties = new HashSet(0);
|
||||
// remember properties already defined on this object prototype
|
||||
predefinedProperties = new HashSet();
|
||||
Object[] keys = op.getAllIds();
|
||||
|
@ -915,17 +878,102 @@ public final class RhinoCore {
|
|||
predefinedProperties.add(keys[i].toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up an empty temporary object prototype to compile this type's
|
||||
* code against.
|
||||
*/
|
||||
public void prepareCompilation() {
|
||||
tmpObjProto = new ScriptableObject() {
|
||||
public String getClassName() {
|
||||
return frameworkProto.getName();
|
||||
}
|
||||
};
|
||||
tmpObjProto.setPrototype(objProto);
|
||||
if (!"global".equals(frameworkProto.getLowerCaseName()))
|
||||
tmpObjProto.setParentScope(global);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compilation has been completed successfully - switch over to code
|
||||
* from temporary prototype, removing properties that haven't been
|
||||
* renewed.
|
||||
*/
|
||||
public void commitCompilation() {
|
||||
// loop through properties defined on the temporary proto and
|
||||
// copy them over to the actual prototype object. Then, remove
|
||||
// those properties that haven't been renewed in this compilation
|
||||
Set newProperties = new HashSet();
|
||||
Object[] keys = tmpObjProto.getAllIds();
|
||||
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
if (! (keys[i] instanceof String)) {
|
||||
System.err.println("JUMP: "+keys[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
String key = (String) keys[i];
|
||||
if (predefinedProperties.contains(key)) {
|
||||
// don't mess with properties we didn't set
|
||||
continue;
|
||||
}
|
||||
|
||||
// add properties to newProperties set
|
||||
newProperties.add(key);
|
||||
// copy them over to the actual prototype object
|
||||
int attributes = tmpObjProto.getAttributes(key);
|
||||
Object value = tmpObjProto.get(key, tmpObjProto);
|
||||
objProto.defineProperty(key, value, attributes);
|
||||
}
|
||||
|
||||
// switch property set over to new version and
|
||||
// get a set of those old properties that weren't renewed
|
||||
Set oldProperties = compiledProperties;
|
||||
compiledProperties = newProperties;
|
||||
oldProperties.removeAll(newProperties);
|
||||
|
||||
for (Iterator it = oldProperties.iterator(); it.hasNext(); ) {
|
||||
// remove those properties that weren't renewed, meaning
|
||||
// their definition has gone from the source files
|
||||
String key = (String) it.next();
|
||||
try {
|
||||
objProto.setAttributes(key, 0);
|
||||
objProto.delete(key);
|
||||
} catch (Exception px) {
|
||||
System.err.println("Error unsetting property "+key+" on "+
|
||||
frameworkProto.getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// mark this type as updated
|
||||
lastUpdate = frameworkProto.getLastUpdate();
|
||||
|
||||
// If this prototype defines a postCompile() function, call it
|
||||
Context cx = Context.getCurrentContext();
|
||||
try {
|
||||
Object fObj = ScriptableObject.getProperty(objProto,
|
||||
"onCodeUpdate");
|
||||
if (fObj instanceof Function) {
|
||||
Object[] args = {frameworkProto.getName()};
|
||||
((Function) fObj).call(cx, global, objProto, args);
|
||||
}
|
||||
} catch (Exception x) {
|
||||
app.logError("Exception in "+frameworkProto.getName()+
|
||||
".onCodeUpdate(): " + x, x);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean needsUpdate() {
|
||||
return frameworkPrototype.getLastUpdate() > lastUpdate;
|
||||
return frameworkProto.getLastUpdate() > lastUpdate;
|
||||
}
|
||||
|
||||
public void setParentType(TypeInfo type) {
|
||||
parentType = type;
|
||||
if (type == null) {
|
||||
objectPrototype.setPrototype(null);
|
||||
objProto.setPrototype(null);
|
||||
} else {
|
||||
objectPrototype.setPrototype(type.objectPrototype);
|
||||
objProto.setPrototype(type.objProto);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -954,7 +1002,7 @@ public final class RhinoCore {
|
|||
}
|
||||
|
||||
public String toString() {
|
||||
return ("TypeInfo[" + frameworkPrototype + "," + new Date(lastUpdate) + "]");
|
||||
return ("TypeInfo[" + frameworkProto + "," + new Date(lastUpdate) + "]");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue