1
1
'use strict'
2
2
3
- const log = require ( '../../dd-trace/src/log' )
4
-
5
3
function copyProperties ( original , wrapped ) {
6
4
// TODO getPrototypeOf is not fast. Should we instead do this in specific
7
5
// instrumentations where needed?
@@ -24,9 +22,7 @@ function copyProperties (original, wrapped) {
24
22
function wrapFunction ( original , wrapper ) {
25
23
if ( typeof original === 'function' ) assertNotClass ( original )
26
24
27
- const wrapped = safeMode
28
- ? safeWrapper ( original , wrapper )
29
- : wrapper ( original )
25
+ const wrapped = wrapper ( original )
30
26
31
27
if ( typeof original === 'function' ) copyProperties ( original , wrapped )
32
28
@@ -37,46 +33,14 @@ const wrapFn = function (original, delegate) {
37
33
throw new Error ( 'calling `wrap()` with 2 args is deprecated. Use wrapFunction instead.' )
38
34
}
39
35
40
- // This is only used in safe mode. It's a simple state machine to track if the
41
- // original method was called and if it returned. We need this to determine if
42
- // an error was thrown by the original method, or by us. We'll use one of these
43
- // per call to a wrapped method.
44
- class CallState {
45
- constructor ( ) {
46
- this . called = false
47
- this . completed = false
48
- this . retVal = undefined
49
- }
50
-
51
- startCall ( ) {
52
- this . called = true
53
- }
54
-
55
- endCall ( retVal ) {
56
- this . completed = true
57
- this . retVal = retVal
58
- }
59
- }
60
-
61
- function isPromise ( obj ) {
62
- return obj && typeof obj === 'object' && typeof obj . then === 'function'
63
- }
64
-
65
- let safeMode = ! ! process . env . DD_INEJCTION_ENABLED
66
- function setSafe ( value ) {
67
- safeMode = value
68
- }
69
-
70
36
function wrapMethod ( target , name , wrapper , noAssert ) {
71
37
if ( ! noAssert ) {
72
38
assertMethod ( target , name )
73
39
assertFunction ( wrapper )
74
40
}
75
41
76
42
const original = target [ name ]
77
- const wrapped = safeMode && original
78
- ? safeWrapper ( original , wrapper )
79
- : wrapper ( original )
43
+ const wrapped = wrapper ( original )
80
44
81
45
const descriptor = Object . getOwnPropertyDescriptor ( target , name )
82
46
@@ -110,94 +74,6 @@ function wrapMethod (target, name, wrapper, noAssert) {
110
74
return target
111
75
}
112
76
113
- function safeWrapper ( original , wrapper ) {
114
- // In this mode, we make a best-effort attempt to handle errors that are thrown
115
- // by us, rather than wrapped code. With such errors, we log them, and then attempt
116
- // to return the result as if no wrapping was done at all.
117
- //
118
- // Caveats:
119
- // * If the original function is called in a later iteration of the event loop,
120
- // and we throw _then_, then it won't be caught by this. In practice, we always call
121
- // the original function synchronously, so this is not a problem.
122
- // * While async errors are dealt with here, errors in callbacks are not. This
123
- // is because we don't necessarily know _for sure_ that any function arguments
124
- // are wrapped by us. We could wrap them all anyway and just make that assumption,
125
- // or just assume that the last argument is always a callback set by us if it's a
126
- // function, but those don't seem like things we can rely on. We could add a
127
- // `shimmer.markCallbackAsWrapped()` function that's a no-op outside safe-mode,
128
- // but that means modifying every instrumentation. Even then, the complexity of
129
- // this code increases because then we'd need to effectively do the reverse of
130
- // what we're doing for synchronous functions. This is a TODO.
131
-
132
- // We're going to hold on to current callState in this variable in this scope,
133
- // which is fine because any time we reference it, we're referencing it synchronously.
134
- // We'll use it in the our wrapper (which, again, is called syncrhonously), and in the
135
- // errorHandler, which will already have been bound to this callState.
136
- let currentCallState
137
-
138
- // Rather than calling the original function directly from the shim wrapper, we wrap
139
- // it again so that we can track if it was called and if it returned. This is because
140
- // we need to know if an error was thrown by the original function, or by us.
141
- // We could do this inside the `wrapper` function defined below, which would simplify
142
- // managing the callState, but then we'd be calling `wrapper` on each invocation, so
143
- // instead we do it here, once.
144
- const innerWrapped = wrapper ( function ( ...args ) {
145
- // We need to stash the callState here because of recursion.
146
- const callState = currentCallState
147
- callState . startCall ( )
148
- const retVal = original . apply ( this , args )
149
- if ( isPromise ( retVal ) ) {
150
- retVal . then ( callState . endCall . bind ( callState ) )
151
- } else {
152
- callState . endCall ( retVal )
153
- }
154
- return retVal
155
- } )
156
-
157
- // This is the crux of what we're doing in safe mode. It handles errors
158
- // that _we_ cause, by logging them, and transparently providing results
159
- // as if no wrapping was done at all. That means detecting (via callState)
160
- // whether the function has already run or not, and if it has, returning
161
- // the result, and otherwise calling the original function unwrapped.
162
- const handleError = function ( args , callState , e ) {
163
- if ( callState . completed ) {
164
- // error was thrown after original function returned/resolved, so
165
- // it was us. log it.
166
- log . error ( 'Shimmer error was thrown after original function returned/resolved' , e )
167
- // original ran and returned something. return it.
168
- return callState . retVal
169
- }
170
-
171
- if ( ! callState . called ) {
172
- // error was thrown before original function was called, so
173
- // it was us. log it.
174
- log . error ( 'Shimmer error was thrown before original function was called' , e )
175
- // original never ran. call it unwrapped.
176
- return original . apply ( this , args )
177
- }
178
-
179
- // error was thrown during original function execution, so
180
- // it was them. throw.
181
- throw e
182
- }
183
-
184
- // The wrapped function is the one that will be called by the user.
185
- // It calls our version of the original function, which manages the
186
- // callState. That way when we use the errorHandler, it can tell where
187
- // the error originated.
188
- return function ( ...args ) {
189
- currentCallState = new CallState ( )
190
- const errorHandler = handleError . bind ( this , args , currentCallState )
191
-
192
- try {
193
- const retVal = innerWrapped . apply ( this , args )
194
- return isPromise ( retVal ) ? retVal . catch ( errorHandler ) : retVal
195
- } catch ( e ) {
196
- return errorHandler ( e )
197
- }
198
- }
199
- }
200
-
201
77
function wrap ( target , name , wrapper ) {
202
78
return typeof name === 'function'
203
79
? wrapFn ( target , name )
@@ -256,6 +132,5 @@ function assertNotClass (target) {
256
132
module . exports = {
257
133
wrap,
258
134
wrapFunction,
259
- massWrap,
260
- setSafe
135
+ massWrap
261
136
}
0 commit comments