| OLD | NEW | 
|---|
|  | (Empty) | 
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |  | 
| 2 // Use of this source code is governed by a BSD-style license that can be |  | 
| 3 // found in the LICENSE file. |  | 
| 4 |  | 
| 5 // Routines used to validate and normalize arguments. |  | 
| 6 // TODO(benwells): unit test this file. |  | 
| 7 |  | 
| 8 var JSONSchemaValidator = require('json_schema').JSONSchemaValidator; |  | 
| 9 |  | 
| 10 var schemaValidator = new JSONSchemaValidator(); |  | 
| 11 |  | 
| 12 // Validate arguments. |  | 
| 13 function validate(args, parameterSchemas) { |  | 
| 14   if (args.length > parameterSchemas.length) |  | 
| 15     throw new Error("Too many arguments."); |  | 
| 16   for (var i = 0; i < parameterSchemas.length; i++) { |  | 
| 17     if (i in args && args[i] !== null && args[i] !== undefined) { |  | 
| 18       schemaValidator.resetErrors(); |  | 
| 19       schemaValidator.validate(args[i], parameterSchemas[i]); |  | 
| 20       if (schemaValidator.errors.length == 0) |  | 
| 21         continue; |  | 
| 22       var message = "Invalid value for argument " + (i + 1) + ". "; |  | 
| 23       for (var i = 0, err; |  | 
| 24           err = schemaValidator.errors[i]; i++) { |  | 
| 25         if (err.path) { |  | 
| 26           message += "Property '" + err.path + "': "; |  | 
| 27         } |  | 
| 28         message += err.message; |  | 
| 29         message = message.substring(0, message.length - 1); |  | 
| 30         message += ", "; |  | 
| 31       } |  | 
| 32       message = message.substring(0, message.length - 2); |  | 
| 33       message += "."; |  | 
| 34       throw new Error(message); |  | 
| 35     } else if (!parameterSchemas[i].optional) { |  | 
| 36       throw new Error("Parameter " + (i + 1) + " (" + |  | 
| 37           parameterSchemas[i].name + ") is required."); |  | 
| 38     } |  | 
| 39   } |  | 
| 40 } |  | 
| 41 |  | 
| 42 // Generate all possible signatures for a given API function. |  | 
| 43 function getSignatures(parameterSchemas) { |  | 
| 44   if (parameterSchemas.length === 0) |  | 
| 45     return [[]]; |  | 
| 46   var signatures = []; |  | 
| 47   var remaining = getSignatures($Array.slice(parameterSchemas, 1)); |  | 
| 48   for (var i = 0; i < remaining.length; i++) |  | 
| 49     $Array.push(signatures, $Array.concat([parameterSchemas[0]], remaining[i])) |  | 
| 50   if (parameterSchemas[0].optional) |  | 
| 51     return $Array.concat(signatures, remaining); |  | 
| 52   return signatures; |  | 
| 53 }; |  | 
| 54 |  | 
| 55 // Return true if arguments match a given signature's schema. |  | 
| 56 function argumentsMatchSignature(args, candidateSignature) { |  | 
| 57   if (args.length != candidateSignature.length) |  | 
| 58     return false; |  | 
| 59   for (var i = 0; i < candidateSignature.length; i++) { |  | 
| 60     var argType =  JSONSchemaValidator.getType(args[i]); |  | 
| 61     if (!schemaValidator.isValidSchemaType(argType, |  | 
| 62         candidateSignature[i])) |  | 
| 63       return false; |  | 
| 64   } |  | 
| 65   return true; |  | 
| 66 }; |  | 
| 67 |  | 
| 68 // Finds the function signature for the given arguments. |  | 
| 69 function resolveSignature(args, definedSignature) { |  | 
| 70   var candidateSignatures = getSignatures(definedSignature); |  | 
| 71   for (var i = 0; i < candidateSignatures.length; i++) { |  | 
| 72     if (argumentsMatchSignature(args, candidateSignatures[i])) |  | 
| 73       return candidateSignatures[i]; |  | 
| 74   } |  | 
| 75   return null; |  | 
| 76 }; |  | 
| 77 |  | 
| 78 // Returns a string representing the defined signature of the API function. |  | 
| 79 // Example return value for chrome.windows.getCurrent: |  | 
| 80 // "windows.getCurrent(optional object populate, function callback)" |  | 
| 81 function getParameterSignatureString(name, definedSignature) { |  | 
| 82   var getSchemaTypeString = function(schema) { |  | 
| 83     var schemaTypes = schemaValidator.getAllTypesForSchema(schema); |  | 
| 84     var typeName = schemaTypes.join(" or ") + " " + schema.name; |  | 
| 85     if (schema.optional) |  | 
| 86       return "optional " + typeName; |  | 
| 87     return typeName; |  | 
| 88   }; |  | 
| 89   var typeNames = definedSignature.map(getSchemaTypeString); |  | 
| 90   return name + "(" + typeNames.join(", ") + ")"; |  | 
| 91 }; |  | 
| 92 |  | 
| 93 // Returns a string representing a call to an API function. |  | 
| 94 // Example return value for call: chrome.windows.get(1, callback) is: |  | 
| 95 // "windows.get(int, function)" |  | 
| 96 function getArgumentSignatureString(name, args) { |  | 
| 97   var typeNames = args.map(JSONSchemaValidator.getType); |  | 
| 98   return name + "(" + typeNames.join(", ") + ")"; |  | 
| 99 }; |  | 
| 100 |  | 
| 101 // Finds the correct signature for the given arguments, then validates the |  | 
| 102 // arguments against that signature. Returns a 'normalized' arguments list |  | 
| 103 // where nulls are inserted where optional parameters were omitted. |  | 
| 104 // |args| is expected to be an array. |  | 
| 105 function normalizeArgumentsAndValidate(args, funDef) { |  | 
| 106   if (funDef.allowAmbiguousOptionalArguments) { |  | 
| 107     validate(args, funDef.definition.parameters); |  | 
| 108     return args; |  | 
| 109   } |  | 
| 110   var definedSignature = funDef.definition.parameters; |  | 
| 111   var resolvedSignature = resolveSignature(args, definedSignature); |  | 
| 112   if (!resolvedSignature) |  | 
| 113     throw new Error("Invocation of form " + |  | 
| 114         getArgumentSignatureString(funDef.name, args) + |  | 
| 115         " doesn't match definition " + |  | 
| 116         getParameterSignatureString(funDef.name, definedSignature)); |  | 
| 117   validate(args, resolvedSignature); |  | 
| 118   var normalizedArgs = []; |  | 
| 119   var ai = 0; |  | 
| 120   for (var si = 0; si < definedSignature.length; si++) { |  | 
| 121     if (definedSignature[si] === resolvedSignature[ai]) |  | 
| 122       $Array.push(normalizedArgs, args[ai++]); |  | 
| 123     else |  | 
| 124       $Array.push(normalizedArgs, null); |  | 
| 125   } |  | 
| 126   return normalizedArgs; |  | 
| 127 }; |  | 
| 128 |  | 
| 129 // Validates that a given schema for an API function is not ambiguous. |  | 
| 130 function isFunctionSignatureAmbiguous(functionDef) { |  | 
| 131   if (functionDef.allowAmbiguousOptionalArguments) |  | 
| 132     return false; |  | 
| 133   var signaturesAmbiguous = function(signature1, signature2) { |  | 
| 134     if (signature1.length != signature2.length) |  | 
| 135       return false; |  | 
| 136     for (var i = 0; i < signature1.length; i++) { |  | 
| 137       if (!schemaValidator.checkSchemaOverlap( |  | 
| 138           signature1[i], signature2[i])) |  | 
| 139         return false; |  | 
| 140     } |  | 
| 141     return true; |  | 
| 142   }; |  | 
| 143   var candidateSignatures = getSignatures(functionDef.parameters); |  | 
| 144   for (var i = 0; i < candidateSignatures.length; i++) { |  | 
| 145     for (var j = i + 1; j < candidateSignatures.length; j++) { |  | 
| 146       if (signaturesAmbiguous(candidateSignatures[i], candidateSignatures[j])) |  | 
| 147         return true; |  | 
| 148     } |  | 
| 149   } |  | 
| 150   return false; |  | 
| 151 }; |  | 
| 152 |  | 
| 153 exports.isFunctionSignatureAmbiguous = isFunctionSignatureAmbiguous; |  | 
| 154 exports.normalizeArgumentsAndValidate = normalizeArgumentsAndValidate; |  | 
| 155 exports.schemaValidator = schemaValidator; |  | 
| 156 exports.validate = validate; |  | 
| OLD | NEW | 
|---|