UNPKG

@getanthill/datastore

Version:

Event-Sourced Datastore

573 lines (508 loc) 15.9 kB
// @ts-nocheck /** * source: https://github.com/codalien/operator-overloading-js/blob/master/lib/overload.js */ import * as espree from 'espree'; import escodegen = require('escodegen'); import FullyHomomorphicEncryptionClient from '.'; const FUNC_NAMES = { '+': '__plus', '==': '__doubleEqual', '===': '__tripleEqual', '||': '__logicalOR', '&&': '__logicalAND', '|': '__bitwiseOR', '^': '__bitwiseXOR', '&': '__bitwiseAND', '!=': '__notEqual', '!==': '__notDoubleEqual', '<': '__lessThan', '>': '__greaterThan', '<=': '__lessThanEqual', '>=': '__greaterThanEqual', in: '__in', instanceof: '__instanceOf', '<<': '__bitwiseLSHIFT', '>>': '__bitwiseRSHIFT', '>>>': '__zeroFillRSHIFT', '-': '__minus', '*': '__multiply', '%': '__modulus', '/': '__divide', 'u-': '__unaryNegation', 'u+': '__unaryAddition', '~': '__bitwiseNOT', '++': '__increment', '--': '__decrement', '!': '__unaryNOT', '+=': '__addAssign', '-=': '__minusAssign', '*=': '__multiplyAssign', '/=': '__divideAssign', '%=': '__modulusAssign', '<<=': '__leftShiftAssign', '>>=': '__rightShiftAssign', '>>>=': '__zeroFillRightShiftAssign', '&=': '__andAssign', '|=': '__orAssign', '^=': '__xorAssign', }; //The AST Walker And Transformer function visit(statement, index, program, client) { switch (statement.type) { case 'VariableDeclaration': statement.declarations.forEach(function (declaration, idx) { visit(declaration.init, idx, program, client); }); break; case 'BinaryExpression': case 'LogicalExpression': if (statement.operator && FUNC_NAMES[statement.operator]) { statement.type = 'CallExpression'; statement.callee = { type: 'MemberExpression', computed: false, object: statement.right, property: { type: 'Identifier', name: FUNC_NAMES[statement.operator], }, }; visit(statement.left, index, program, client); visit(statement.right, index, program, client); statement['arguments'] = [statement.left]; } else { visit(statement.left, index, program, client); visit(statement.right, index, program, client); } break; case 'ExpressionStatement': visit(statement.expression, index, program, client); break; case 'CallExpression': statement['arguments'].forEach(function (argument, idx) { visit(argument, idx, program, client); }); visit(statement.callee, index, program, client); if (statement.callee.type === 'Identifier') { statement.callee = { type: 'MemberExpression', computed: false, object: statement.callee, property: { type: 'Identifier', name: 'call' }, }; statement.arguments = [ { type: 'ThisExpression' }, ...statement.arguments, ]; } break; case 'AssignmentExpression': if (statement.operator && FUNC_NAMES[statement.operator]) { statement.right = { type: 'CallExpression', callee: { type: 'MemberExpression', computed: false, object: statement.left, property: { type: 'Identifier', name: FUNC_NAMES[statement.operator], }, }, arguments: [statement.right], }; statement.operator = '='; visit(statement.left, index, program, client); visit(statement.right.arguments[0], index, program, client); } else { visit(statement.right, index, program, client); } break; case 'UnaryExpression': if (statement.operator && FUNC_NAMES[statement.operator]) { statement.type = 'CallExpression'; statement.callee = { type: 'MemberExpression', computed: false, object: statement.argument, property: { type: 'Identifier', name: statement.operator === '+' || statement.operator === '-' ? FUNC_NAMES['u' + statement.operator] : FUNC_NAMES[statement.operator], }, }; visit(statement.argument, index, program, client); statement['arguments'] = []; } else { visit(statement.argument, index, program, client); } break; case 'UpdateExpression': if (statement.operator && FUNC_NAMES[statement.operator]) { statement.type = 'CallExpression'; statement.callee = { type: 'MemberExpression', computed: false, object: statement.argument, property: { type: 'Identifier', name: FUNC_NAMES[statement.operator], }, }; visit(statement.argument, index, program, client); statement['arguments'] = []; } break; case 'FunctionDeclaration': case 'FunctionExpression': visit(statement.body, index, program, client); break; case 'BlockStatement': statement.body.forEach(function (statement) { visit(statement, index, program, client); }); break; case 'ReturnStatement': visit(statement.argument, index, program, client); break; case 'MemberExpression': visit(statement.object, index, program, client); break; case 'SwitchStatement': statement.cases.forEach(function (_case, idx) { visit(_case, idx, program, client); }); break; case 'SwitchCase': statement.consequent.forEach(function (con, idx) { visit(con, idx, program, client); }); break; case 'Literal': break; statement.type = 'NewExpression'; statement.callee = { type: 'MemberExpression', computed: false, object: { type: 'ThisExpression' }, //property: { type: 'Identifier', name: 'HEValue' }, property: { type: 'Identifier', name: 'HEValue' }, }; statement.arguments = [ { type: 'ThisExpression' }, { type: 'CallExpression', callee: { type: 'MemberExpression', computed: false, object: { type: 'ThisExpression' }, property: { type: 'Identifier', name: 'createCypher' }, }, arguments: [ { type: 'Literal', value: statement.value, raw: `${statement.value}`, }, ], }, ]; break; //We don't need to transform following nodes! Phew! case 'Identifier': case 'ThisExpression': case 'NewExpression': case 'AwaitExpression': break; } } //Do the magic export function overload(func, client) { //Generate AST const ast = espree.parse('let fn = ' + func, { ecmaVersion: 11 }); //Check for AST if (!ast) throw new Error( 'Invalid code block! Cannot overload. AST Generation Error.', ); //Fetch arguments var args = ast.body[0].declarations[0].init.params.reduce(function ( init, val, ) { init.push(val.name); return init; }, []); //Fetch function body var body = ast.body[0].declarations[0].init.body; //Build the desired program var program = { type: 'Program', body: body.body, }; //Transform program.body.forEach(function (statement, index) { visit(statement, index, program, client); }); //Build new function args args.push( escodegen.generate(program, { comment: true, format: { indent: { style: ' ', }, }, }), ); const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor; var retFn = AsyncFunction(...args); if (process.env.OVERLOAD_DEBUG) console.log(JSON.stringify(program, null, 4)); if (process.env.OVERLOAD_DEBUG) console.log(retFn.toString()); return retFn; } export function defaultOperators( client: FullyHomomorphicEncryptionClient, constructors = [Object, Number, String, Function, RegExp], ) { function defineDefaultProp(constructor, name, val) { Object.defineProperty(constructor.prototype, name, { enumerable: false, writable: true, configurable: false, value: val, }); } const mapValue = (value) => { if (typeof value === 'number' || value instanceof Number) { const cipher = client.createCypher( Array(client.polyModulusDegree).fill(value), ); client.addInstance(cipher); return cipher; } if (typeof value === 'string' || value instanceof String) { const cipher = client.seal.CipherText(); cipher.load(client.context!, value.toString()); client.addInstance(cipher); return cipher; } return value; }; constructors.forEach(function (constructor) { defineDefaultProp(constructor, FUNC_NAMES['+'], function (left) { // Map entry const _left = mapValue(left); const _right = mapValue(this); // Perform the operation const resultCipher = client.relinearize( client.evaluator.add(_left, _right)!, )!; return resultCipher; // Save const result = resultCipher.save(); return result; }); defineDefaultProp(constructor, FUNC_NAMES['=='], function (left) { return left == this; }); defineDefaultProp(constructor, FUNC_NAMES['==='], function (left) { return left === this; }); defineDefaultProp(constructor, FUNC_NAMES['||'], function (left) { return left || this; }); defineDefaultProp(constructor, FUNC_NAMES['&&'], function (left) { return left && this; }); defineDefaultProp(constructor, FUNC_NAMES['&'], function (left) { return left & this; }); defineDefaultProp(constructor, FUNC_NAMES['|'], function (left) { return left | this; }); defineDefaultProp(constructor, FUNC_NAMES['^'], function (left) { return left ^ this; }); defineDefaultProp(constructor, FUNC_NAMES['!='], function (left) { return left != this; }); defineDefaultProp(constructor, FUNC_NAMES['!=='], function (left) { return left !== this; }); defineDefaultProp(constructor, FUNC_NAMES['<'], function (left) { return left < this; }); defineDefaultProp(constructor, FUNC_NAMES['>'], function (left) { return left > this; }); defineDefaultProp(constructor, FUNC_NAMES['>>'], function (left) { return left >> this; }); defineDefaultProp(constructor, FUNC_NAMES['<<'], function (left) { return left << this; }); defineDefaultProp(constructor, FUNC_NAMES['>>>'], function (left) { return left >>> this; }); defineDefaultProp(constructor, FUNC_NAMES['<='], function (left) { return left <= this; }); defineDefaultProp(constructor, FUNC_NAMES['>='], function (left) { return left >= this; }); defineDefaultProp(constructor, FUNC_NAMES['in'], function (left) { return left in this; }); defineDefaultProp(constructor, FUNC_NAMES['instanceof'], function (left) { return left instanceof this; }); defineDefaultProp(constructor, FUNC_NAMES['-'], function (left) { // Map entry const _left = mapValue(left); const _right = mapValue(this); // Perform the operation const resultCipher = client.relinearize( client.evaluator.sub(_left, _right)!, )!; return resultCipher; // Save const result = resultCipher.save(); return result; }); defineDefaultProp(constructor, FUNC_NAMES['*'], function (left) { // Map entry const _left = mapValue(left); const _right = mapValue(this); // Perform the operation const resultCipher = client.relinearize( client.evaluator.multiply(_left, _right)!, )!; return resultCipher; // Save const result = resultCipher.save(); return result; }); defineDefaultProp(constructor, FUNC_NAMES['%'], function (left) { return left % this; }); defineDefaultProp(constructor, FUNC_NAMES['/'], function (left) { return left / this; }); defineDefaultProp(constructor, FUNC_NAMES['u-'], function () { const _right = mapValue(this); // Perform the operation const resultCipher = client.relinearize( client.evaluator.negate(_right)!, )!; return resultCipher; // Save const result = resultCipher.save(); return result; }); defineDefaultProp(constructor, FUNC_NAMES['u+'], function () { return +this; }); defineDefaultProp(constructor, FUNC_NAMES['~'], function () { return ~this; }); defineDefaultProp(constructor, FUNC_NAMES['++'], function () { var val = this; ++val; return val; }); defineDefaultProp(constructor, FUNC_NAMES['--'], function () { var val = this; --val; return val; }); defineDefaultProp(constructor, FUNC_NAMES['!'], function () { return !this; }); defineDefaultProp(constructor, FUNC_NAMES['+='], function (left) { // Map entry const _left = mapValue(left); const _right = mapValue(this); // Perform the operation const resultCipher = client.relinearize( client.evaluator.add(_left, _right)!, )!; return resultCipher; // Save const result = resultCipher.save(); return result; return (left += this); }); defineDefaultProp(constructor, FUNC_NAMES['-='], function (left) { // Map entry const _left = mapValue(left); const _right = mapValue(this); // Perform the operation const resultCipher = client.relinearize( client.evaluator.sub(_right, _left)!, )!; return resultCipher; // Save const result = resultCipher.save(); return result; return (left -= this); }); defineDefaultProp(constructor, FUNC_NAMES['*='], function (left) { // Map entry const _left = mapValue(left); const _right = mapValue(this); // Perform the operation const resultCipher = client.relinearize( client.evaluator.multiply(_left, _right)!, )!; return resultCipher; // Save const result = resultCipher.save(); return result; return (left *= this); }); defineDefaultProp(constructor, FUNC_NAMES['/='], function (left) { return (left /= this); }); defineDefaultProp(constructor, FUNC_NAMES['%='], function (left) { return (left %= this); }); defineDefaultProp(constructor, FUNC_NAMES['<<='], function (left) { return (left <<= this); }); defineDefaultProp(constructor, FUNC_NAMES['>>='], function (left) { return (left >>= this); }); defineDefaultProp(constructor, FUNC_NAMES['>>>='], function (left) { return (left >>>= this); }); defineDefaultProp(constructor, FUNC_NAMES['&='], function (left) { return (left &= this); }); defineDefaultProp(constructor, FUNC_NAMES['|='], function (left) { return (left |= this); }); defineDefaultProp(constructor, FUNC_NAMES['^='], function (left) { return (left ^= this); }); }); } export function overloadAsString(func, args, client) { const overloadedFunc = overload(func, client); const rawScript = ` const FUNC_NAMES = ${JSON.stringify(FUNC_NAMES)}; ${defaultOperators.toString()}; // Standard constructors overload: defaultOperators(client); // Arguments constructors overload: defaultOperators(client, args.map((arg) => !!arg ? arg.constructor : Object)); ${overloadedFunc.toString()}; anonymous.call(client, ${Array(args.length) .fill(1) .map((v, i) => `args[${i}]`) .join(', ')}).then((res) => resolve(res));`; return rawScript; }