UNPKG

shift-interpreter

Version:

Shift-interpreter is an experimental JavaScript meta-interpreter useful for reverse engineering and analysis. One notable difference from other projects is that shift-interpreter retains state over an entire script but can be fed expressions and statement

167 lines 6.33 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const chai_1 = __importDefault(require("chai")); const shift_parser_1 = require("shift-parser"); const src_1 = require("../../src"); const util_1 = require("../util"); describe('Functions', () => { it('should declare functions', () => { util_1.assertResult(util_1.compare('function a(){return 2}; a();')); }); it('should hoist functions for purposes of member access/assignment', () => { util_1.assertResult(util_1.compare("a.foo = 'bar'; function a(){}; a.foo;")); }); it('should use global context if called with undefined context', () => { util_1.assertResult(util_1.compare(`'();'.replace('()', function(){})`)); }); it('declarations in block scope should be defined across all scope Variable references', () => { // shift-scope defines multiple variables for block scopes, references should be the same all around. util_1.assertResult(util_1.compare(`let a = '';{function fn() {return 'decl'}a+=fn()}a+=fn();a;`)); util_1.assertResult(util_1.compare(`let inner;{function fn() {}inner=fn}let outer = fn; outer === inner`)); }); it('should call methods on primitive literals', () => { util_1.assertResult(util_1.compare("'a'.replace('a','b');")); }); it('should call methods on primitive return values', () => { util_1.assertResult(util_1.compare("fn = () => 'a';fn().replace('a','b');")); }); it('should assign arrow expressions', () => { util_1.assertResult(util_1.compare('let a = (a) => {return a}; a(4)')); }); it('arrow expressions should retain `this` binding', () => { util_1.assertResult(util_1.compare('let a = { a: () => {return this.b}, b: 44 }; const b = a.a; b();')); }); it('should evaluate shorthand arrow expressions', () => { util_1.assertResult(util_1.compare('let a = _ => _ + 10; a(2);')); }); it('should call functions with arguments that have defaults', () => { util_1.assertResult(util_1.compare('function fn(a = 22){return a + 10}; fn() + fn(33);')); }); it('should call functions with arguments', () => { util_1.assertResult(util_1.compare('function a(a,b){return a+b}; a(2,5) === 7;')); }); it('should allow reference to arguments special variable', () => { util_1.assertResult(util_1.compare('function a(b){return arguments[0] + 10}; a(33);')); }); it('should support .call()', () => { util_1.assertResult(util_1.compare(` var context = { expected: "hello", }; function dyno(prop) { return this[prop]; } dyno.call(context, "expected"); `)); }); it('should support prototype modifications', () => { util_1.assertResult(util_1.compare(` function d() {} d.prototype.run = function () {return "expected"}; d.prototype.run(); `)); }); it('should support .apply()', () => { util_1.assertResult(util_1.compare(` var context = { expected: "hello", }; function dyno(prop) { return this[prop]; } dyno.apply(context, ["expected"]); `)); }); it('should access appropriate context', () => { util_1.assertResult(util_1.compare(` var c = { expected: "hello", test: function(actual) { return actual === c.expected; } }; c.test("hello") === true; `)); util_1.assertResult(util_1.compare(` var c = { expected: "hello", test: function(actual) { return actual === c.expected; } }; var b = { expected: "on b" }; b.test = c.test; b.test('on b') === true; `)); }); it('should hoist functions', () => { const src = 'a.b = 2; function a(){}'; const ast = shift_parser_1.parseScript(src); const interpreter = new src_1.Interpreter(); interpreter.load(ast); interpreter.run(); const fnDecl = ast.statements.find((st) => st.type === 'FunctionDeclaration'); const fn = () => { const value = interpreter.getRuntimeValue(fnDecl.name); chai_1.default.expect(value.b).to.equal(2); }; chai_1.default.expect(fn).to.not.throw(); }); it('should store and execute function expressions', () => { util_1.assertResult(util_1.compare('let a = function(){return 2}; a();')); }); it('should return from sub statements', () => { util_1.assertResult(util_1.compare("function a() { if (true) return 'in branch'; return 'should not get here'}; a();")); }); it('should return from sub blocks', () => { util_1.assertResult(util_1.compare(` function _isSameValue(a, b) { if (a === b) { return true; } return false; }; _isSameValue("1","1"); `)); }); }); describe('Getters/Setters', () => { it('should define getters', () => { util_1.assertResult(util_1.compare('let a = { get b() {return 2} }; a.b;')); }); it('should define setters', () => { util_1.assertResult(util_1.compare('let holder = { set property(argument) {this._secretProp = argument} }; holder.property = 22; false /* dummy expression to catch promise-based setter regression */; holder._secretProp')); }); it('should register setters properly on host objects', () => { const tree = shift_parser_1.parseScript(`holder = { set property(argument) {this._secretProp = argument} };`); //@ts-ignore const objectExpression = tree.statements[0].expression; const interpreter = new src_1.Interpreter(); interpreter.load(tree); const obj = interpreter.evaluate(objectExpression); obj.property = 22; chai_1.default.expect(obj._secretProp).to.equal(22); }); it('should define both', () => { const src = ` let a = { _b: 0, set b(c) { this._b = c + 10; }, get b() { return this._b; }, }; a.b = 22; a.b; `; util_1.assertResult(util_1.compare(src)); }); }); //# sourceMappingURL=functions.test.js.map