Index: chrome/renderer/resources/extensions/schema_generated_bindings.js |
diff --git a/chrome/renderer/resources/extensions/schema_generated_bindings.js b/chrome/renderer/resources/extensions/schema_generated_bindings.js |
index df126906ea7050abd57103c9fb40392d055c07ae..d613d616f17c17da67a5c7f144e1aa6251881ebb 100644 |
--- a/chrome/renderer/resources/extensions/schema_generated_bindings.js |
+++ b/chrome/renderer/resources/extensions/schema_generated_bindings.js |
@@ -31,6 +31,105 @@ var chrome = chrome || {}; |
} |
} |
+ // Returns a list of types that this schema allows. |
+ chromeHidden.getAllTypesForSchema = function(schema) { |
+ var validator = new chromeHidden.JSONSchemaValidator(); |
+ validator.addTypes(chromeHidden.validationTypes); |
+ var schema_types = []; |
+ if (schema.type) |
+ schema_types.push(schema.type); |
+ if (schema.choices) { |
+ for (var i = 0; i < schema.choices.length; i++) |
+ schema_types.push(schema.choices[i].type); |
+ } |
+ if (schema['$ref']) |
+ schema_types.push(validator.types[schema['$ref']].type); |
+ return schema_types; |
+ }; |
+ |
+ // Returns true if |schema| would accept an argument of type |type|. |
+ chromeHidden.isValidSchemaType = function(type, schema) { |
+ schema_types = chromeHidden.getAllTypesForSchema(schema); |
+ for (var i = 0; i < schema_types.length; i++) { |
+ if (schema_types[i] == "any" || type == schema_types[i]) |
+ return true; |
+ } |
+ return type == "any"; |
+ }; |
+ |
+ // Returns true if there is a non-null value that both schemas would accept. |
+ chromeHidden.checkSchemaOverlap = function(schema1, schema2) { |
+ schema_types1 = chromeHidden.getAllTypesForSchema(schema1); |
+ for (var i = 0; i < schema_types1.length; i++) { |
+ if (chromeHidden.isValidSchemaType(schema_types1[i], schema2)) |
+ return true; |
+ } |
+ return false; |
+ }; |
+ |
+ // Generate all possible signatures for a given API function scehma. |
Matt Perry
2012/02/16 00:31:08
schema*
Matt Tytel
2012/02/24 01:29:27
Done.
|
+ chromeHidden.getSignatures = function(schemas) { |
+ if (schemas.length === 0) |
+ return [[]]; |
+ |
+ var signatures = []; |
+ var remaining = chromeHidden.getSignatures(schemas.slice(1)); |
+ for (var i = 0; i < remaining.length; i++) { |
+ var signature = remaining[i].slice(0); |
+ signature.unshift(schemas[0]); |
+ signatures.push(signature); |
Matt Perry
2012/02/16 00:31:08
could do this more simply as signatures.push([sche
Matt Tytel
2012/02/24 01:29:27
Done.
|
+ } |
+ if (schemas[0].optional) |
+ return signatures.concat(remaining); |
+ return signatures; |
+ }; |
+ |
+ // Return true if arguments match a given signature's schema. |
+ chromeHidden.argumentsMatchSignature = function(args, schemas) { |
+ if (args.length != schemas.length) |
+ return false; |
+ |
+ for (var i = 0; i < schemas.length; i++) { |
+ if (!chromeHidden.isValidSchemaType( |
+ chromeHidden.JSONSchemaValidator.getType(args[i]), schemas[i])) |
+ return false; |
+ } |
+ return true; |
+ }; |
+ |
+ // Finds the function signature for the given arguments. |
+ chromeHidden.matchSignature = function(args, schemas) { |
Matt Perry
2012/02/16 00:31:08
The implementation here is somewhat wasteful. You
Matt Tytel
2012/02/16 04:35:02
I think this a code efficiency vs code simplicity
Matt Perry
2012/02/16 19:21:29
Yep. I think you made the right tradeoff here.
|
+ var signatures = chromeHidden.getSignatures(schemas); |
+ for (var i = 0; i < signatures.length; i++) { |
+ if (chromeHidden.argumentsMatchSignature(args, signatures[i])) |
+ return signatures[i]; |
+ } |
+ return null; |
+ }; |
+ |
+ // Validates that a given schema for an API function is not ambiguous. |
+ chromeHidden.isSchemaAmbiguous = function(schema) { |
+ var signaturesAmbiguous = function(signature1, signature2) { |
+ if (signature1.length != signature2.length) |
+ return false; |
+ |
+ for (var i = 0; i < signature1.length; i++) { |
+ if (!chromeHidden.checkSchemaOverlap(signature1[i], signature2[i])) |
+ return false; |
+ } |
+ return true; |
+ }; |
+ |
+ var signatures = chromeHidden.getSignatures(schema); |
+ for (var i = 0; i < signatures.length; i++) { |
+ for (var j = i + 1; j < signatures.length; j++) { |
+ if (signaturesAmbiguous(signatures[i], signatures[j])) |
+ return true; |
+ } |
+ } |
+ return false; |
+ }; |
+ |
// Validate arguments. |
chromeHidden.validationTypes = []; |
chromeHidden.validate = function(args, schemas) { |
@@ -64,6 +163,52 @@ var chrome = chrome || {}; |
} |
}; |
+ // Returns a string representing the defined signature of the API function. |
+ // Example return value for chrome.windows.getCurrent: |
+ // "windows.getCurrent(optional object populate, function callback)" |
+ chromeHidden.getSchemaSignatureString = function(name, schemas) { |
+ var getSchemaTypeString = function(schema) { |
+ var schema_types = chromeHidden.getAllTypesForSchema(schema); |
+ var type_name = schema_types.join(" or ") + " " + schema.name; |
+ if (schema.optional) |
+ return "optional " + type_name; |
+ return type_name; |
+ }; |
+ |
+ var type_names = schemas.map(getSchemaTypeString); |
+ return name +"(" + type_names.join(", ") + ")"; |
Matt Perry
2012/02/16 00:31:08
space before "("
Matt Tytel
2012/02/24 01:29:27
Done.
|
+ }; |
+ |
+ // Returns a string representing a call to an API function |
+ // Example return value for call: chrome.windows.get(1, callback) is: |
+ // "windows.get(int, function)" |
+ chromeHidden.getArgumentSignatureString = function(name, args) { |
+ var type_names = args.map(chromeHidden.JSONSchemaValidator.getType); |
+ return name +"(" + type_names.join(", ") + ")"; |
Matt Perry
2012/02/16 00:31:08
space before "("
Matt Tytel
2012/02/24 01:29:27
Done.
|
+ }; |
+ |
+ // Finds the correct signature for the given arguments |
Matt Perry
2012/02/16 00:31:08
end sentence with .
Matt Tytel
2012/02/24 01:29:27
Done.
|
+ chromeHidden.matchSignatureAndValidate = function(args, fun_def) { |
+ var schemas = fun_def.definition.parameters; |
+ var match = chromeHidden.matchSignature(args, schemas); |
+ if (!match) |
+ throw new Error("Invocation of form " + |
+ chromeHidden.getArgumentSignatureString(fun_def.name, args) + |
+ " doesn't match definition " + |
+ chromeHidden.getSchemaSignatureString(fun_def.name, schemas)); |
+ chromeHidden.validate(args, match); |
+ |
+ var normalized_args = []; |
+ var ai = 0; |
+ for (var si = 0; si < schemas.length; si++) { |
+ if (schemas[si] === match[ai]) |
+ normalized_args.push(args[ai++]); |
+ else |
+ normalized_args.push(null); |
+ } |
+ return normalized_args; |
+ }; |
+ |
// Callback handling. |
var requests = []; |
chromeHidden.handleResponse = function(requestId, name, |
@@ -436,13 +581,19 @@ var chrome = chrome || {}; |
var apiFunction = {}; |
apiFunction.definition = functionDef; |
apiFunction.name = apiDef.namespace + "." + functionDef.name; |
+ |
+ // Validate API for ambiguity only in DEBUG mode. |
+ if (chromeHidden.validateAPI && |
+ chromeHidden.isSchemaAmbiguous(apiFunction.definition.parameters)) |
+ throw new Error(apiFunction.name + " is ambiguous"); |
apiFunctions.register(apiFunction.name, apiFunction); |
module[functionDef.name] = (function() { |
- var args = arguments; |
+ var args = Array.prototype.slice.call(arguments); |
if (this.updateArgumentsPreValidate) |
args = this.updateArgumentsPreValidate.apply(this, args); |
- chromeHidden.validate(args, this.definition.parameters); |
+ |
+ args = chromeHidden.matchSignatureAndValidate(args, this); |
if (this.updateArgumentsPostValidate) |
args = this.updateArgumentsPostValidate.apply(this, args); |