From: wingo@igalia.com Date: Thu, 18 Sep 2014 13:30:15 +0000 (+0000) Subject: Implement generator mirror X-Git-Tag: upstream/4.7.83~6841 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=3117f6b35884dfbf9f1f540849c35762d6b51163;p=platform%2Fupstream%2Fv8.git Implement generator mirror R=yangguo@chromium.org, aandrey@chromium.org BUG=v8:3292 LOG=N Review URL: https://codereview.chromium.org/580823002 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24043 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/mirror-debugger.js b/src/mirror-debugger.js index b13b3fa..c36d6fd 100644 --- a/src/mirror-debugger.js +++ b/src/mirror-debugger.js @@ -87,6 +87,8 @@ function MakeMirror(value, opt_transient) { 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); } @@ -161,6 +163,7 @@ var SCOPE_TYPE = 'scope'; 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; @@ -214,6 +217,7 @@ var ScopeType = { Global: 0, // - PromiseMirror // - MapMirror // - SetMirror +// - GeneratorMirror // - PropertyMirror // - InternalPropertyMirror // - FrameMirror @@ -371,6 +375,15 @@ Mirror.prototype.isPromise = function() { /** + * 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 */ @@ -986,8 +999,8 @@ FunctionMirror.prototype.script = function() { * @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_); } @@ -1352,6 +1365,66 @@ SetMirror.prototype.values = function() { /** + * 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 @@ -2539,6 +2612,7 @@ JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference, case ERROR_TYPE: case REGEXP_TYPE: case PROMISE_TYPE: + case GENERATOR_TYPE: // Add object representation. this.serializeObject_(mirror, content, details); break; @@ -2668,6 +2742,21 @@ JSONProtocolSerializer.prototype.serializeObject_ = function(mirror, content, } } + 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(); diff --git a/src/runtime.cc b/src/runtime.cc index 83cbfd1..d5cd2a8 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -14627,6 +14627,65 @@ RUNTIME_FUNCTION(Runtime_GetV8Version) { } +// 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(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); diff --git a/src/runtime.h b/src/runtime.h index cfff6f0..603745e 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -210,6 +210,11 @@ namespace internal { 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) \ \ diff --git a/test/mjsunit/es6/generators-mirror.js b/test/mjsunit/es6/generators-mirror.js new file mode 100644 index 0000000..6925285 --- /dev/null +++ b/test/mjsunit/es6/generators-mirror.js @@ -0,0 +1,84 @@ +// 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()); +});