/** * This singleton provides the ability to convert the parse tree of an `Ext.data.Query` * (that is, its `ast` or Abstract Syntax Tree) into a `Function` that accepts an object * or {@link Ext.data.Model record} and returns a `Boolean` if the query matches it. * @private * @since 6.7.0 */Ext.define('Ext.data.query.Compiler', { compile: function() { /* O = Operation helpers F = Functions function create (Ext, O, F) { return function (item) { return ( 4 && ( ) ); }; } */ var me = this, ast = me.ast, body, factory, vars; me.error = null; if (!ast) { me.fn = Ext.returnTrue; } else { body = [ 'return function (item) {', '\tvar rec = item.isEntity && item;', '\treturn ' ]; vars = []; me.query = me; me.assemble(body, vars, '\t', ast); body.push('}'); body = vars.concat(body).join('\n'); try { factory = new Function('Ext', 'O', 'F', body); me.fn = factory(Ext, me.operators, me.getFunctions()); me.fn.generation = me.generation; } catch (e) { me.error = e; e.message = 'Failed to compile: ' + e.message; throw e; } finally { me.query = null; } } }, privates: { asmOps: { '>': 'gt', '<': 'lt', '==': 'eq', '>=': 'ge', '<=': 'le', '!=': 'ne' }, assemblers: { binary: function(me, body, vars, indent, node, last, childIndent) { var op = me.operatorTypeMap[node.type][1], asmOp = me.asmOps[op], operands = node.on, close = '', i; if (asmOp) { body[last] += 'O.' + asmOp + '('; op = ', '; close = ')'; } else { op = ' ' + op + ' '; } body[last] += '('; for (i = 0; i < operands.length; ++i) { if (i) { body.push(indent + ')' + op + '('); } body.push(childIndent); me.assemble(body, vars, childIndent, operands[i]); } body.push(indent + ')' + close); }, between: function(me, body, vars, indent, node, last, childIndent) { var operands = node.on, i; body[last] += 'O.between('; for (i = 0; i < 3; ++i) { if (i) { last = body.length - 1; body[last] += ', '; } me.assemble(body, vars, childIndent, operands[i]); } body.push(indent + ')'); }, fn: function(me, body, vars, indent, node, last, childIndent) { var fn = node.fn.toLowerCase(), func = me.query.getFunctions(), exprs, i; //<debug> if (!func[fn]) { Ext.raise('Unsupported function "' + node.fn + '"'); } //</debug> func = func[fn]; if (func.vargs) { body[last] += 'F.' + fn + '.fn(['; } else { body[last] += 'F.' + fn + '.fn('; } exprs = node.args; for (i = 0; i < exprs.length; ++i) { if (i) { last = body.length - 1; body[last] += ', '; } body.push(childIndent); me.assemble(body, vars, childIndent, exprs[i]); } if (func.vargs) { body.push(indent + '])'); } else { body.push(indent + ')'); } }, id: function(me, body, vars, indent, node, last, childIndent) { var v = node.value, exprs = v.split('.'); if (exprs.length === 1) { body[last] += 'rec ? rec.interpret(' + Ext.JSON.encode(v) + ') : item.' + v; } else { v = 'p' + vars.length; vars.push('var ' + v + ' = ' + Ext.JSON.encode(exprs) + ';'); body[last] += 'O.dots(item, ' + v + ')'; } }, 'in': function(me, body, vars, indent, node, last, childIndent) { var operands = node.on; body[last] += 'O.in('; me.assemble(body, vars, childIndent, operands[0]); last = body.length - 1; body[last] += ', '; me.assemble(body, vars, childIndent, operands[1]); body.push(indent + ')'); }, like: function(me, body, vars, indent, node, last, childIndent) { var operands = node.on, rhs; body[last] += 'O.like('; me.assemble(body, vars, childIndent, operands[0]); last = body.length - 1; body[last] += ', '; rhs = operands[1]; if (rhs.re) { rhs = { type: 'regexp', value: rhs.re, flags: rhs.flags }; } me.assemble(body, vars, childIndent, rhs); last = body.length - 1; body[last] += ') '; }, list: function(me, body, vars, indent, node, last, childIndent) { body[last] += '['; // eslint-disable-next-line vars-on-top for (var i = 0, exprs = node.value; i < exprs.length; ++i) { if (i) { last = body.length - 1; body[last] += ', '; } body.push(childIndent); me.assemble(body, vars, childIndent, exprs[i]); } body.push(indent + ']'); }, string: 'regexp', regexp: function(me, body, vars, indent, node, last) { var re = 're' + vars.length; vars.push('var ' + re + ' = /' + (node.re || node.value) + '/' + (node.flags || '') + ';'); body[last] += re; }, unary: function(me, body, vars, indent, node, last, childIndent) { var op = me.operatorTypeMap[node.type][1], operands = node.on; body[last] += op + '('; body.push(childIndent); me.assemble(body, vars, childIndent, operands); body.push(indent + ')'); } }, /** * These methods are used by the compiled function to process certain operators. * @private */ operators: { between: function(val, lo, hi) { return lo <= val && val <= hi; }, dots: function(item, names) { var i, ret; if (item.isEntity) { for (ret = item, i = 0; i < names.length; ++i) { if (!ret || !ret.interpret) { ret = undefined; // don't return '', 0 or false break; } ret = ret.interpret(names[i]); } } else { for (ret = item, i = 0; i < names.length; ++i) { if (!ret) { ret = undefined; break; } ret = ret[names[i]]; } } return ret; }, 'in': function(val, values) { return Ext.Array.contains(values, val); }, like: function(val, pat) { val = String(val); if (typeof pat === 'string') { return !!val && val.toLowerCase().indexOf(pat.toLowerCase()) > -1; } return pat.test(val); }, //-------------------------------- eq: function(lhs, rhs) { if (lhs && rhs && (lhs instanceof Date || rhs instanceof Date)) { return !Ext.Date.compare(lhs, rhs); } return lhs == rhs; // eslint-disable-line eqeqeq }, ge: function(lhs, rhs) { if (lhs && rhs && (lhs instanceof Date || rhs instanceof Date)) { return Ext.Date.compare(lhs, rhs) >= 0; } return lhs >= rhs; }, gt: function(lhs, rhs) { if (lhs && rhs && (lhs instanceof Date || rhs instanceof Date)) { return Ext.Date.compare(lhs, rhs) > 0; } return lhs > rhs; }, le: function(lhs, rhs) { if (lhs && rhs && (lhs instanceof Date || rhs instanceof Date)) { return Ext.Date.compare(lhs, rhs) <= 0; } return lhs <= rhs; }, lt: function(lhs, rhs) { if (lhs && rhs && (lhs instanceof Date || rhs instanceof Date)) { return Ext.Date.compare(lhs, rhs) < 0; } return lhs < rhs; }, ne: function(lhs, rhs) { if (lhs && rhs && (lhs instanceof Date || rhs instanceof Date)) { return !!Ext.Date.compare(lhs, rhs); } return lhs != rhs; // eslint-disable-line eqeqeq } }, assemble: function(body, vars, indent, node) { var me = this, assemblers = me.assemblers, t = typeof node, last = body.length - 1, childIndent = indent + '\t', type = node.type, arity, asm; if (t === 'boolean' || t === 'number') { body[last] += node; } else if (t === 'string') { body[last] += Ext.JSON.encode(node); } else { arity = me.operatorTypeMap[type]; asm = assemblers[type] || (arity && assemblers[arity[0]]); if (typeof asm === 'string') { asm = assemblers[asm]; } asm(me, body, vars, indent, node, body.length - 1, childIndent); } } }});