UNPKG

webppl

Version:

Probabilistic programming for the web

114 lines (97 loc) 3.31 kB
'use strict'; var assert = require('assert'); var _ = require('lodash'); var SourceMap = require('source-map'); var util = require('../util'); function extendError(error, assets, currentAddress) { if (error instanceof Error) { error.sourceMap = assets.sourceMap; error.wpplCallStack = addressToWpplCallStack(currentAddress.value, assets.addressMap); error.wpplRuntimeError = true; } } function addressToStack(address) { if (address === undefined) { return []; } var addUnderscore = function(s) { return '_' + s; }; var split = address.split('_').slice(1).map(addUnderscore); split.reverse(); return split; } function addressToWpplCallStack(address, addressMap) { var stack = addressToStack(address); return _.chain(stack).map(function(id) { var loc = addressMap[id]; return loc && loc.start && { fileName: loc.source, lineNumber: loc.start.line, columnNumber: loc.start.column, native: false, webppl: true, name: loc.name, id: id }; }).filter().value(); } function recoverStack(error, parseStack) { return _.flow([ parseStack, filterJsStackTrace, function(s) { return sourceMapJsStackTrace(s, error.sourceMap); }, function(s) { return s.concat(error.wpplCallStack); } ])(error); } function filterJsStackTrace(stackTrace) { // Takes a parsed stack trace (see v8.js for an example) and removes // entries not useful for debugging webppl programs. This means // taking the top-most entry corresponding to a function application // in webppl code, and any frames above it. // When no entry corresponding to application of a webppl function // is present, we return the entire stack. The reason this is // possible is that (by default in V8) only the top 10 stack frames // are captured on error. // We can only ever take the top-most webppl frame as any earlier // frames may have been generated on a different execution path. // (And besides, there should only be one entry as the JS stack is // cleared between each webppl function application.) var ix = _.findIndex(stackTrace, _.matches({webppl: true})); return (ix >= 0) ? stackTrace.slice(0, ix + 1) : stackTrace; } function filterGensym(name) { return name === null || name.slice(0, 7) === '_result' ? null : name; } function sourceMapJsStackTrace(stackTrace, sourceMap) { // Takes a parsed stack trace and rewrites webppl entries to refer // to their original location in the source program. var map = new SourceMap.SourceMapConsumer(sourceMap); return _.chain(stackTrace).map(function(entry) { if (entry.webppl) { var pos = map.originalPositionFor({ line: entry.lineNumber, column: entry.columnNumber }); if (positionPopulated(pos)) { return { fileName: pos.source, lineNumber: pos.line, columnNumber: pos.column, native: entry.native, webppl: true, name: filterGensym(pos.name) }; } else { return null; } } else { return entry; } }).filter().value(); } function positionPopulated(pos) { return pos.source !== null && pos.line !== null && pos.column !== null; } module.exports = { recoverStack: recoverStack, extendError: extendError };