mirror = new SetMirror(value);
} else if (ObjectIsPromise(value)) {
mirror = new PromiseMirror(value);
+ } else if (IS_GENERATOR(value)) {
+ mirror = new GeneratorMirror(value);
} else {
mirror = new ObjectMirror(value, OBJECT_TYPE, opt_transient);
}
var PROMISE_TYPE = 'promise';
var MAP_TYPE = 'map';
var SET_TYPE = 'set';
+var GENERATOR_TYPE = 'generator';
// Maximum length when sending strings through the JSON protocol.
var kMaxProtocolStringLength = 80;
// - PromiseMirror
// - MapMirror
// - SetMirror
+// - GeneratorMirror
// - PropertyMirror
// - InternalPropertyMirror
// - FrameMirror
/**
+ * Check whether the mirror reflects a generator object.
+ * @returns {boolean} True if the mirror reflects a generator object
+ */
+Mirror.prototype.isGenerator = function() {
+ return this instanceof GeneratorMirror;
+};
+
+
+/**
* Check whether the mirror reflects a property.
* @returns {boolean} True if the mirror reflects a property
*/
* @return {Number or undefined} in-script position for the function
*/
FunctionMirror.prototype.sourcePosition_ = function() {
- // Return script if function is resolved. Otherwise just fall through
- // to return undefined.
+ // Return position if function is resolved. Otherwise just fall
+ // through to return undefined.
if (this.resolved()) {
return %FunctionGetScriptSourcePosition(this.value_);
}
/**
+ * Mirror object for a Generator object.
+ * @param {Object} data The Generator object
+ * @constructor
+ * @extends Mirror
+ */
+function GeneratorMirror(value) {
+ %_CallFunction(this, value, GENERATOR_TYPE, ObjectMirror);
+}
+inherits(GeneratorMirror, ObjectMirror);
+
+
+GeneratorMirror.prototype.status = function() {
+ var continuation = %GeneratorGetContinuation(this.value_);
+ if (continuation < 0) return "running";
+ if (continuation == 0) return "closed";
+ return "suspended";
+};
+
+
+GeneratorMirror.prototype.sourcePosition_ = function() {
+ return %GeneratorGetSourcePosition(this.value_);
+};
+
+
+GeneratorMirror.prototype.sourceLocation = function() {
+ var pos = this.sourcePosition_();
+ if (!IS_UNDEFINED(pos)) {
+ var script = this.func().script();
+ if (script) {
+ return script.locationFromPosition(pos, true);
+ }
+ }
+};
+
+
+GeneratorMirror.prototype.func = function() {
+ if (!this.func_) {
+ this.func_ = MakeMirror(%GeneratorGetFunction(this.value_));
+ }
+ return this.func_;
+};
+
+
+GeneratorMirror.prototype.context = function() {
+ if (!this.context_) {
+ this.context_ = new ContextMirror(%GeneratorGetContext(this.value_));
+ }
+ return this.context_;
+};
+
+
+GeneratorMirror.prototype.receiver = function() {
+ if (!this.receiver_) {
+ this.receiver_ = MakeMirror(%GeneratorGetReceiver(this.value_));
+ }
+ return this.receiver_;
+};
+
+
+/**
* Base mirror object for properties.
* @param {ObjectMirror} mirror The mirror object having this property
* @param {string} name The name of the property
case ERROR_TYPE:
case REGEXP_TYPE:
case PROMISE_TYPE:
+ case GENERATOR_TYPE:
// Add object representation.
this.serializeObject_(mirror, content, details);
break;
}
}
+ if (mirror.isGenerator()) {
+ // Add generator specific properties.
+
+ // Either 'running', 'closed', or 'suspended'.
+ content.status = mirror.status();
+
+ content.func = this.serializeReference(mirror.func())
+ content.receiver = this.serializeReference(mirror.receiver())
+
+ // If the generator is suspended, the content add line/column properties.
+ serializeLocationFields(mirror.sourceLocation(), content);
+
+ // TODO(wingo): Also serialize a reference to the context (scope chain).
+ }
+
if (mirror.isDate()) {
// Add date specific properties.
content.value = mirror.value();
}
+// Returns function of generator activation.
+RUNTIME_FUNCTION(Runtime_GeneratorGetFunction) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSGeneratorObject, generator, 0);
+
+ return generator->function();
+}
+
+
+// Returns context of generator activation.
+RUNTIME_FUNCTION(Runtime_GeneratorGetContext) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSGeneratorObject, generator, 0);
+
+ return generator->context();
+}
+
+
+// Returns receiver of generator activation.
+RUNTIME_FUNCTION(Runtime_GeneratorGetReceiver) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSGeneratorObject, generator, 0);
+
+ return generator->receiver();
+}
+
+
+// Returns generator continuation as a PC offset, or the magic -1 or 0 values.
+RUNTIME_FUNCTION(Runtime_GeneratorGetContinuation) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSGeneratorObject, generator, 0);
+
+ return Smi::FromInt(generator->continuation());
+}
+
+
+RUNTIME_FUNCTION(Runtime_GeneratorGetSourcePosition) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSGeneratorObject, generator, 0);
+
+ if (generator->is_suspended()) {
+ Handle<Code> code(generator->function()->code(), isolate);
+ int offset = generator->continuation();
+
+ RUNTIME_ASSERT(0 <= offset && offset < code->Size());
+ Address pc = code->address() + offset;
+
+ return Smi::FromInt(code->SourcePosition(pc));
+ }
+
+ return isolate->heap()->undefined_value();
+}
+
+
RUNTIME_FUNCTION(Runtime_Abort) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 1);
F(GetScript, 1, 1) \
F(CollectStackTrace, 2, 1) \
F(GetV8Version, 0, 1) \
+ F(GeneratorGetFunction, 1, 1) \
+ F(GeneratorGetContext, 1, 1) \
+ F(GeneratorGetReceiver, 1, 1) \
+ F(GeneratorGetContinuation, 1, 1) \
+ F(GeneratorGetSourcePosition, 1, 1) \
\
F(SetCode, 2, 1) \
\
--- /dev/null
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --expose-debug-as debug
+// Test the mirror object for functions.
+
+function *generator(f) {
+ "use strict";
+ yield;
+ f();
+ yield;
+}
+
+function MirrorRefCache(json_refs) {
+ var tmp = eval('(' + json_refs + ')');
+ this.refs_ = [];
+ for (var i = 0; i < tmp.length; i++) {
+ this.refs_[tmp[i].handle] = tmp[i];
+ }
+}
+
+MirrorRefCache.prototype.lookup = function(handle) {
+ return this.refs_[handle];
+}
+
+function TestGeneratorMirror(g, test) {
+ // Create mirror and JSON representation.
+ var mirror = debug.MakeMirror(g);
+ var serializer = debug.MakeMirrorSerializer();
+ var json = JSON.stringify(serializer.serializeValue(mirror));
+ var refs = new MirrorRefCache(
+ JSON.stringify(serializer.serializeReferencedObjects()));
+
+ // Check the mirror hierachy.
+ assertTrue(mirror instanceof debug.Mirror);
+ assertTrue(mirror instanceof debug.ValueMirror);
+ assertTrue(mirror instanceof debug.ObjectMirror);
+ assertTrue(mirror instanceof debug.GeneratorMirror);
+
+ // Check the mirror properties.
+ assertTrue(mirror.isGenerator());
+ assertEquals('generator', mirror.type());
+ assertFalse(mirror.isPrimitive());
+ assertEquals('Generator', mirror.className());
+
+ assertTrue(mirror.receiver().isUndefined());
+ assertEquals(generator, mirror.func().value());
+
+ test(mirror);
+}
+
+var iter = generator(function () {
+ assertEquals('running', debug.MakeMirror(iter).status());
+})
+
+// Note that line numbers are 0-based, not 1-based.
+function assertSourceLocation(loc, line, column) {
+ assertEquals(line, loc.line);
+ assertEquals(column, loc.column);
+}
+
+TestGeneratorMirror(iter, function (mirror) {
+ assertEquals('suspended', mirror.status())
+ assertSourceLocation(mirror.sourceLocation(), 7, 19);
+});
+
+iter.next();
+TestGeneratorMirror(iter, function (mirror) {
+ assertEquals('suspended', mirror.status())
+ assertSourceLocation(mirror.sourceLocation(), 9, 2);
+});
+
+iter.next();
+TestGeneratorMirror(iter, function (mirror) {
+ assertEquals('suspended', mirror.status())
+ assertSourceLocation(mirror.sourceLocation(), 11, 2);
+});
+
+iter.next();
+TestGeneratorMirror(iter, function (mirror) {
+ assertEquals('closed', mirror.status())
+ assertEquals(undefined, mirror.sourceLocation());
+});