From 6b27ce4837105f6a26eb5cd8df141f601aa3df28 Mon Sep 17 00:00:00 2001 From: Gvozd Date: Sun, 26 Feb 2017 18:49:01 +0300 Subject: [PATCH] Optimize performance of call-stack getting call stack not actually needed at most cases, but got always get(actually only first get) call stack from error is so slow operation, and may be moved on-demand getter Please see this simple example and his performance: https://gist.github.com/Gvozd/291e4f0adf247ff70ed72150059e4977 with node: before commit: 810-840ms now: 170-180ms with babel-node(slowe because source-map converting stack): before commit: 2900-3100ms now: 190-230ms --- lib/sinon/call.js | 11 +++++++++-- lib/sinon/spy.js | 20 +++++++++----------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/lib/sinon/call.js b/lib/sinon/call.js index 8d89eab8d..741e997bb 100644 --- a/lib/sinon/call.js +++ b/lib/sinon/call.js @@ -174,10 +174,17 @@ var callProto = { return callStr; } }; +Object.defineProperty(callProto, "stack", { + enumerable: true, + configurable: true, + get: function () { + return this.errorWithCallStack && this.errorWithCallStack.stack || ""; + } +}); callProto.invokeCallback = callProto.yield; -function createSpyCall(spy, thisValue, args, returnValue, exception, id, stack) { +function createSpyCall(spy, thisValue, args, returnValue, exception, id, errorWithCallStack) { if (typeof id !== "number") { throw new TypeError("Call id is not a number"); } @@ -188,7 +195,7 @@ function createSpyCall(spy, thisValue, args, returnValue, exception, id, stack) proxyCall.returnValue = returnValue; proxyCall.exception = exception; proxyCall.callId = id; - proxyCall.stack = stack; + proxyCall.errorWithCallStack = errorWithCallStack; return proxyCall; } diff --git a/lib/sinon/spy.js b/lib/sinon/spy.js index 5b5fe075e..40f9b9e5a 100644 --- a/lib/sinon/spy.js +++ b/lib/sinon/spy.js @@ -130,7 +130,7 @@ var spyApi = { this.thisValues = []; this.exceptions = []; this.callIds = []; - this.stacks = []; + this.errorsWithCallStack = []; if (this.fakes) { this.fakes.forEach(function (fake) { fake.reset(); @@ -203,15 +203,13 @@ var spyApi = { push.call(this.exceptions, exception); push.call(this.returnValues, returnValue); var err = new ErrorConstructor(); - var stack = err.stack; - if (!stack) { - // PhantomJS does not serialize the stack trace until the error has been thrown: - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Stack - try { - throw err; - } catch (e) {/* empty */} - } - push.call(this.stacks, err.stack); + // 1. Please do not get stack at this point. It's may be so very slow, and not actually used + // 2. PhantomJS does not serialize the stack trace until the error has been thrown: + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Stack + try { + throw err; + } catch (e) {/* empty */} + push.call(this.errorsWithCallStack, err); // Make return value and exception available in the calls: createCallProperties.call(this); @@ -235,7 +233,7 @@ var spyApi = { return spyCall(this, this.thisValues[i], this.args[i], this.returnValues[i], this.exceptions[i], - this.callIds[i], this.stacks[i]); + this.callIds[i], this.errorsWithCallStack[i]); }, getCalls: function () {