@getanthill/datastore
Version:
Event-Sourced Datastore
573 lines (508 loc) • 15.9 kB
text/typescript
// @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;
}