Bug 1363886 - Part 3: Check async callback arguments against schema r=kmag

MozReview-Commit-ID: E0yp9SdJrv6

--HG--
extra : rebase_source : d94e5269d73883d970aba0768d4727943e753cb2
This commit is contained in:
Tomislav Jovanovic 2017-05-21 04:19:46 +02:00
Родитель d688cf9be3
Коммит 7ae4dbf848
2 изменённых файлов: 75 добавлений и 3 удалений

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

@ -2108,6 +2108,7 @@ FunctionEntry = class FunctionEntry extends CallEntry {
returns = {
type: Schemas.parseSchema(schema.returns, path, ["optional", "name"]),
optional: schema.returns.optional || false,
name: "result",
};
}
@ -2132,7 +2133,7 @@ FunctionEntry = class FunctionEntry extends CallEntry {
this.hasAsyncCallback = type.hasAsyncCallback;
}
checkValue({type, optional}, value, context) {
checkValue({type, optional, name}, value, context) {
if (optional && value == null) {
return;
}
@ -2143,7 +2144,14 @@ FunctionEntry = class FunctionEntry extends CallEntry {
}
const {error} = type.normalize(value, context);
if (error) {
this.throwError(context, `Type error for result value (${error})`);
this.throwError(context, `Type error for ${name} value (${error})`);
}
}
checkCallback(args, context) {
const callback = this.parameters[this.parameters.length - 1];
for (const [i, param] of callback.type.parameters.entries()) {
this.checkValue(param, args[i], context);
}
}
@ -2165,7 +2173,21 @@ FunctionEntry = class FunctionEntry extends CallEntry {
// and lastError values are reported immediately.
callback = () => {};
}
return apiImpl.callAsyncFunction(actuals, callback);
if (DEBUG && this.hasAsyncCallback && callback) {
let original = callback;
callback = (...args) => {
this.checkCallback(args, context);
original(...args);
};
}
let result = apiImpl.callAsyncFunction(actuals, callback);
if (DEBUG && this.hasAsyncCallback && !callback) {
return result.then(result => {
this.checkCallback([result], context);
return result;
});
}
return result;
};
} else if (!this.returns) {
stub = (...args) => {

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

@ -8,6 +8,14 @@ let {BaseContext, LocalAPIImplementation} = ExtensionCommon;
let schemaJson = [
{
namespace: "testnamespace",
types: [{
id: "Widget",
type: "object",
properties: {
size: {type: "integer"},
colour: {type: "string", optional: true},
},
}],
functions: [{
name: "one_required",
type: "function",
@ -44,6 +52,18 @@ let schemaJson = [
parameters: [],
optional: true,
}],
}, {
name: "async_result",
type: "function",
async: "callback",
parameters: [{
name: "callback",
type: "function",
parameters: [{
name: "widget",
$ref: "Widget",
}],
}],
}],
},
];
@ -147,6 +167,36 @@ add_task(async function testParameterValidation() {
}
});
add_task(async function testCheckAsyncResults() {
await Schemas.load("data:," + JSON.stringify(schemaJson));
const complete = generateAPIs({}, {
async_result: async () => ({size: 5, colour: "green"}),
});
const optional = generateAPIs({}, {
async_result: async () => ({size: 6}),
});
const invalid = generateAPIs({}, {
async_result: async () => ({}),
});
deepEqual(await complete.async_result(), {size: 5, colour: "green"});
deepEqual(await optional.async_result(), {size: 6},
"Missing optional properties is allowed");
if (AppConstants.DEBUG) {
await Assert.rejects(invalid.async_result(),
`Type error for widget value (Property "size" is required)`,
"Should throw for invalid callback argument in DEBUG builds");
} else {
deepEqual(await invalid.async_result(), {},
"Invalid callback argument doesn't throw in release builds");
}
});
add_task(async function testAsyncResults() {
await Schemas.load("data:," + JSON.stringify(schemaJson));
function runWithCallback(func) {