Skip to content

Commit 7cb9d6c

Browse files
benglwatson
authored andcommitted
dd-trace-api: don't proxy objects returned from callbacks (#5240)
Some APIs (pretty much just trace) return whatever value is returned from a callback passed in. Without providing for this, this would trip up the check that returned objects are proxied. We don't want to proxy these objects since they come directly from the caller.
1 parent 5c49e66 commit 7cb9d6c

File tree

2 files changed

+29
-9
lines changed

2 files changed

+29
-9
lines changed

packages/datadog-plugin-dd-trace-api/src/index.js

+11-2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ module.exports = class DdTraceApiPlugin extends Plugin {
4848
self = objectMap.get(self)
4949
}
5050

51+
// `trace` returns the value that's returned from the original callback
52+
// passed to it, so we need to detect that happening and bypass the check
53+
// for a proxy, since a proxy isn't needed, since the object originates
54+
// from the caller. In callbacks, we'll assign return values to this
55+
// value, and bypass the proxy check if `ret.value` is exactly this
56+
// value.
57+
let passthroughRetVal
58+
5159
for (let i = 0; i < args.length; i++) {
5260
if (objectMap.has(args[i])) {
5361
args[i] = objectMap.get(args[i])
@@ -63,7 +71,8 @@ module.exports = class DdTraceApiPlugin extends Plugin {
6371
}
6472
}
6573
// TODO do we need to apply(this, ...) here?
66-
return orig(...fnArgs)
74+
passthroughRetVal = orig(...fnArgs)
75+
return passthroughRetVal
6776
}
6877
}
6978
}
@@ -74,7 +83,7 @@ module.exports = class DdTraceApiPlugin extends Plugin {
7483
const proxyVal = proxy()
7584
objectMap.set(proxyVal, ret.value)
7685
ret.value = proxyVal
77-
} else if (ret.value && typeof ret.value === 'object') {
86+
} else if (ret.value && typeof ret.value === 'object' && passthroughRetVal !== ret.value) {
7887
throw new TypeError(`Objects need proxies when returned via API (${name})`)
7988
}
8089
} catch (e) {

packages/datadog-plugin-dd-trace-api/test/index.spec.js

+18-7
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,20 @@ describe('Plugin', () => {
220220
describeMethod('extract', null)
221221
describeMethod('getRumData', '')
222222
describeMethod('trace')
223+
224+
describe('trace with return value', () => {
225+
it('should return the exact same value', () => {
226+
const obj = { mustBeThis: 'value' }
227+
tracer.trace.resetHistory() // clear previous call to `trace`
228+
testChannel({
229+
name: 'trace',
230+
fn: tracer.trace,
231+
ret: obj,
232+
proxy: false,
233+
args: ['foo', {}, () => obj]
234+
})
235+
})
236+
})
223237
describeMethod('wrap')
224238
describeMethod('use', SELF)
225239
describeMethod('profilerStarted', Promise.resolve(false))
@@ -268,16 +282,13 @@ describe('Plugin', () => {
268282
})
269283
}
270284

271-
function testChannel ({ name, fn, self = dummyTracer, ret = undefined, args = [] }) {
285+
function testChannel ({ name, fn, self = dummyTracer, ret, args = [], proxy }) {
272286
testedChannels.add('datadog-api:v1:' + name)
273287
const ch = dc.channel('datadog-api:v1:' + name)
274-
const payload = {
275-
self,
276-
args,
277-
ret: {},
278-
proxy: ret && typeof ret === 'object' ? () => ret : undefined,
279-
revProxy: []
288+
if (proxy === undefined) {
289+
proxy = ret && typeof ret === 'object' ? () => ret : undefined
280290
}
291+
const payload = { self, args, ret: {}, proxy, revProxy: [] }
281292
ch.publish(payload)
282293
if (payload.ret.error) {
283294
throw payload.ret.error

0 commit comments

Comments
 (0)