firebase-bolt
Version:
Firebase Bolt Security and Modeling Language Compiler
854 lines (853 loc) • 130 kB
JavaScript
"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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