| "use strict"; |
| |
| const arrayProto = require("@sinonjs/commons").prototypes.array; |
| const proxyCallUtil = require("./proxy-call-util"); |
| |
| const push = arrayProto.push; |
| const forEach = arrayProto.forEach; |
| const concat = arrayProto.concat; |
| const ErrorConstructor = Error.prototype.constructor; |
| const bind = Function.prototype.bind; |
| |
| let callId = 0; |
| |
| module.exports = function invoke(func, thisValue, args) { |
| const matchings = this.matchingFakes(args); |
| const currentCallId = callId++; |
| let exception, returnValue; |
| |
| proxyCallUtil.incrementCallCount(this); |
| push(this.thisValues, thisValue); |
| push(this.args, args); |
| push(this.callIds, currentCallId); |
| forEach(matchings, function (matching) { |
| proxyCallUtil.incrementCallCount(matching); |
| push(matching.thisValues, thisValue); |
| push(matching.args, args); |
| push(matching.callIds, currentCallId); |
| }); |
| |
| // Make call properties available from within the spied function: |
| proxyCallUtil.createCallProperties(this); |
| forEach(matchings, proxyCallUtil.createCallProperties); |
| |
| try { |
| this.invoking = true; |
| |
| const thisCall = this.getCall(this.callCount - 1); |
| |
| if (thisCall.calledWithNew()) { |
| // Call through with `new` |
| returnValue = new (bind.apply( |
| this.func || func, |
| concat([thisValue], args), |
| ))(); |
| |
| if ( |
| typeof returnValue !== "object" && |
| typeof returnValue !== "function" |
| ) { |
| returnValue = thisValue; |
| } |
| } else { |
| returnValue = (this.func || func).apply(thisValue, args); |
| } |
| } catch (e) { |
| exception = e; |
| } finally { |
| delete this.invoking; |
| } |
| |
| push(this.exceptions, exception); |
| push(this.returnValues, returnValue); |
| forEach(matchings, function (matching) { |
| push(matching.exceptions, exception); |
| push(matching.returnValues, returnValue); |
| }); |
| |
| const err = new ErrorConstructor(); |
| // 1. Please do not get stack at this point. It 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(this.errorsWithCallStack, err); |
| forEach(matchings, function (matching) { |
| push(matching.errorsWithCallStack, err); |
| }); |
| |
| // Make return value and exception available in the calls: |
| proxyCallUtil.createCallProperties(this); |
| forEach(matchings, proxyCallUtil.createCallProperties); |
| |
| if (exception !== undefined) { |
| throw exception; |
| } |
| |
| return returnValue; |
| }; |