UNPKG

firebase-bolt

Version:

Firebase Bolt Security and Modeling Language Compiler

854 lines (853 loc) 130 kB
"use strict"; exports.__esModule = true; /* * Copyright 2015 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var util = require("./util"); var ast = require("./ast"); var logger_1 = require("./logger"); var parser = require('./rules-parser'); var parse_util_1 = require("./parse-util"); var errors = { badIndex: "The index function must return a String or an array of Strings.", noPaths: "Must have at least one path expression.", nonObject: "Type contains properties and must extend 'Object'.", missingSchema: "Missing definition for type.", recursive: "Recursive function call.", mismatchParams: "Incorrect number of function arguments.", generateFailed: "Could not generate JSON: ", noSuchType: "No type definition for: ", badSchemaMethod: "Unsupported method name in type statement: ", badPathMethod: "Unsupported method name in path statement: ", badWriteAlias: "Cannot have both a write() method and a write-aliasing method: ", coercion: "Cannot convert value: ", undefinedFunction: "Undefined function: ", application: "Bolt application error: ", invalidGeneric: "Invalid generic schema usage: ", invalidMapKey: "Map<Key, T> - Key must derive from String type.", invalidWildChildren: "Types can have at most one $wild property and cannot mix with other properties.", invalidPropertyName: "Property names cannot contain any of: . $ # [ ] / or control characters: " }; var INVALID_KEY_REGEX = /[\[\].#$\/\u0000-\u001F\u007F]/; ; var builtinSchemaNames = ['Any', 'Null', 'String', 'Number', 'Boolean', 'Object']; // Method names allowed in Bolt files. var valueMethods = ['length', 'includes', 'startsWith', 'beginsWith', 'endsWith', 'replace', 'toLowerCase', 'toUpperCase', 'test', 'contains', 'matches']; // TODO: Make sure users don't call internal methods...make private to impl. var snapshotMethods = ['parent', 'child', 'hasChildren', 'val', 'isString', 'isNumber', 'isBoolean'].concat(valueMethods); var writeAliases = { 'create': parse_util_1.parseExpression('prior(this) == null'), 'update': parse_util_1.parseExpression('prior(this) != null && this != null'), 'delete': parse_util_1.parseExpression('prior(this) != null && this == null') }; // Usage: // json = bolt.generate(bolt-text) function generate(symbols) { if (typeof symbols === 'string') { symbols = parser.parse(symbols); } var gen = new Generator(symbols); return gen.generateRules(); } exports.generate = generate; // Symbols contains: // functions: {} // schema: {} // paths: {} var Generator = /** @class */ (function () { function Generator(symbols) { this.symbols = symbols; this.validators = {}; this.rules = {}; this.errorCount = 0; this.runSilently = false; this.allowUndefinedFunctions = false; this.keyIndex = 0; // TODO: globals should be part of this.symbols (nested scopes) this.globals = { "root": ast.call(ast.variable('@root')) }; this.registerBuiltinSchema(); } // Return Firebase compatible Rules JSON for a the given symbols definitions. Generator.prototype.generateRules = function () { var _this = this; this.errorCount = 0; var paths = this.symbols.paths; var schema = this.symbols.schema; var name; paths.forEach(function (path) { _this.validateMethods(errors.badPathMethod, path.methods, ['validate', 'read', 'write', 'index']); }); for (name in schema) { if (!util.arrayIncludes(builtinSchemaNames, name)) { this.validateMethods(errors.badSchemaMethod, schema[name].methods, ['validate', 'read', 'write']); } } if (paths.length === 0) { this.fatal(errors.noPaths); } paths.forEach(function (path) { return _this.updateRules(path); }); this.convertExpressions(this.rules); if (this.errorCount !== 0) { throw new Error(errors.generateFailed + this.errorCount + " errors."); } util.deletePropName(this.rules, '.scope'); util.pruneEmptyChildren(this.rules); return { rules: this.rules }; }; Generator.prototype.validateMethods = function (m, methods, allowed) { var _this = this; if (util.arrayIncludes(allowed, 'write')) { allowed = allowed.concat(Object.keys(writeAliases)); } for (var method in methods) { if (!util.arrayIncludes(allowed, method)) { logger_1.warn(m + util.quoteString(method) + " (allowed: " + allowed.map(util.quoteString).join(', ') + ")"); } } if ('write' in methods) { Object.keys(writeAliases).forEach(function (alias) { if (alias in methods) { _this.fatal(errors.badWriteAlias + alias); } }); } }; Generator.prototype.registerBuiltinSchema = function () { var self = this; var thisVar = ast.variable('this'); function registerAsCall(name, methodName) { self.symbols.registerSchema(name, ast.typeType('Any'), undefined, { validate: ast.method(['this'], ast.call(ast.reference(ast.cast(thisVar, 'Any'), ast.string(methodName)))) }); } this.symbols.registerSchema('Any', ast.typeType('Any'), undefined, { validate: ast.method(['this'], ast.boolean(true)) }); registerAsCall('Object', 'hasChildren'); // Because of the way firebase treats Null values, there is no way to // write a validation rule, that will EVER be called with this == null // (firebase allows values to be deleted no matter their validation rules). // So, comparing this == null will always return false -> that is what // we do here, which will be optimized away if ORed with other validations. this.symbols.registerSchema('Null', ast.typeType('Any'), undefined, { validate: ast.method(['this'], ast.boolean(false)) }); self.symbols.registerSchema('String', ast.typeType('Any'), undefined, { validate: ast.method(['this'], ast.call(ast.reference(ast.cast(thisVar, 'Any'), ast.string('isString')))), includes: ast.method(['this', 's'], ast.call(ast.reference(ast.value(thisVar), ast.string('contains')), [ast.value(ast.variable('s'))])), startsWith: ast.method(['this', 's'], ast.call(ast.reference(ast.value(thisVar), ast.string('beginsWith')), [ast.value(ast.variable('s'))])), endsWith: ast.method(['this', 's'], ast.call(ast.reference(ast.value(thisVar), ast.string('endsWith')), [ast.value(ast.variable('s'))])), replace: ast.method(['this', 's', 'r'], ast.call(ast.reference(ast.value(thisVar), ast.string('replace')), [ast.value(ast.variable('s')), ast.value(ast.variable('r'))])), test: ast.method(['this', 'r'], ast.call(ast.reference(ast.value(thisVar), ast.string('matches')), [ast.call(ast.variable('@RegExp'), [ast.variable('r')])])) }); registerAsCall('Number', 'isNumber'); registerAsCall('Boolean', 'isBoolean'); this.symbols.registerFunction('@RegExp', ['r'], ast.builtin(this.ensureType.bind(this, 'RegExp'))); var map = this.symbols.registerSchema('Map', ast.typeType('Any'), undefined, undefined, ['Key', 'Value']); map.getValidator = this.getMapValidator.bind(this); }; // type Map<Key, Value> => { // $key: { // '.validate': $key instanceof Key and this instanceof Value; // '.validate': 'newData.hasChildren()' // } // Key must derive from String Generator.prototype.getMapValidator = function (params) { var keyType = params[0]; var valueType = params[1]; if (keyType.type !== 'type' || !this.symbols.isDerivedFrom(keyType, 'String')) { throw new Error(errors.invalidMapKey + " (" + ast.decodeExpression(keyType) + " does not)"); } var validator = {}; var index = this.uniqueKey(); validator[index] = {}; extendValidator(validator, this.ensureValidator(ast.typeType('Object'))); // First validate the key (omit terminal String type validation). while (keyType.name !== 'String') { var schema = this.symbols.schema[keyType.name]; if (schema.methods['validate']) { var exp = this.partialEval(schema.methods['validate'].body, { 'this': ast.literal(index) }); extendValidator(validator[index], { '.validate': [exp] }); } keyType = schema.derivedFrom; } extendValidator(validator[index], this.ensureValidator(valueType)); return validator; }; Generator.prototype.uniqueKey = function () { this.keyIndex += 1; return '$key' + this.keyIndex; }; // Collection schema has exactly one $wildchild property Generator.prototype.isCollectionSchema = function (schema) { var props = Object.keys(schema.properties); var result = props.length === 1 && props[0][0] === '$'; return result; }; // Ensure we have a definition for a validator for the given schema. Generator.prototype.ensureValidator = function (type) { var key = ast.decodeExpression(type); if (!this.validators[key]) { this.validators[key] = { '.validate': ast.literal('***TYPE RECURSION***') }; var allowSave = this.allowUndefinedFunctions; this.allowUndefinedFunctions = true; this.validators[key] = this.createValidator(type); this.allowUndefinedFunctions = allowSave; } return this.validators[key]; }; Generator.prototype.createValidator = function (type) { var _this = this; switch (type.type) { case 'type': return this.createValidatorFromSchemaName(type.name); case 'union': var union_1 = {}; type.types.forEach(function (typePart) { // Make a copy var singleType = extendValidator({}, _this.ensureValidator(typePart)); mapValidator(singleType, ast.andArray); extendValidator(union_1, singleType); }); mapValidator(union_1, ast.orArray); return union_1; case 'generic': var genericType = type; return this.createValidatorFromGeneric(genericType.name, genericType.params); default: throw new Error(errors.application + "invalid internal type: " + type.type); } }; Generator.prototype.createValidatorFromGeneric = function (schemaName, params) { var schema = this.symbols.schema[schemaName]; if (schema === undefined || !ast.Schema.isGeneric(schema)) { throw new Error(errors.noSuchType + schemaName + " (generic)"); } var schemaParams = schema.params; if (params.length !== schemaParams.length) { throw new Error(errors.invalidGeneric + " expected <" + schemaParams.join(', ') + ">"); } // Call custom validator, if given. if (schema.getValidator) { return schema.getValidator(params); } var bindings = {}; for (var i = 0; i < params.length; i++) { bindings[schemaParams[i]] = params[i]; } // Expand generics and generate validator from schema. schema = this.replaceGenericsInSchema(schema, bindings); return this.createValidatorFromSchema(schema); }; Generator.prototype.replaceGenericsInSchema = function (schema, bindings) { var _this = this; var expandedSchema = { derivedFrom: this.replaceGenericsInExp(schema.derivedFrom, bindings), properties: {}, methods: {} }; var props = Object.keys(schema.properties); props.forEach(function (prop) { expandedSchema.properties[prop] = _this.replaceGenericsInExp(schema.properties[prop], bindings); }); var methods = Object.keys(schema.methods); methods.forEach(function (methodName) { expandedSchema.methods[methodName] = _this.replaceGenericsInMethod(schema.methods[methodName], bindings); }); return expandedSchema; }; Generator.prototype.replaceGenericsInExp = function (exp, bindings) { var self = this; function replaceGenericsInArray(exps) { return exps.map(function (expPart) { return self.replaceGenericsInExp(expPart, bindings); }); } switch (exp.type) { case 'op': case 'call': var opType = ast.copyExp(exp); opType.args = replaceGenericsInArray(opType.args); return opType; case 'type': var simpleType = exp; return bindings[simpleType.name] || simpleType; case 'union': var unionType = exp; return ast.unionType(replaceGenericsInArray(unionType.types)); case 'generic': var genericType = exp; return ast.genericType(genericType.name, replaceGenericsInArray(genericType.params)); default: return exp; } }; Generator.prototype.replaceGenericsInMethod = function (method, bindings) { var expandedMethod = { params: method.params, body: method.body }; expandedMethod.body = this.replaceGenericsInExp(method.body, bindings); return expandedMethod; }; Generator.prototype.createValidatorFromSchemaName = function (schemaName) { var schema = this.symbols.schema[schemaName]; if (!schema) { throw new Error(errors.noSuchType + schemaName); } if (ast.Schema.isGeneric(schema)) { throw new Error(errors.noSuchType + schemaName + " used as non-generic type."); } return this.createValidatorFromSchema(schema); }; Generator.prototype.createValidatorFromSchema = function (schema) { var _this = this; var hasProps = Object.keys(schema.properties).length > 0 && !this.isCollectionSchema(schema); if (hasProps && !this.symbols.isDerivedFrom(schema.derivedFrom, 'Object')) { this.fatal(errors.nonObject + " (is " + ast.decodeExpression(schema.derivedFrom) + ")"); return {}; } var validator = {}; if (!(schema.derivedFrom.type === 'type' && schema.derivedFrom.name === 'Any')) { extendValidator(validator, this.ensureValidator(schema.derivedFrom)); } var requiredProperties = []; var wildProperties = 0; Object.keys(schema.properties).forEach(function (propName) { if (propName[0] === '$') { wildProperties += 1; if (INVALID_KEY_REGEX.test(propName.slice(1))) { _this.fatal(errors.invalidPropertyName + propName); } } else { if (INVALID_KEY_REGEX.test(propName)) { _this.fatal(errors.invalidPropertyName + propName); } } if (!validator[propName]) { validator[propName] = {}; } var propType = schema.properties[propName]; if (propName[0] !== '$' && !_this.isNullableType(propType)) { requiredProperties.push(propName); } extendValidator(validator[propName], _this.ensureValidator(propType)); }); if (wildProperties > 1 || wildProperties === 1 && requiredProperties.length > 0) { this.fatal(errors.invalidWildChildren); } if (requiredProperties.length > 0) { // this.hasChildren(requiredProperties) extendValidator(validator, { '.validate': [hasChildrenExp(requiredProperties)] }); } // Disallow $other properties by default if (hasProps) { validator['$other'] = {}; extendValidator(validator['$other'], { '.validate': ast.boolean(false) }); } this.extendValidationMethods(validator, schema.methods); return validator; }; Generator.prototype.isNullableType = function (type) { var result = this.symbols.isDerivedFrom(type, 'Null') || this.symbols.isDerivedFrom(type, 'Map'); return result; }; // Update rules based on the given path expression. Generator.prototype.updateRules = function (path) { var i; var location = util.ensureObjectPath(this.rules, path.template.getLabels()); var exp; extendValidator(location, this.ensureValidator(path.isType)); location['.scope'] = path.template.getScope(); this.extendValidationMethods(location, path.methods); // Write indices if (path.methods['index']) { switch (path.methods['index'].body.type) { case 'String': exp = ast.array([path.methods['index'].body]); break; case 'Array': exp = path.methods['index'].body; break; default: this.fatal(errors.badIndex); return; } var indices = []; for (i = 0; i < exp.value.length; i++) { if (exp.value[i].type !== 'String') { this.fatal(errors.badIndex + " (not " + exp.value[i].type + ")"); } else { indices.push(exp.value[i].value); } } // TODO: Error check not over-writing index rules. location['.indexOn'] = indices; } }; Generator.prototype.extendValidationMethods = function (validator, methods) { var writeMethods = []; ['create', 'update', 'delete'].forEach(function (method) { if (method in methods) { writeMethods.push(ast.andArray([writeAliases[method], methods[method].body])); } }); if (writeMethods.length !== 0) { extendValidator(validator, { '.write': ast.orArray(writeMethods) }); } ['validate', 'read', 'write'].forEach(function (method) { if (method in methods) { var methodValidator = {}; methodValidator['.' + method] = methods[method].body; extendValidator(validator, methodValidator); } }); }; // Return union validator (||) over each schema Generator.prototype.unionValidators = function (schema) { var union = {}; schema.forEach(function (typeName) { // First and the validator terms for a single type // Todo extend to unions and generics var singleType = extendValidator({}, this.ensureValidator(typeName)); mapValidator(singleType, ast.andArray); extendValidator(union, singleType); }.bind(this)); mapValidator(union, ast.orArray); return union; }; // Convert expressions to text, and at the same time, apply pruning operations // to remove no-op rules. Generator.prototype.convertExpressions = function (validator) { var _this = this; var methodThisIs = { '.validate': 'newData', '.read': 'data', '.write': 'newData' }; function hasWildcardSibling(path) { var parts = path.getLabels(); var childPart = parts.pop(); var parent = util.deepLookup(validator, parts); if (parent === undefined) { return false; } for (var _i = 0, _a = Object.keys(parent); _i < _a.length; _i++) { var prop = _a[_i]; if (prop === childPart) { continue; } if (prop[0] === '$') { return true; } } return false; } mapValidator(validator, function (value, prop, scope, path) { if (prop in methodThisIs) { var result = _this.getExpressionText(ast.andArray(collapseHasChildren(value)), methodThisIs[prop], scope, path); // Remove no-op .read or .write rule if no sibling wildcard props. if ((prop === '.read' || prop === '.write') && result === 'false') { if (!hasWildcardSibling(path)) { return undefined; } } // Remove no-op .validate rule if no sibling wildcard props. if (prop === '.validate' && result === 'true') { if (!hasWildcardSibling(path)) { return undefined; } } return result; } return value; }); }; Generator.prototype.getExpressionText = function (exp, thisIs, scope, path) { if (!('type' in exp)) { throw new Error(errors.application + "Not an expression: " + util.prettyJSON(exp)); } // First evaluate w/o binding of this to specific location. this.allowUndefinedFunctions = true; scope = util.extend({}, scope, { 'this': ast.cast(ast.call(ast.variable('@getThis')), 'Snapshot') }); exp = this.partialEval(exp, scope); // Now re-evaluate the flattened expression. this.allowUndefinedFunctions = false; this.thisIs = thisIs; this.symbols.registerFunction('@getThis', [], ast.builtin(this.getThis.bind(this))); this.symbols.registerFunction('@root', [], ast.builtin(this.getRootReference.bind(this, path))); this.symbols.registerFunction('prior', ['exp'], ast.builtin(this.prior.bind(this))); this.symbols.registerFunction('key', [], ast.builtin(this.getKey.bind(this, path.length() === 0 ? '' : path.getPart(-1).label))); exp = this.partialEval(exp); delete this.symbols.functions['@getThis']; delete this.symbols.functions['@root']; delete this.symbols.functions['prior']; delete this.symbols.functions['key']; // Top level expressions should never be to a snapshot reference - should // always evaluate to a boolean. exp = ast.ensureBoolean(exp); return ast.decodeExpression(exp); }; /* * Wrapper for partialEval debugging. */ Generator.prototype.partialEval = function (exp, params, functionCalls) { if (params === void 0) { params = {}; } if (functionCalls === void 0) { functionCalls = {}; } // Wrap real call for debugging. var result = this.partialEvalReal(exp, params, functionCalls); // console.log(ast.decodeExpression(exp) + " => " + ast.decodeExpression(result)); return result; }; // Partial evaluation of expressions - copy of expression tree (immutable). // // - Expand inline function calls. // - Replace local and global variables with their values. // - Expand snapshot references using child('ref'). // - Coerce snapshot references to values as needed. Generator.prototype.partialEvalReal = function (exp, params, functionCalls) { if (params === void 0) { params = {}; } if (functionCalls === void 0) { functionCalls = {}; } var self = this; function subExpression(exp2) { return self.partialEval(exp2, params, functionCalls); } function valueExpression(exp2) { return ast.ensureValue(subExpression(exp2)); } function booleanExpression(exp2) { return ast.ensureBoolean(subExpression(exp2)); } function lookupVar(exp2) { // TODO: Unbound variable access should be an error. return params[exp2.name] || self.globals[exp2.name] || exp2; } // Convert ref[prop] => ref.child(prop) function snapshotChild(ref) { return ast.cast(ast.call(ast.reference(ref.base, ast.string('child')), [ref.accessor]), 'Snapshot'); } switch (exp.type) { case 'op': var expOp = ast.copyExp(exp); // Ensure arguments are boolean (or values) where needed. if (expOp.op === 'value') { expOp.args[0] = valueExpression(expOp.args[0]); } else if (expOp.op === '||' || expOp.op === '&&' || expOp.op === '!') { for (var i = 0; i < expOp.args.length; i++) { expOp.args[i] = booleanExpression(expOp.args[i]); } } else if (expOp.op === '?:') { expOp.args[0] = booleanExpression(expOp.args[0]); expOp.args[1] = valueExpression(expOp.args[1]); expOp.args[2] = valueExpression(expOp.args[2]); } else { for (var i = 0; i < expOp.args.length; i++) { expOp.args[i] = valueExpression(expOp.args[i]); } } return expOp; case 'var': return lookupVar(exp); case 'ref': var expRef = ast.copyExp(exp); expRef.base = subExpression(expRef.base); // var[ref] => var[ref] if (expRef.base.valueType !== 'Snapshot') { expRef.accessor = subExpression(expRef.accessor); return expRef; } var propName = ast.getPropName(expRef); // snapshot.prop (static string property) if (propName !== '') { // snapshot.valueMethod => snapshot.val().valueMethod if (util.arrayIncludes(valueMethods, propName)) { expRef.base = valueExpression(expRef.base); return expRef; } // snapshot.ssMethod => snapshot.ssMethod if (util.arrayIncludes(snapshotMethods, propName)) { return expRef; } } // snapshot[exp] => snapshot.child(exp) or // snapshot[ref] => snapshot.child(ref.val()) expRef.accessor = valueExpression(expRef.accessor); return snapshotChild(expRef); case 'call': var expCall = ast.copyExp(exp); expCall.ref = subExpression(expCall.ref); var callee = this.lookupFunction(expCall.ref); // Expand the function call inline if (callee) { var fn = callee.fn; if (callee.self) { expCall.args.unshift(ast.ensureValue(callee.self)); } if (fn.params.length !== expCall.args.length) { this.fatal(errors.mismatchParams + " ( " + callee.methodName + " expects " + fn.params.length + " but actually passed " + expCall.args.length + ")"); return exp; } if (fn.body.type === 'builtin') { return fn.body.fn(expCall.args, params); } var innerParams = {}; for (var i = 0; i < fn.params.length; i++) { innerParams[fn.params[i]] = subExpression(expCall.args[i]); } if (functionCalls[callee.methodName]) { throw new Error(errors.recursive + " (" + callee.methodName + ")"); } functionCalls[callee.methodName] = true; var result = this.partialEval(fn.body, innerParams, functionCalls); functionCalls[callee.methodName] = false; return result; } // Can't expand function - but just expand the arguments. if (!this.allowUndefinedFunctions) { var funcName = ast.getMethodName(expCall); if (funcName !== '' && !(funcName in this.symbols.schema['String'].methods || util.arrayIncludes(snapshotMethods, funcName))) { this.fatal(errors.undefinedFunction + ast.decodeExpression(expCall.ref)); } } for (var i = 0; i < expCall.args.length; i++) { expCall.args[i] = subExpression(expCall.args[i]); } // Hack for snapshot.parent().val() // Todo - build table-based method signatures. if (ast.getMethodName(expCall) === 'parent') { expCall = ast.cast(expCall, 'Snapshot'); } return expCall; // Expression types (like literals) than need no expansion. default: return exp; } }; // Builtin function - convert all 'this' to 'data' (from 'newData'). // Args are function arguments, and params are the local (function) scope variables. Generator.prototype.prior = function (args, params) { var lastThisIs = this.thisIs; this.thisIs = 'data'; var exp = this.partialEval(args[0], params); this.thisIs = lastThisIs; return exp; }; // Builtin function - current value of 'this' Generator.prototype.getThis = function (args, params) { return ast.snapshotVariable(this.thisIs); }; // Builtin function - ensure type of argument Generator.prototype.ensureType = function (type, args, params) { if (args.length !== 1) { throw new Error(errors.application + "ensureType arguments."); } var exp = this.partialEval(args[0], params); if (exp.type !== type) { throw new Error(errors.coercion + ast.decodeExpression(exp) + " => " + type); } return exp; }; // Builtin function - return the parent key of 'this'. Generator.prototype.getKey = function (key, args, params) { if (args.length !== 0) { throw new Error(errors.mismatchParams + "(found " + args.length + " but expected 1)"); } return key[0] === '$' ? ast.literal(key) : ast.string(key); }; // Builtin function - return the reference to the root // When in read mode - use 'root' // When in write/validate - use path to root via newData.parent()... Generator.prototype.getRootReference = function (path, args, params) { if (args.length !== 0) { throw new Error(errors.application + "@root arguments."); } // 'data' case if (this.thisIs === 'data') { return ast.snapshotVariable('root'); } // TODO(koss): Remove this special case if JSON supports newRoot instead. // 'newData' case - traverse to root via parent()'s. var result = ast.snapshotVariable('newData'); for (var i = 0; i < path.length(); i++) { result = ast.snapshotParent(result); } return result; }; // Lookup globally defined function. Generator.prototype.lookupFunction = function (ref) { // Function call. if (ref.type === 'var') { var refVar = ref; var fn = this.symbols.functions[refVar.name]; if (!fn) { return undefined; } return { self: undefined, fn: fn, methodName: refVar.name }; } // Method call. if (ref.type === 'ref') { var refRef = ref; // TODO: Require static type validation before calling String methods. if (refRef.base.op !== 'value' && refRef.accessor.value in this.symbols.schema['String'].methods) { var methodName = refRef.accessor.value; return { self: refRef.base, fn: this.symbols.schema['String'].methods[methodName], methodName: 'String.' + methodName }; } } return undefined; }; Generator.prototype.fatal = function (s) { logger_1.error(s); this.errorCount += 1; }; return Generator; }()); exports.Generator = Generator; ; // Merge all .X terms into target. function extendValidator(target, src) { if (src === undefined) { throw new Error(errors.application + "Illegal validation source."); } for (var prop in src) { if (!src.hasOwnProperty(prop)) { continue; } if (prop[0] === '.') { if (target[prop] === undefined) { target[prop] = []; } if (util.isType(src[prop], 'array')) { util.extendArray(target[prop], src[prop]); } else { target[prop].push(src[prop]); } } else { if (!target[prop]) { target[prop] = {}; } extendValidator(target[prop], src[prop]); } } return target; } exports.extendValidator = extendValidator; // Call fn(value, prop, path) on all '.props' and assiging the value back into the // validator. function mapValidator(v, fn, scope, path) { if (!scope) { scope = {}; } if (!path) { path = new ast.PathTemplate(); } if ('.scope' in v) { scope = v['.scope']; } for (var prop in v) { if (!v.hasOwnProperty(prop)) { continue; } if (prop[0] === '.') { var value = fn(v[prop], prop, scope, path); if (value !== undefined) { v[prop] = value; } else { delete v[prop]; } } else if (!util.isType(v[prop], 'object')) { continue; } else { var child = new ast.PathTemplate([prop]); path.push(child); mapValidator(v[prop], fn, scope, path); path.pop(child); } } } exports.mapValidator = mapValidator; // Collapse all hasChildren calls into one (combining their arguments). // E.g. [newData.hasChildren(), newData.hasChildren(['x']), newData.hasChildren(['y'])] => // newData.hasChildren(['x', 'y']) function collapseHasChildren(exps) { var hasHasChildren = false; var combined = []; var result = []; exps.forEach(function (exp) { if (exp.type !== 'call') { result.push(exp); return; } var expCall = exp; if (ast.getMethodName(expCall) !== 'hasChildren') { result.push(exp); return; } if (expCall.args.length === 0) { hasHasChildren = true; return; } // Expect one argument of Array type. if (expCall.args.length !== 1 || expCall.args[0].type !== 'Array') { throw new Error(errors.application + "Invalid argument to hasChildren(): " + expCall.args[0].type); } var args = expCall.args[0].value; args.forEach(function (arg) { hasHasChildren = true; if (arg.type !== 'String') { throw new Error(errors.application + "Expect string argument to hasChildren(), not: " + arg.type); } combined.push(arg.value); }); }); if (hasHasChildren) { result.unshift(hasChildrenExp(combined)); } return result; } // Generate this.hasChildren([props, ...]) or this.hasChildren() function hasChildrenExp(props) { var args = props.length === 0 ? [] : [ast.array(props.map(ast.string))]; return ast.call(ast.reference(ast.cast(ast.variable('this'), 'Any'), ast.string('hasChildren')), args); } //# sourceMappingURL=data:application/json;charset=utf8;base64,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