Bug 1231489 - Use content prototype for autocompletion of String, Number and Array literals; r=bgrins.

When provided a string, number or array literals, we use to
simply return the properties from String.prototype, Number.prototype
and Array.prototype.
This is working fine unless the content prototypes are modified.
In order to make it work properly, we retrieve the actual content
prototype.
The js-property-provider unit test is modified to ensure this is
working as expected.

Differential Revision: https://phabricator.services.mozilla.com/D15843

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Nicolas Chevobbe 2019-01-08 07:59:24 +00:00
Родитель 273cf84cc9
Коммит 09c8a73d83
2 изменённых файлов: 50 добавлений и 9 удалений

Просмотреть файл

@ -290,6 +290,7 @@ function JSPropertyProvider({
return null;
}
const env = environment || dbgObject.asEnvironment();
const completionPart = lastStatement;
const lastDotIndex = completionPart.lastIndexOf(".");
const lastOpeningBracketIndex = isElementAccess ? completionPart.lastIndexOf("[") : -1;
@ -319,12 +320,12 @@ function JSPropertyProvider({
let matchingObject;
if (astExpression.type === "ArrayExpression") {
matchingObject = Array.prototype;
matchingObject = getContentPrototypeObject(env, "Array");
} else if (
astExpression.type === "Literal" &&
typeof astExpression.value === "string"
) {
matchingObject = String.prototype;
matchingObject = getContentPrototypeObject(env, "String");
} else if (
astExpression.type === "Literal" &&
Number.isFinite(astExpression.value)
@ -338,7 +339,7 @@ function JSPropertyProvider({
!Number.isInteger(astExpression.value) ||
/\d[^\.]{0}\.$/.test(completionPart) === false
) {
matchingObject = Number.prototype;
matchingObject = getContentPrototypeObject(env, "Number");
} else {
return null;
}
@ -353,7 +354,7 @@ function JSPropertyProvider({
search = matchProp.replace(startQuoteRegex, "");
}
let props = getMatchedProps(matchingObject, search);
let props = getMatchedPropsInDbgObject(matchingObject, search);
if (isElementAccess) {
props = wrapMatchesInQuotes(props, elementAccessQuote);
}
@ -398,11 +399,6 @@ function JSPropertyProvider({
}
let obj = dbgObject;
// The first property must be found in the environment of the paused debugger
// or of the global lexical scope.
const env = environment || obj.asEnvironment();
if (properties.length === 0) {
return {
isElementAccess,
@ -514,6 +510,29 @@ function JSPropertyProvider({
return prepareReturnedObject(getMatchedPropsInDbgObject(obj, search));
}
/**
* For a given environment and constructor name, returns its Debugger.Object wrapped
* prototype.
*
* @param {Environment} env
* @param {String} name: Name of the constructor object we want the prototype of.
* @returns {Debugger.Object|null} the prototype, or null if it not found.
*/
function getContentPrototypeObject(env, name) {
// Retrieve the outermost environment to get the global object.
let outermostEnv = env;
while (outermostEnv && outermostEnv.parent) {
outermostEnv = outermostEnv.parent;
}
const constructorObj = DevToolsUtils.getProperty(outermostEnv.object, name);
if (!constructorObj) {
return null;
}
return DevToolsUtils.getProperty(constructorObj, "prototype");
}
/**
* @param {Object} ast: An AST representing a property access (e.g. `foo.bar["baz"].x`)
* @returns {Array|null} An array representing the property access

Просмотреть файл

@ -67,6 +67,12 @@ function run_test() {
const dbg = new Debugger();
const dbgObject = dbg.addDebuggee(sandbox);
const dbgEnv = dbgObject.asEnvironment();
Cu.evalInSandbox(`
const hello = Object.create(null, Object.getOwnPropertyDescriptors({world: 1}));
String.prototype.hello = hello;
Number.prototype.hello = hello;
Array.prototype.hello = hello;
`, sandbox);
Cu.evalInSandbox(testArray, sandbox);
Cu.evalInSandbox(testObject, sandbox);
Cu.evalInSandbox(testHyphenated, sandbox);
@ -149,6 +155,12 @@ function runChecks(dbgObject, environment, sandbox) {
results = propertyProvider("[1,2,3].");
test_has_result(results, "indexOf");
results = propertyProvider("[1,2,3].h");
test_has_result(results, "hello");
results = propertyProvider("[1,2,3].hello.w");
test_has_result(results, "world");
info("Test that suggestions are given for literal arrays with newlines.");
results = propertyProvider("[1,2,3,\n4\n].");
test_has_result(results, "indexOf");
@ -168,6 +180,10 @@ function runChecks(dbgObject, environment, sandbox) {
test_has_result(results, "charAt");
results = propertyProvider("'[1,2,3]'.");
test_has_result(results, "charAt");
results = propertyProvider("'foo'.h");
test_has_result(results, "hello");
results = propertyProvider("'foo'.hello.w");
test_has_result(results, "world");
info("Test that suggestions are not given for syntax errors.");
results = propertyProvider("'foo\"");
@ -349,6 +365,12 @@ function runChecks(dbgObject, environment, sandbox) {
results = propertyProvider("(1)['toFixed");
test_has_exact_results(results, ["'toFixed'"]);
results = propertyProvider("(1).h");
test_has_result(results, "hello");
results = propertyProvider("(1).hello.w");
test_has_result(results, "world");
info("Test access on dot-notation invalid property name");
results = propertyProvider("testHyphenated.prop");
Assert.ok(!results.matches.has("prop-A"),