Index: src/mirror-delay.js |
=================================================================== |
--- src/mirror-delay.js (revision 1079) |
+++ src/mirror-delay.js (working copy) |
@@ -33,28 +33,73 @@ |
Date; |
+var next_handle_ = 0; |
+var mirror_cache_ = []; |
+ |
/** |
+ * Clear the mirror handle cache. |
+ */ |
+function ClearMirrorCache() { |
+ next_handle_ = 0; |
+ mirror_cache_ = []; |
+} |
+ |
+ |
+/** |
* Returns the mirror for a specified value or object. |
* |
* @param {value or Object} value the value or object to retreive the mirror for |
* @returns {Mirror} the mirror reflects the passed value or object |
*/ |
function MakeMirror(value) { |
- if (IS_UNDEFINED(value)) return new UndefinedMirror(); |
- if (IS_NULL(value)) return new NullMirror(); |
- if (IS_BOOLEAN(value)) return new BooleanMirror(value); |
- if (IS_NUMBER(value)) return new NumberMirror(value); |
- if (IS_STRING(value)) return new StringMirror(value); |
- if (IS_ARRAY(value)) return new ArrayMirror(value); |
- if (IS_DATE(value)) return new DateMirror(value); |
- if (IS_FUNCTION(value)) return new FunctionMirror(value); |
- if (IS_REGEXP(value)) return new RegExpMirror(value); |
- if (IS_ERROR(value)) return new ErrorMirror(value); |
- return new ObjectMirror(value); |
+ var mirror; |
+ for (id in mirror_cache_) { |
+ mirror = mirror_cache_[id]; |
+ if (mirror.value() === value) { |
+ return mirror; |
+ } |
+ } |
+ |
+ if (IS_UNDEFINED(value)) { |
+ mirror = new UndefinedMirror(); |
+ } else if (IS_NULL(value)) { |
+ mirror = new NullMirror(); |
+ } else if (IS_BOOLEAN(value)) { |
+ mirror = new BooleanMirror(value); |
+ } else if (IS_NUMBER(value)) { |
+ mirror = new NumberMirror(value); |
+ } else if (IS_STRING(value)) { |
+ mirror = new StringMirror(value); |
+ } else if (IS_ARRAY(value)) { |
+ mirror = new ArrayMirror(value); |
+ } else if (IS_DATE(value)) { |
+ mirror = new DateMirror(value); |
+ } else if (IS_FUNCTION(value)) { |
+ mirror = new FunctionMirror(value); |
+ } else if (IS_REGEXP(value)) { |
+ mirror = new RegExpMirror(value); |
+ } else if (IS_ERROR(value)) { |
+ mirror = new ErrorMirror(value); |
+ } else { |
+ mirror = new ObjectMirror(value); |
+ } |
+ |
+ mirror_cache_[mirror.handle()] = mirror; |
+ return mirror; |
} |
/** |
+ * Returns the mirror for the undefined value. |
+ * |
+ * @returns {Mirror} the mirror reflects the undefined value |
+ */ |
+function GetUndefinedMirror() { |
+ return MakeMirror(void 0); |
+} |
+ |
+ |
+/** |
* Inherit the prototype methods from one constructor into another. |
* |
* The Function.prototype.inherits from lang.js rewritten as a standalone |
@@ -87,7 +132,6 @@ |
const REGEXP_TYPE = 'regexp'; |
const ERROR_TYPE = 'error'; |
const PROPERTY_TYPE = 'property'; |
-const ACCESSOR_TYPE = 'accessor'; |
const FRAME_TYPE = 'frame'; |
const SCRIPT_TYPE = 'script'; |
@@ -155,6 +199,15 @@ |
/** |
+ * Check whether the mirror reflects a value. |
+ * @returns {boolean} True if the mirror reflects a value. |
+ */ |
+Mirror.prototype.isValue = function() { |
+ return this instanceof ValueMirror; |
+} |
+ |
+ |
+/** |
* Check whether the mirror reflects the undefined value. |
* @returns {boolean} True if the mirror reflects the undefined value. |
*/ |
@@ -289,18 +342,6 @@ |
} |
-/** |
- * Serialize object in JSON format. The actual serialization is handled by the |
- * JSONProtocolSerializer. |
- * @param {boolean} details Indicate level of details to include |
- * @return {string} JSON serialization |
- */ |
-Mirror.prototype.toJSONProtocol = function(details) { |
- var serializer = new JSONProtocolSerializer(details) |
- return serializer.serialize(this) |
-} |
- |
- |
Mirror.prototype.toText = function() { |
// Simpel to text which is used when on specialization in subclass. |
return "#<" + builtins.GetInstanceName(this.constructor.name) + ">"; |
@@ -316,11 +357,17 @@ |
*/ |
function ValueMirror(type, value) { |
Mirror.call(this, type); |
+ this.handle_ = next_handle_++; |
this.value_ = value; |
} |
inherits(ValueMirror, Mirror); |
+Mirror.prototype.handle = function() { |
+ return this.handle_; |
+}; |
+ |
+ |
/** |
* Check whether this is a primitive value. |
* @return {boolean} True if the mirror reflects a primitive value |
@@ -335,7 +382,7 @@ |
}; |
- /** |
+/** |
* Get the actual value reflected by this mirror. |
* @return {value} The value reflected by this mirror |
*/ |
@@ -581,7 +628,7 @@ |
} |
// Nothing found. |
- return new UndefinedMirror(); |
+ return GetUndefinedMirror(); |
}; |
@@ -608,7 +655,7 @@ |
} |
// Nothing found. |
- return new UndefinedMirror(); |
+ return GetUndefinedMirror(); |
}; |
@@ -687,8 +734,6 @@ |
// Return source if function is resolved. Otherwise just fall through to |
// return undefined. |
if (this.resolved()) { |
- // This builtins function is context independant (only uses runtime |
- // calls and typeof. |
return builtins.FunctionSourceString(this.value_); |
} |
}; |
@@ -763,17 +808,17 @@ |
UnresolvedFunctionMirror.prototype.constructorFunction = function() { |
- return new UndefinedMirror(); |
+ return GetUndefinedMirror(); |
}; |
UnresolvedFunctionMirror.prototype.prototypeObject = function() { |
- return new UndefinedMirror(); |
+ return GetUndefinedMirror(); |
}; |
UnresolvedFunctionMirror.prototype.protoObject = function() { |
- return new UndefinedMirror(); |
+ return GetUndefinedMirror(); |
}; |
@@ -815,7 +860,7 @@ |
if (details) { |
value = new PropertyMirror(this, i, details); |
} else { |
- value = new UndefinedMirror(); |
+ value = GetUndefinedMirror(); |
} |
values[i - from_index] = value; |
} |
@@ -1225,7 +1270,7 @@ |
// value returned from the VM might be a string if the function for the |
// frame is unresolved. |
if (IS_FUNCTION(f)) { |
- return new FunctionMirror(f); |
+ return MakeMirror(f); |
} else { |
return new UnresolvedFunctionMirror(f); |
} |
@@ -1345,7 +1390,7 @@ |
} |
// Try to find the function as a property in the receiver. Include the |
// prototype chain in the lookup. |
- var property = new UndefinedMirror(); |
+ var property = GetUndefinedMirror(); |
if (!receiver.isUndefined()) { |
for (var r = receiver; !r.isNull() && property.isUndefined(); r = r.protoObject()) { |
property = r.lookupProperty(func); |
@@ -1526,16 +1571,107 @@ |
} |
+/** |
+ * Returns a mirror serializer |
+ * |
+ * @param {boolean} details Set to true to include details |
+ * @returns {MirrorSerializer} mirror serializer |
+ */ |
+function MakeMirrorSerializer(details) { |
+ return new JSONProtocolSerializer(details); |
+} |
+ |
+ |
+/** |
+ * Object for serializing a mirror objects and its direct references. |
+ * @param {boolean} details Indicates whether to include details for the mirror |
+ * serialized |
+ * @constructor |
+ */ |
function JSONProtocolSerializer(details) { |
this.details_ = details; |
+ this.mirrors_ = [ ]; |
} |
-JSONProtocolSerializer.prototype.serialize = function(mirror) { |
- // Collect the JSON property/value pairs in a array. |
+/** |
+ * Returns a serialization of an object reference. The referenced object are |
+ * added to the serialization state. |
+ * |
+ * @param {Mirror} mirror The mirror to serialize |
+ * @returns {String} JSON serialization |
+ */ |
+JSONProtocolSerializer.prototype.serializeReference = function(mirror) { |
+ return this.serialize_(mirror, true, true); |
+} |
+ |
+ |
+/** |
+ * Returns a serialization of an object value. The referenced objects are |
+ * added to the serialization state. |
+ * |
+ * @param {Mirror} mirror The mirror to serialize |
+ * @returns {String} JSON serialization |
+ */ |
+JSONProtocolSerializer.prototype.serializeValue = function(mirror) { |
+ var json = this.serialize_(mirror, false, true); |
+ return json; |
+} |
+ |
+ |
+/** |
+ * Returns a serialization of all the objects referenced. |
+ * |
+ * @param {Mirror} mirror The mirror to serialize |
+ * @returns {String} JSON serialization |
+ */ |
+JSONProtocolSerializer.prototype.serializeReferencedObjects = function() { |
+ // Collect the JSON serialization of the referenced objects in an array. |
var content = new Array(); |
+ |
+ // Get the number of referenced objects. |
+ var count = this.mirrors_.length; |
+ |
+ for (var i = 0; i < count; i++) { |
+ content.push(this.serialize_(this.mirrors_[i], false, false)); |
+ } |
- // Always add the type |
+ var json = ArrayToJSONArray_(content); |
+ return json; |
+} |
+ |
+ |
+JSONProtocolSerializer.prototype.add_ = function(mirror) { |
+ // If this mirror is already in the list just return. |
+ for (var i = 0; i < this.mirrors_.length; i++) { |
+ if (this.mirrors_[i] === mirror) { |
+ return; |
+ } |
+ } |
+ |
+ // Add the mirror to the list of mirrors to be serialized. |
+ this.mirrors_.push(mirror); |
+} |
+ |
+ |
+JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference, |
+ details) { |
+ // If serializing a reference to a value just return the reference and add the |
+ // mirror to the referenced mirrors. |
+ if (reference && mirror.isValue()) { |
+ this.add_(mirror); |
+ return '{ref:' + mirror.handle() + '}'; |
+ } |
+ |
+ // Collect the JSON property/value pairs in an array. |
+ var content = new Array(); |
+ |
+ // Add the handle for value mirrors. |
+ if (mirror.isValue()) { |
+ content.push(MakeJSONPair_('handle', NumberToJSON_(mirror.handle()))); |
+ } |
+ |
+ // Always add the type. |
content.push(MakeJSONPair_('type', StringToJSON_(mirror.type()))); |
switch (mirror.type()) { |
@@ -1573,37 +1709,13 @@ |
case ERROR_TYPE: |
case REGEXP_TYPE: |
// Add object representation. |
- this.serializeObject_(mirror, content); |
+ this.serializeObject_(mirror, content, details); |
break; |
case PROPERTY_TYPE: |
- // Properties are represented by name, value, attributes and type. |
- content.push(MakeJSONPair_('name', |
- StringToJSON_(mirror.name()))); |
- content.push(MakeJSONPair_('value', |
- mirror.value().toJSONProtocol(this.details_))); |
- if (mirror.attributes() != PropertyAttribute.None) { |
- content.push(MakeJSONPair_('attributes', |
- NumberToJSON_(mirror.attributes()))); |
- } |
- if (mirror.propertyType() != PropertyType.Normal) { |
- content.push(MakeJSONPair_('propertyType', |
- NumberToJSON_(mirror.propertyType()))); |
- } |
+ throw new Error('PropertyMirror cannot be serialized independeltly') |
break; |
- case ACCESSOR_TYPE: |
- // An accessor can either be native or defined through JavaScript. |
- if (mirror.isNative()) { |
- content.push(MakeJSONPair_('native', BooleanToJSON_(true))); |
- } else { |
- content.push(MakeJSONPair_('getter', |
- mirror.getter().toJSONProtocol(false))); |
- content.push(MakeJSONPair_('setter', |
- mirror.setter().toJSONProtocol(false))); |
- } |
- break; |
- |
case FRAME_TYPE: |
// Add object representation. |
this.serializeFrame_(mirror, content); |
@@ -1623,7 +1735,6 @@ |
content.push(MakeJSONPair_('scriptType', |
NumberToJSON_(mirror.scriptType()))); |
break; |
- |
} |
// Always add the text representation. |
@@ -1634,86 +1745,121 @@ |
} |
-JSONProtocolSerializer.prototype.serializeObject_ = function(mirror, content) { |
+/** |
+ * Serialize object information to the following JSON format. |
+ * |
+ * {"className":"<class name>", |
+ * "constructorFunction":{"ref":<number>}, |
+ * "protoObject":{"ref":<number>}, |
+ * "prototypeObject":{"ref":<number>}, |
+ * "namedInterceptor":<boolean>, |
+ * "indexedInterceptor":<boolean>, |
+ * "properties":[<properties>]} |
+ */ |
+JSONProtocolSerializer.prototype.serializeObject_ = function(mirror, content, |
+ details) { |
+ // Add general object properties. |
content.push(MakeJSONPair_('className', |
StringToJSON_(mirror.className()))); |
+ content.push(MakeJSONPair_('constructorFunction', |
+ this.serializeReference(mirror.constructorFunction()))); |
+ content.push(MakeJSONPair_('protoObject', |
+ this.serializeReference(mirror.protoObject()))); |
+ content.push(MakeJSONPair_('prototypeObject', |
+ this.serializeReference(mirror.prototypeObject()))); |
- if (this.details_) { |
- content.push(MakeJSONPair_('constructorFunction', |
- mirror.constructorFunction().toJSONProtocol(false))); |
- content.push(MakeJSONPair_('protoObject', |
- mirror.protoObject().toJSONProtocol(false))); |
- content.push(MakeJSONPair_('prototypeObject', |
- mirror.prototypeObject().toJSONProtocol(false))); |
- |
- // Add properties. For arrays don't include indexed proeprties. |
- var kind = PropertyKind.Named; |
- if (!mirror.isArray()) { |
- kind |= PropertyKind.Indexed |
- } |
- var propertyNames = mirror.propertyNames(kind); |
- var x = new Array(propertyNames.length); |
- for (var i = 0; i < propertyNames.length; i++) { |
- x[i] = mirror.property(propertyNames[i]).toJSONProtocol(false); |
- } |
- content.push(MakeJSONPair_('properties', ArrayToJSONArray_(x))); |
- |
- // For arrays the indexed properties are added separately and the length is |
- // added as well. |
- if (mirror.isArray()) { |
- var propertyNames = mirror.propertyNames(PropertyKind.Indexed); |
- var x = new Array(propertyNames.length); |
- for (var i = 0; i < propertyNames.length; i++) { |
- x[i] = mirror.property(propertyNames[i]).toJSONProtocol(false); |
- } |
- content.push(MakeJSONPair_('indexedProperties', ArrayToJSONArray_(x))); |
- |
- // Add the array length. |
- content.push(MakeJSONPair_('length', NumberToJSON_(mirror.length()))); |
- } |
- } |
- |
+ // Add flags to indicate whether there are interceptors. |
if (mirror.hasNamedInterceptor()) { |
content.push(MakeJSONPair_('namedInterceptor', BooleanToJSON_(true))); |
} |
- |
if (mirror.hasIndexedInterceptor()) { |
content.push(MakeJSONPair_('indexedInterceptor', BooleanToJSON_(true))); |
} |
+ // Add function specific properties. |
if (mirror.isFunction()) { |
// Add function specific properties. |
content.push(MakeJSONPair_('name', StringToJSON_(mirror.name()))); |
content.push(MakeJSONPair_('resolved', BooleanToJSON_(mirror.resolved()))); |
- if (this.details_ && mirror.resolved()) { |
+ if (mirror.resolved()) { |
content.push(MakeJSONPair_('source', StringToJSON_(mirror.source()))); |
} |
if (mirror.script()) { |
- content.push(MakeJSONPair_('script', mirror.script().toJSONProtocol())); |
+ content.push(MakeJSONPair_('script', this.serializeValue(mirror.script()))); |
} |
- } else if (mirror.isDate()) { |
+ } |
+ |
+ // Add date specific properties. |
+ if (mirror.isDate()) { |
// Add date specific properties. |
content.push(MakeJSONPair_('value', DateToJSON_(mirror.value()))); |
- } else if (mirror.isRegExp()) { |
- // Add regexp specific properties. |
- content.push(MakeJSONPair_('source', StringToJSON_(mirror.source()))); |
- content.push(MakeJSONPair_('global', BooleanToJSON_(mirror.global()))); |
- content.push(MakeJSONPair_('ignoreCase', |
- BooleanToJSON_(mirror.ignoreCase()))); |
- content.push(MakeJSONPair_('multiline', |
- BooleanToJSON_(mirror.multiline()))); |
- } else if (mirror.isError()) { |
- // Add error specific properties. |
- content.push(MakeJSONPair_('message', StringToJSON_(mirror.message()))); |
} |
+ |
+ // Add actual properties - named properties followed by indexed properties. |
+ var propertyNames = mirror.propertyNames(PropertyKind.Named); |
+ var propertyIndexes = mirror.propertyNames(PropertyKind.Indexed); |
+ var p = new Array(propertyNames.length + propertyIndexes.length); |
+ for (var i = 0; i < propertyNames.length; i++) { |
+ var property_mirror = mirror.property(propertyNames[i]); |
+ p[i] = this.serializeProperty_(property_mirror); |
+ if (details) { |
+ this.add_(property_mirror.value()); |
+ } |
+ } |
+ for (var i = 0; i < propertyIndexes.length; i++) { |
+ var property_mirror = mirror.property(propertyIndexes[i]); |
+ p[propertyNames.length + i] = this.serializeProperty_(property_mirror); |
+ if (details) { |
+ this.add_(property_mirror.value()); |
+ } |
+ } |
+ content.push(MakeJSONPair_('properties', ArrayToJSONArray_(p))); |
} |
+/** |
+ * Serialize property information to the following JSON format for building the |
+ * array of properties. |
+ * |
+ * {"name":"<property name>", |
+ * "attributes":<number>, |
+ * "propertyType":<number>, |
+ * "ref":<number>} |
+ * |
+ * If the attribute for the property is PropertyAttribute.None it is not added. |
+ * If the propertyType for the property is PropertyType.Normal it is not added. |
+ * Here are a couple of examples. |
+ * |
+ * {"name":"hello","ref":1} |
+ * {"name":"length","attributes":7,"propertyType":3,"ref":2} |
+ * |
+ * @param {PropertyMirror} property_mirror The property to serialize |
+ * @returns {String} JSON serialization |
+ */ |
+JSONProtocolSerializer.prototype.serializeProperty_ = function(property_mirror) { |
+ var builder = new builtins.StringBuilder(); |
+ builder.add('{"name":'); |
+ builder.add(StringToJSON_(property_mirror.name())); |
+ if (property_mirror.attributes() != PropertyAttribute.None) { |
+ builder.add(',"attributes":'); |
+ builder.add(NumberToJSON_(property_mirror.attributes())); |
+ } |
+ if (property_mirror.propertyType() != PropertyType.Normal) { |
+ builder.add(',"propertyType":'); |
+ builder.add(NumberToJSON_(property_mirror.propertyType())); |
+ } |
+ builder.add(',"ref":'); |
+ builder.add(NumberToJSON_(property_mirror.value().handle())); |
+ builder.add('}'); |
+ return builder.generate(); |
+} |
+ |
+ |
JSONProtocolSerializer.prototype.serializeFrame_ = function(mirror, content) { |
content.push(MakeJSONPair_('index', NumberToJSON_(mirror.index()))); |
content.push(MakeJSONPair_('receiver', |
- mirror.receiver().toJSONProtocol(false))); |
- content.push(MakeJSONPair_('func', mirror.func().toJSONProtocol(false))); |
+ this.serializeReference(mirror.receiver()))); |
+ content.push(MakeJSONPair_('func', this.serializeReference(mirror.func()))); |
content.push(MakeJSONPair_('constructCall', |
BooleanToJSON_(mirror.isConstructCall()))); |
content.push(MakeJSONPair_('debuggerFrame', |
@@ -1726,7 +1872,7 @@ |
arg.push(MakeJSONPair_('name', StringToJSON_(argument_name))); |
} |
arg.push(MakeJSONPair_('value', |
- mirror.argumentValue(i).toJSONProtocol(false))); |
+ this.serializeReference(mirror.argumentValue(i)))); |
x[i] = ArrayToJSONObject_(arg); |
} |
content.push(MakeJSONPair_('arguments', ArrayToJSONArray_(x))); |
@@ -1734,7 +1880,7 @@ |
for (var i = 0; i < mirror.localCount(); i++) { |
var name = MakeJSONPair_('name', StringToJSON_(mirror.localName(i))); |
var value = MakeJSONPair_('value', |
- mirror.localValue(i).toJSONProtocol(false)); |
+ this.serializeReference(mirror.localValue(i))); |
x[i] = '{' + name + ',' + value + '}'; |
} |
content.push(MakeJSONPair_('locals', ArrayToJSONArray_(x))); |