error-polyfill
Version:
Javascript Error Polyfill
133 lines (127 loc) • 5.2 kB
JavaScript
var Class = require("o3").Class,
abstractMethod = require("o3").abstractMethod,
eachCombination = require("u3").eachCombination,
cache = require("u3").cache,
capability = require("capability");
var AbstractFrameStringSource = Class(Object, {
prototype: {
captureFrameStrings: function (frameShifts) {
var error = this.createError();
frameShifts.unshift(this.captureFrameStrings);
frameShifts.unshift(this.createError);
var capturedFrameStrings = this.getFrameStrings(error);
var frameStrings = capturedFrameStrings.slice(frameShifts.length),
functionValues = [];
if (capability("arguments.callee.caller")) {
var capturedFunctionValues = [
this.createError,
this.captureFrameStrings
];
try {
var aCaller = arguments.callee;
while (aCaller = aCaller.caller)
capturedFunctionValues.push(aCaller);
}
catch (useStrictError) {
}
functionValues = capturedFunctionValues.slice(frameShifts.length);
}
return {
frameStrings: frameStrings,
functionValues: functionValues
};
},
getFrameStrings: function (error) {
var message = error.message || "";
var name = error.name || "";
var stackString = this.getStackString(error);
if (stackString === undefined)
return;
var stackStringChunks = stackString.split("\n");
var fromPosition = 0;
var toPosition = stackStringChunks.length;
if (this.hasHeader)
fromPosition += name.split("\n").length + message.split("\n").length - 1;
if (this.hasFooter)
toPosition -= 1;
return stackStringChunks.slice(fromPosition, toPosition);
},
createError: abstractMethod,
getStackString: abstractMethod,
hasHeader: undefined,
hasFooter: undefined
}
});
var FrameStringSourceCalibrator = Class(Object, {
prototype: {
calibrateClass: function (FrameStringSource) {
return this.calibrateMethods(FrameStringSource) && this.calibrateEnvelope(FrameStringSource);
},
calibrateMethods: function (FrameStringSource) {
try {
eachCombination([[
function (message) {
return new Error(message);
},
function (message) {
try {
throw new Error(message);
}
catch (error) {
return error;
}
}
], [
function (error) {
return error.stack;
},
function (error) {
return error.stacktrace;
}
]], function (createError, getStackString) {
if (getStackString(createError()))
throw {
getStackString: getStackString,
createError: createError
};
});
} catch (workingImplementation) {
Class.merge.call(FrameStringSource, {
prototype: workingImplementation
});
return true;
}
return false;
},
calibrateEnvelope: function (FrameStringSource) {
var getStackString = FrameStringSource.prototype.getStackString;
var createError = FrameStringSource.prototype.createError;
var calibratorStackString = getStackString(createError("marker"));
var calibratorFrameStrings = calibratorStackString.split("\n");
Class.merge.call(FrameStringSource, {
prototype: {
hasHeader: /marker/.test(calibratorFrameStrings[0]),
hasFooter: calibratorFrameStrings[calibratorFrameStrings.length - 1] === ""
}
});
return true;
}
}
});
module.exports = {
getClass: cache(function () {
var FrameStringSource;
if (FrameStringSource)
return FrameStringSource;
FrameStringSource = Class(AbstractFrameStringSource, {});
var calibrator = new FrameStringSourceCalibrator();
if (!calibrator.calibrateClass(FrameStringSource))
throw new Error("Cannot read Error.prototype.stack in this environment.");
return FrameStringSource;
}),
getInstance: cache(function () {
var FrameStringSource = this.getClass();
var instance = new FrameStringSource();
return instance;
})
};