зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 2 changesets (bug 1473996) for failures in devtools/server/tests/unit/test_objectgrips-fn-apply.js on a CLOSED TREE
Backed out changeset dc601e6050f6 (bug 1473996) Backed out changeset 9ba8a6f1857d (bug 1473996)
This commit is contained in:
Родитель
0de56634e3
Коммит
a8bf2cbb53
|
@ -28,8 +28,6 @@ const {
|
|||
isTypedArray,
|
||||
} = require("devtools/server/actors/object/utils");
|
||||
|
||||
const propertyValueGettersMap = new WeakMap();
|
||||
|
||||
const proto = {
|
||||
/**
|
||||
* Creates an actor for the specified object.
|
||||
|
@ -492,120 +490,6 @@ const proto = {
|
|||
return { descriptor: this._propertyDescriptor(name) };
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a protocol request to provide the value of the object's
|
||||
* specified property.
|
||||
*
|
||||
* Note: Since this will evaluate getters, it can trigger execution of
|
||||
* content code and may cause side effects. This endpoint should only be used
|
||||
* when you are confident that the side-effects will be safe, or the user
|
||||
* is expecting the effects.
|
||||
*
|
||||
* @param {string} name
|
||||
* The property we want the value of.
|
||||
*/
|
||||
propertyValue: function(name) {
|
||||
if (!name) {
|
||||
return this.throwError("missingParameter", "no property name was specified");
|
||||
}
|
||||
|
||||
const value = this._getPropertyGetter()(this.obj, name);
|
||||
|
||||
return { value: this._buildCompletion(value) };
|
||||
},
|
||||
|
||||
/**
|
||||
* Rather than re-implement the logic for looking up the property of an
|
||||
* object, this utility allows for easily generating a content function
|
||||
* that can perform that lookup.
|
||||
*/
|
||||
_getPropertyGetter() {
|
||||
const { global } = this.obj;
|
||||
let getter = propertyValueGettersMap.get(global);
|
||||
if (getter) {
|
||||
return getter;
|
||||
}
|
||||
|
||||
const debugeeGetter = global.executeInGlobal("((obj, key) => obj[key]);").return;
|
||||
getter = (obj, key) => {
|
||||
// eslint-disable-next-line no-useless-call
|
||||
return debugeeGetter.call(undefined, obj, key);
|
||||
};
|
||||
propertyValueGettersMap.set(global, getter);
|
||||
|
||||
return getter;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a protocol request to evaluate a function and provide the value of
|
||||
* the result.
|
||||
*
|
||||
* Note: Since this will evaluate the function, it can trigger execution of
|
||||
* content code and may cause side effects. This endpoint should only be used
|
||||
* when you are confident that the side-effects will be safe, or the user
|
||||
* is expecting the effects.
|
||||
*
|
||||
* @param {any} context
|
||||
* The 'this' value to call the function with.
|
||||
* @param {Array<any>} args
|
||||
* The array of un-decoded actor objects, or primitives.
|
||||
*/
|
||||
apply: function(context, args) {
|
||||
const debugeeContext = this._getValueFromGrip(context);
|
||||
const debugeeArgs = args && args.map(this._getValueFromGrip, this);
|
||||
|
||||
if (!this.obj.callable) {
|
||||
return this.throwError("notCallable", "debugee object is not callable");
|
||||
}
|
||||
|
||||
const value = this.obj.apply(debugeeContext, debugeeArgs);
|
||||
|
||||
return { value: this._buildCompletion(value) };
|
||||
},
|
||||
|
||||
_getValueFromGrip(grip) {
|
||||
if (typeof grip !== "object" || !grip) {
|
||||
return grip;
|
||||
}
|
||||
|
||||
if (typeof grip.actor !== "string") {
|
||||
return this.throwError("invalidGrip", "grip argument did not include actor ID");
|
||||
}
|
||||
|
||||
const actor = this.conn.getActor(grip.actor);
|
||||
|
||||
if (!actor) {
|
||||
return this.throwError("unknownActor", "grip actor did not match a known object");
|
||||
}
|
||||
|
||||
return actor.obj;
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts a Debugger API completion value record into an eqivalent
|
||||
* object grip for use by the API.
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Tools/Debugger-API/Conventions#completion-values
|
||||
* for more specifics on the expected behavior.
|
||||
*/
|
||||
_buildCompletion(value) {
|
||||
let completionGrip = null;
|
||||
|
||||
// .apply result will be falsy if the script being executed is terminated
|
||||
// via the "slow script" dialog.
|
||||
if (value) {
|
||||
completionGrip = {};
|
||||
if ("return" in value) {
|
||||
completionGrip.return = this.hooks.createValueGrip(value.return);
|
||||
}
|
||||
if ("throw" in value) {
|
||||
completionGrip.throw = this.hooks.createValueGrip(value.throw);
|
||||
}
|
||||
}
|
||||
|
||||
return completionGrip;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a protocol request to provide the display string for the object.
|
||||
*/
|
||||
|
|
|
@ -1,142 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* eslint-disable no-shadow, max-nested-callbacks */
|
||||
|
||||
"use strict";
|
||||
|
||||
async function run_test() {
|
||||
try {
|
||||
do_test_pending();
|
||||
await run_test_with_server(DebuggerServer);
|
||||
await run_test_with_server(WorkerDebuggerServer);
|
||||
} finally {
|
||||
do_test_finished();
|
||||
}
|
||||
}
|
||||
|
||||
async function run_test_with_server(server) {
|
||||
initTestDebuggerServer(server);
|
||||
const debuggee = addTestGlobal("test-grips", server);
|
||||
debuggee.eval(`
|
||||
function stopMe(arg1) {
|
||||
debugger;
|
||||
}
|
||||
`);
|
||||
|
||||
const dbgClient = new DebuggerClient(server.connectPipe());
|
||||
await dbgClient.connect();
|
||||
const [,, threadClient] = await attachTestTabAndResume(dbgClient, "test-grips");
|
||||
|
||||
await test_object_grip(debuggee, threadClient);
|
||||
|
||||
await dbgClient.close();
|
||||
}
|
||||
|
||||
async function test_object_grip(debuggee, threadClient) {
|
||||
await assert_object_argument(
|
||||
debuggee,
|
||||
threadClient,
|
||||
`
|
||||
stopMe({
|
||||
obj1: {},
|
||||
obj2: {},
|
||||
context(arg) {
|
||||
return this === arg ? "correct context" : "wrong context";
|
||||
},
|
||||
sum(...parts) {
|
||||
return parts.reduce((acc, v) => acc + v, 0);
|
||||
},
|
||||
error() {
|
||||
throw "an error";
|
||||
},
|
||||
});
|
||||
`,
|
||||
async objClient => {
|
||||
const obj1 = (await objClient.getPropertyValue("obj1")).value.return;
|
||||
const obj2 = (await objClient.getPropertyValue("obj2")).value.return;
|
||||
|
||||
const context = threadClient.pauseGrip(
|
||||
(await objClient.getPropertyValue("context")).value.return,
|
||||
);
|
||||
const sum = threadClient.pauseGrip(
|
||||
(await objClient.getPropertyValue("sum")).value.return,
|
||||
);
|
||||
const error = threadClient.pauseGrip(
|
||||
(await objClient.getPropertyValue("error")).value.return,
|
||||
);
|
||||
|
||||
assert_response(await context.apply(obj1, [obj1]), {
|
||||
return: "correct context",
|
||||
});
|
||||
assert_response(await context.apply(obj2, [obj2]), {
|
||||
return: "correct context",
|
||||
});
|
||||
assert_response(await context.apply(obj1, [obj2]), {
|
||||
return: "wrong context",
|
||||
});
|
||||
assert_response(await context.apply(obj2, [obj1]), {
|
||||
return: "wrong context",
|
||||
});
|
||||
// eslint-disable-next-line no-useless-call
|
||||
assert_response(await sum.apply(null, [1, 2, 3, 4, 5, 6, 7]), {
|
||||
return: 1 + 2 + 3 + 4 + 5 + 6 + 7,
|
||||
});
|
||||
// eslint-disable-next-line no-useless-call
|
||||
assert_response(await error.apply(null, []), {
|
||||
throw: "an error",
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function assert_object_argument(debuggee, threadClient, code, objectHandler) {
|
||||
return new Promise((resolve, reject) => {
|
||||
threadClient.addOneTimeListener("paused", function(event, packet) {
|
||||
(async () => {
|
||||
try {
|
||||
const arg1 = packet.frame.arguments[0];
|
||||
Assert.equal(arg1.class, "Object");
|
||||
|
||||
await objectHandler(threadClient.pauseGrip(arg1));
|
||||
} finally {
|
||||
await threadClient.resume();
|
||||
}
|
||||
})().then(resolve, reject);
|
||||
});
|
||||
|
||||
// This synchronously blocks until 'threadClient.resume()' above runs
|
||||
// because the 'paused' event runs everthing in a new event loop.
|
||||
debuggee.eval(code);
|
||||
});
|
||||
}
|
||||
|
||||
function assert_response({ value }, expected) {
|
||||
assert_completion(value, expected);
|
||||
}
|
||||
|
||||
function assert_completion(value, expected) {
|
||||
if (expected && "return" in expected) {
|
||||
assert_value(value.return, expected.return);
|
||||
}
|
||||
if (expected && "throw" in expected) {
|
||||
assert_value(value.throw, expected.throw);
|
||||
}
|
||||
if (!expected) {
|
||||
assert_value(value, expected);
|
||||
}
|
||||
}
|
||||
|
||||
function assert_value(actual, expected) {
|
||||
Assert.equal(typeof actual, typeof expected);
|
||||
|
||||
if (typeof expected === "object") {
|
||||
// Note: We aren't using deepEqual here because we're only doing a cursory
|
||||
// check of a few properties, not a full comparison of the result, since
|
||||
// the full outputs includes stuff like preview info that we don't need.
|
||||
for (const key of Object.keys(expected)) {
|
||||
assert_value(actual[key], expected[key]);
|
||||
}
|
||||
} else {
|
||||
Assert.equal(actual, expected);
|
||||
}
|
||||
}
|
|
@ -1,171 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* eslint-disable no-shadow, max-nested-callbacks */
|
||||
|
||||
"use strict";
|
||||
|
||||
async function run_test() {
|
||||
try {
|
||||
do_test_pending();
|
||||
await run_test_with_server(DebuggerServer);
|
||||
await run_test_with_server(WorkerDebuggerServer);
|
||||
} finally {
|
||||
do_test_finished();
|
||||
}
|
||||
}
|
||||
|
||||
async function run_test_with_server(server) {
|
||||
initTestDebuggerServer(server);
|
||||
const debuggee = addTestGlobal("test-grips", server);
|
||||
debuggee.eval(`
|
||||
function stopMe(arg1) {
|
||||
debugger;
|
||||
}
|
||||
`);
|
||||
|
||||
const dbgClient = new DebuggerClient(server.connectPipe());
|
||||
await dbgClient.connect();
|
||||
const [,, threadClient] = await attachTestTabAndResume(dbgClient, "test-grips");
|
||||
|
||||
await test_object_grip(debuggee, threadClient);
|
||||
|
||||
await dbgClient.close();
|
||||
}
|
||||
|
||||
async function test_object_grip(debuggee, threadClient) {
|
||||
await assert_object_argument(
|
||||
debuggee,
|
||||
threadClient,
|
||||
`
|
||||
var obj = {
|
||||
stringProp: "a value",
|
||||
get stringNormal(){
|
||||
return "a value";
|
||||
},
|
||||
get stringAbrupt() {
|
||||
throw "a value";
|
||||
},
|
||||
get objectNormal() {
|
||||
return { prop: 4 };
|
||||
},
|
||||
get objectAbrupt() {
|
||||
throw { prop: 4 };
|
||||
},
|
||||
get context(){
|
||||
return this === obj ? "correct context" : "wrong context";
|
||||
},
|
||||
method() {
|
||||
return "a value";
|
||||
},
|
||||
};
|
||||
stopMe(obj);
|
||||
`,
|
||||
async objClient => {
|
||||
const expectedValues = {
|
||||
stringProp: {
|
||||
return: "a value",
|
||||
},
|
||||
stringNormal: {
|
||||
return: "a value",
|
||||
},
|
||||
stringAbrupt: {
|
||||
throw: "a value",
|
||||
},
|
||||
objectNormal: {
|
||||
return: {
|
||||
type: "object",
|
||||
class: "Object",
|
||||
ownPropertyLength: 1,
|
||||
preview: {
|
||||
kind: "Object",
|
||||
ownProperties: {
|
||||
prop: {
|
||||
value: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
objectAbrupt: {
|
||||
throw: {
|
||||
type: "object",
|
||||
class: "Object",
|
||||
ownPropertyLength: 1,
|
||||
preview: {
|
||||
kind: "Object",
|
||||
ownProperties: {
|
||||
prop: {
|
||||
value: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
context: {
|
||||
return: "correct context",
|
||||
},
|
||||
method: {
|
||||
return: {
|
||||
type: "object",
|
||||
class: "Function",
|
||||
name: "method",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
for (const [key, expected] of Object.entries(expectedValues)) {
|
||||
const { value } = await objClient.getPropertyValue(key);
|
||||
|
||||
assert_completion(value, expected);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function assert_object_argument(debuggee, threadClient, code, objectHandler) {
|
||||
return new Promise((resolve, reject) => {
|
||||
threadClient.addOneTimeListener("paused", function(event, packet) {
|
||||
(async () => {
|
||||
try {
|
||||
const arg1 = packet.frame.arguments[0];
|
||||
Assert.equal(arg1.class, "Object");
|
||||
|
||||
await objectHandler(threadClient.pauseGrip(arg1));
|
||||
} finally {
|
||||
await threadClient.resume();
|
||||
}
|
||||
})().then(resolve, reject);
|
||||
});
|
||||
|
||||
// This synchronously blocks until 'threadClient.resume()' above runs
|
||||
// because the 'paused' event runs everthing in a new event loop.
|
||||
debuggee.eval(code);
|
||||
});
|
||||
}
|
||||
|
||||
function assert_completion(value, expected) {
|
||||
if (expected && "return" in expected) {
|
||||
assert_value(value.return, expected.return);
|
||||
}
|
||||
if (expected && "throw" in expected) {
|
||||
assert_value(value.throw, expected.throw);
|
||||
}
|
||||
if (!expected) {
|
||||
assert_value(value, expected);
|
||||
}
|
||||
}
|
||||
|
||||
function assert_value(actual, expected) {
|
||||
Assert.equal(typeof actual, typeof expected);
|
||||
|
||||
if (typeof expected === "object") {
|
||||
// Note: We aren't using deepEqual here because we're only doing a cursory
|
||||
// check of a few properties, not a full comparison of the result, since
|
||||
// the full outputs includes stuff like preview info that we don't need.
|
||||
for (const key of Object.keys(expected)) {
|
||||
assert_value(actual[key], expected[key]);
|
||||
}
|
||||
} else {
|
||||
Assert.equal(actual, expected);
|
||||
}
|
||||
}
|
|
@ -177,8 +177,6 @@ reason = bug 1104838
|
|||
[test_objectgrips-21.js]
|
||||
[test_objectgrips-22.js]
|
||||
[test_objectgrips-array-like-object.js]
|
||||
[test_objectgrips-fn-apply.js]
|
||||
[test_objectgrips-property-value.js]
|
||||
[test_promise_state-01.js]
|
||||
[test_promise_state-02.js]
|
||||
[test_promise_state-03.js]
|
||||
|
|
|
@ -181,17 +181,6 @@ ObjectClient.prototype = {
|
|||
name: arg(0)
|
||||
}),
|
||||
|
||||
/**
|
||||
* Request the value of the object's specified property.
|
||||
*
|
||||
* @param name string The name of the requested property.
|
||||
* @param onResponse function Called with the request's response.
|
||||
*/
|
||||
getPropertyValue: DebuggerClient.requester({
|
||||
type: "propertyValue",
|
||||
name: arg(0)
|
||||
}),
|
||||
|
||||
/**
|
||||
* Request the prototype of the object.
|
||||
*
|
||||
|
@ -201,18 +190,6 @@ ObjectClient.prototype = {
|
|||
type: "prototype"
|
||||
}),
|
||||
|
||||
/**
|
||||
* Request the value of the object's specified property.
|
||||
*
|
||||
* @param name string The name of the requested property.
|
||||
* @param onResponse function Called with the request's response.
|
||||
*/
|
||||
apply: DebuggerClient.requester({
|
||||
type: "apply",
|
||||
this: arg(0),
|
||||
arguments: arg(1),
|
||||
}),
|
||||
|
||||
/**
|
||||
* Request the display string of the object.
|
||||
*
|
||||
|
|
|
@ -24,11 +24,6 @@ types.addDictType("object.descriptor", {
|
|||
set: "nullable:json",
|
||||
});
|
||||
|
||||
types.addDictType("object.completion", {
|
||||
return: "nullable:json",
|
||||
throw: "nullable:json"
|
||||
});
|
||||
|
||||
types.addDictType("object.definitionSite", {
|
||||
source: "source",
|
||||
line: "number",
|
||||
|
@ -50,14 +45,6 @@ types.addDictType("object.property", {
|
|||
descriptor: "nullable:object.descriptor"
|
||||
});
|
||||
|
||||
types.addDictType("object.propertyValue", {
|
||||
value: "nullable:object.completion"
|
||||
});
|
||||
|
||||
types.addDictType("object.apply", {
|
||||
value: "nullable:object.completion"
|
||||
});
|
||||
|
||||
types.addDictType("object.bindings", {
|
||||
arguments: "array:json",
|
||||
variables: "json",
|
||||
|
@ -178,19 +165,6 @@ const objectSpec = generateActorSpec({
|
|||
},
|
||||
response: RetVal("object.property")
|
||||
},
|
||||
propertyValue: {
|
||||
request: {
|
||||
name: Arg(0, "string")
|
||||
},
|
||||
response: RetVal("object.propertyValue")
|
||||
},
|
||||
apply: {
|
||||
request: {
|
||||
this: Arg(0, "nullable:json"),
|
||||
arguments: Arg(1, "nullable:array:json"),
|
||||
},
|
||||
response: RetVal("object.apply")
|
||||
},
|
||||
rejectionStack: {
|
||||
request: {},
|
||||
response: {
|
||||
|
|
Загрузка…
Ссылка в новой задаче