Skip to content

Commit ad069c6

Browse files
committed
Exclude Object and Promise prototype properties from shadowing for params and searchParams
params and searchParams are now promises however to facilitate migration params and searchParams can still be referenced directly on these props. There are a number of special properties however that conflict with this and this change special cases more property names to not be synchronously accessed. We exclude common Object prototype properties, Promise prototype properties, and properties that are commonly existence tested like toJSON and displayName.
1 parent fb15ee9 commit ad069c6

File tree

2 files changed

+198
-18
lines changed

2 files changed

+198
-18
lines changed

packages/next/src/server/request/params.ts

Lines changed: 97 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -192,10 +192,32 @@ function makeAbortingExoticParams(
192192

193193
Object.keys(underlyingParams).forEach((prop) => {
194194
switch (prop) {
195+
// Object prototype
196+
case 'hasOwnProperty':
197+
case 'isPrototypeOf':
198+
case 'propertyIsEnumerable':
199+
case 'toString':
200+
case 'valueOf':
201+
case 'toLocaleString':
202+
203+
// Promise prototype
204+
// fallthrough
195205
case 'then':
196-
case 'status': {
197-
// We can't assign params over these properties because the VM and React use
198-
// them to reason about the Promise.
206+
case 'catch':
207+
case 'finally':
208+
209+
// React Promise extension
210+
// fallthrough
211+
case 'value':
212+
case 'status':
213+
214+
// Common tested properties
215+
// fallthrough
216+
case 'toJSON':
217+
case '$$typeof':
218+
case '__esModule': {
219+
// These properties cannot be shadowed because they need to be the
220+
// true underlying value for Promises to work correctly at runtime
199221
break
200222
}
201223
default: {
@@ -246,11 +268,32 @@ function makeErroringExoticParams(
246268

247269
Object.keys(underlyingParams).forEach((prop) => {
248270
switch (prop) {
271+
// Object prototype
272+
case 'hasOwnProperty':
273+
case 'isPrototypeOf':
274+
case 'propertyIsEnumerable':
275+
case 'toString':
276+
case 'valueOf':
277+
case 'toLocaleString':
278+
279+
// Promise prototype
280+
// fallthrough
249281
case 'then':
282+
case 'catch':
283+
case 'finally':
284+
285+
// React Promise extension
286+
// fallthrough
287+
case 'value':
250288
case 'status':
251-
case 'value': {
252-
// We can't assign params over these properties because the VM and React use
253-
// them to reason about the Promise.
289+
290+
// Common tested properties
291+
// fallthrough
292+
case 'toJSON':
293+
case '$$typeof':
294+
case '__esModule': {
295+
// These properties cannot be shadowed because they need to be the
296+
// true underlying value for Promises to work correctly at runtime
254297
break
255298
}
256299
default: {
@@ -335,11 +378,32 @@ function makeUntrackedExoticParams(underlyingParams: Params): Promise<Params> {
335378

336379
Object.keys(underlyingParams).forEach((prop) => {
337380
switch (prop) {
381+
// Object prototype
382+
case 'hasOwnProperty':
383+
case 'isPrototypeOf':
384+
case 'propertyIsEnumerable':
385+
case 'toString':
386+
case 'valueOf':
387+
case 'toLocaleString':
388+
389+
// Promise prototype
390+
// fallthrough
338391
case 'then':
392+
case 'catch':
393+
case 'finally':
394+
395+
// React Promise extension
396+
// fallthrough
339397
case 'value':
340-
case 'status': {
341-
// These properties cannot be shadowed with a search param because they
342-
// are necessary for ReactPromise's to work correctly with `use`
398+
case 'status':
399+
400+
// Common tested properties
401+
// fallthrough
402+
case 'toJSON':
403+
case '$$typeof':
404+
case '__esModule': {
405+
// These properties cannot be shadowed because they need to be the
406+
// true underlying value for Promises to work correctly at runtime
343407
break
344408
}
345409
default: {
@@ -370,11 +434,32 @@ function makeDynamicallyTrackedExoticParamsWithDevWarnings(
370434

371435
Object.keys(underlyingParams).forEach((prop) => {
372436
switch (prop) {
437+
// Object prototype
438+
case 'hasOwnProperty':
439+
case 'isPrototypeOf':
440+
case 'propertyIsEnumerable':
441+
case 'toString':
442+
case 'valueOf':
443+
case 'toLocaleString':
444+
445+
// Promise prototype
446+
// fallthrough
373447
case 'then':
448+
case 'catch':
449+
case 'finally':
450+
451+
// React Promise extension
452+
// fallthrough
374453
case 'value':
375-
case 'status': {
376-
// These properties cannot be shadowed with a search param because they
377-
// are necessary for ReactPromise's to work correctly with `use`
454+
case 'status':
455+
456+
// Common tested properties
457+
// fallthrough
458+
case 'toJSON':
459+
case '$$typeof':
460+
case '__esModule': {
461+
// These properties cannot be shadowed because they need to be the
462+
// true underlying value for Promises to work correctly at runtime
378463
unproxiedProperties.push(prop)
379464
break
380465
}

packages/next/src/server/request/search-params.ts

Lines changed: 101 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,33 @@ function makeAbortingExoticSearchParams(
193193
annotateDynamicAccess(expression, prerenderStore)
194194
return ReflectAdapter.get(target, prop, receiver)
195195
}
196+
// Object prototype
197+
case 'hasOwnProperty':
198+
case 'isPrototypeOf':
199+
case 'propertyIsEnumerable':
200+
case 'toString':
201+
case 'valueOf':
202+
case 'toLocaleString':
203+
204+
// Promise prototype
205+
// fallthrough
206+
case 'catch':
207+
case 'finally':
208+
209+
// React Promise extension
210+
// fallthrough
211+
case 'value':
212+
213+
// Common tested properties
214+
// fallthrough
215+
case 'toJSON':
216+
case '$$typeof':
217+
case '__esModule': {
218+
// These properties cannot be shadowed because they need to be the
219+
// true underlying value for Promises to work correctly at runtime
220+
return ReflectAdapter.get(target, prop, receiver)
221+
}
222+
196223
default: {
197224
if (typeof prop === 'string') {
198225
const expression = describeStringPropertyAccess(
@@ -267,6 +294,32 @@ function makeErroringExoticSearchParams(
267294
}
268295

269296
switch (prop) {
297+
// Object prototype
298+
case 'hasOwnProperty':
299+
case 'isPrototypeOf':
300+
case 'propertyIsEnumerable':
301+
case 'toString':
302+
case 'valueOf':
303+
case 'toLocaleString':
304+
305+
// Promise prototype
306+
// fallthrough
307+
case 'catch':
308+
case 'finally':
309+
310+
// React Promise extension
311+
// fallthrough
312+
case 'value':
313+
314+
// Common tested properties
315+
// fallthrough
316+
case 'toJSON':
317+
case '$$typeof':
318+
case '__esModule': {
319+
// These properties cannot be shadowed because they need to be the
320+
// true underlying value for Promises to work correctly at runtime
321+
return ReflectAdapter.get(target, prop, receiver)
322+
}
270323
case 'then': {
271324
const expression =
272325
'`await searchParams`, `searchParams.then`, or similar'
@@ -402,11 +455,32 @@ function makeUntrackedExoticSearchParams(
402455

403456
Object.keys(underlyingSearchParams).forEach((prop) => {
404457
switch (prop) {
458+
// Object prototype
459+
case 'hasOwnProperty':
460+
case 'isPrototypeOf':
461+
case 'propertyIsEnumerable':
462+
case 'toString':
463+
case 'valueOf':
464+
case 'toLocaleString':
465+
466+
// Promise prototype
467+
// fallthrough
405468
case 'then':
469+
case 'catch':
470+
case 'finally':
471+
472+
// React Promise extension
473+
// fallthrough
406474
case 'value':
407-
case 'status': {
408-
// These properties cannot be shadowed with a search param because they
409-
// are necessary for ReactPromise's to work correctly with `use`
475+
case 'status':
476+
477+
// Common tested properties
478+
// fallthrough
479+
case 'toJSON':
480+
case '$$typeof':
481+
case '__esModule': {
482+
// These properties cannot be shadowed because they need to be the
483+
// true underlying value for Promises to work correctly at runtime
410484
break
411485
}
412486
default: {
@@ -503,11 +577,32 @@ function makeDynamicallyTrackedExoticSearchParamsWithDevWarnings(
503577

504578
Object.keys(underlyingSearchParams).forEach((prop) => {
505579
switch (prop) {
580+
// Object prototype
581+
case 'hasOwnProperty':
582+
case 'isPrototypeOf':
583+
case 'propertyIsEnumerable':
584+
case 'toString':
585+
case 'valueOf':
586+
case 'toLocaleString':
587+
588+
// Promise prototype
589+
// fallthrough
506590
case 'then':
591+
case 'catch':
592+
case 'finally':
593+
594+
// React Promise extension
595+
// fallthrough
507596
case 'value':
508-
case 'status': {
509-
// These properties cannot be shadowed with a search param because they
510-
// are necessary for ReactPromise's to work correctly with `use`
597+
case 'status':
598+
599+
// Common tested properties
600+
// fallthrough
601+
case 'toJSON':
602+
case '$$typeof':
603+
case '__esModule': {
604+
// These properties cannot be shadowed because they need to be the
605+
// true underlying value for Promises to work correctly at runtime
511606
unproxiedProperties.push(prop)
512607
break
513608
}

0 commit comments

Comments
 (0)