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

114 lines (101 loc) 3.52 kB
import { expect } from 'chai'; import { parseScript } from 'shift-parser'; import { Interpreter } from '../src/interpreter'; import { BasicContext } from '../src/context'; import deepEqual from 'deep-equal'; import DEBUG from 'debug'; const debug = DEBUG('shift:interpreter:test'); const evaluate = require('./nostrict-eval.js').eval; export interface Result { actual: any; actualError: Error; expected: any; expectedError: Error; src: string; success: boolean; interpreter: Interpreter; } export function assertResult(result: Result) { const message = result.expectedError ? `${result.src}: Actual "${result.actualError.message}", Expected "${result.expectedError.message}"` : `${result.src}: Actual ${JSON.stringify(result.actual)}, Expected ${JSON.stringify(result.expected)}`; expect(result.success).to.equal(true, message); } export function assertError(src: string, error: string) { debug(`assertError(\`${src}\`)`); const interpreter = new Interpreter(); let expected = 'No error', actual = 'No error'; try { evaluate(src); } catch (e) { expected = e.message; } try { interpreter.run(parseScript(src)); } catch (e) { actual = e.message; } if (actual) debug(`Interpreter error: ${actual}`); if (expected) debug(`Native error: ${expected}`); expect(actual).to.equal(expected); } function funcify(fn: Function) { const src = fn.toString(); return `(${src})()`; } export function compare(src: string | Function, context?: BasicContext): Result { const interpreter = new Interpreter(); let nativeExpectedValue, nativeExpectedError; debug(`compare(\`${src}\`)`); try { nativeExpectedValue = evaluate(src, context); } catch (e) { nativeExpectedError = e; } let interpreterActualValue, interpreterActualError; let finalSrc = typeof src === 'string' ? src : funcify(src); try { interpreter.load(parseScript(finalSrc), context); interpreterActualValue = interpreter.run(); } catch (e) { interpreterActualError = e; } debug(`== Interpreter result: ${interpreterActualValue}`); debug(`== Native result : ${nativeExpectedValue}`); if (interpreterActualError) debug(`!! Interpreter error: ${interpreterActualError.message}`); else debug(`!! Interpreter error: <none>`); if (nativeExpectedError) debug(`!! Native error : ${nativeExpectedError.message}`); else debug(`!! Native error : <none>`); let success = false; if (Number.isNaN(nativeExpectedValue)) { success = Number.isNaN(interpreterActualValue); debug(`Interpreter produced NaN, Native produced ${interpreterActualValue}`); } else if (nativeExpectedError) { if (!interpreterActualError) { debug(`Failure: Native produced error, Interpreter did not`); interpreterActualError = { message: '<<Did not throw an error>>' }; success = false; } else { success = interpreterActualError.message === nativeExpectedError.message; debug(`Both produced errors (same===${success})`); } } else { if (interpreterActualError) { debug(`Failure: Interpreter produced error, Native did not`); console.log(interpreterActualError); success = false; } else { success = deepEqual(nativeExpectedValue, interpreterActualValue); } } return { actual: interpreterActualValue, actualError: interpreterActualError, expected: nativeExpectedValue, expectedError: nativeExpectedError, src: finalSrc, success, interpreter, }; }