Skip to content

Commit 5ae73c9

Browse files
author
Brian Vaughn
committed
Added missing guard to DevTools for Objects with null proto
This prevents a runtime error from occurring when these objects are inspected.
1 parent 2c1e5d2 commit 5ae73c9

File tree

6 files changed

+44
-6
lines changed

6 files changed

+44
-6
lines changed

packages/react-devtools-shared/src/__tests__/__snapshots__/inspectedElementContext-test.js.snap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,11 @@ exports[`InspectedElementContext should support complex data types: 1: Inspected
510510
"object_of_objects": {
511511
"inner": {}
512512
},
513+
"object_with_null_proto": {
514+
"string": "abc",
515+
"number": 123,
516+
"boolean": true
517+
},
513518
"react_element": {},
514519
"regexp": {},
515520
"set": {

packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,7 @@ describe('InspectedElementContext', () => {
524524
const setOfSets = new Set([new Set(['a', 'b', 'c']), new Set([1, 2, 3])]);
525525
const mapOfMaps = new Map([['first', mapShallow], ['second', mapShallow]]);
526526
const objectOfObjects = {
527-
inner: {string: 'abc', number: 213, boolean: true},
527+
inner: {string: 'abc', number: 123, boolean: true},
528528
};
529529
const typedArray = Int8Array.from([100, -100, 0]);
530530
const arrayBuffer = typedArray.buffer;
@@ -537,6 +537,10 @@ describe('InspectedElementContext', () => {
537537
xyz: 1,
538538
},
539539
});
540+
const objectWithNullProto = Object.create(null);
541+
objectWithNullProto.string = 'abc';
542+
objectWithNullProto.number = 123;
543+
objectWithNullProto.boolean = true;
540544

541545
const container = document.createElement('div');
542546
await utils.actAsync(() =>
@@ -554,6 +558,7 @@ describe('InspectedElementContext', () => {
554558
map={mapShallow}
555559
map_of_maps={mapOfMaps}
556560
object_of_objects={objectOfObjects}
561+
object_with_null_proto={objectWithNullProto}
557562
react_element={<span />}
558563
regexp={/abc/giu}
559564
set={setShallow}
@@ -604,6 +609,7 @@ describe('InspectedElementContext', () => {
604609
map,
605610
map_of_maps,
606611
object_of_objects,
612+
object_with_null_proto,
607613
react_element,
608614
regexp,
609615
set,
@@ -691,10 +697,16 @@ describe('InspectedElementContext', () => {
691697
expect(object_of_objects.inner[meta.name]).toBe('');
692698
expect(object_of_objects.inner[meta.type]).toBe('object');
693699
expect(object_of_objects.inner[meta.preview_long]).toBe(
694-
'{boolean: true, number: 213, string: "abc"}',
700+
'{boolean: true, number: 123, string: "abc"}',
695701
);
696702
expect(object_of_objects.inner[meta.preview_short]).toBe('{…}');
697703

704+
expect(object_with_null_proto).toEqual({
705+
boolean: true,
706+
number: 123,
707+
string: 'abc',
708+
});
709+
698710
expect(react_element[meta.inspectable]).toBe(false);
699711
expect(react_element[meta.name]).toBe('span');
700712
expect(react_element[meta.type]).toBe('react_element');

packages/react-devtools-shared/src/__tests__/legacy/__snapshots__/inspectElement-test.js.snap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,11 @@ Object {
151151
"object_of_objects": {
152152
"inner": {}
153153
},
154+
"object_with_null_proto": {
155+
"string": "abc",
156+
"number": 123,
157+
"boolean": true
158+
},
154159
"react_element": {},
155160
"regexp": {},
156161
"set": {

packages/react-devtools-shared/src/__tests__/legacy/inspectElement-test.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ describe('InspectedElementContext', () => {
155155
const setOfSets = new Set([new Set(['a', 'b', 'c']), new Set([1, 2, 3])]);
156156
const mapOfMaps = new Map([['first', mapShallow], ['second', mapShallow]]);
157157
const objectOfObjects = {
158-
inner: {string: 'abc', number: 213, boolean: true},
158+
inner: {string: 'abc', number: 123, boolean: true},
159159
};
160160
const typedArray = Int8Array.from([100, -100, 0]);
161161
const arrayBuffer = typedArray.buffer;
@@ -168,6 +168,10 @@ describe('InspectedElementContext', () => {
168168
xyz: 1,
169169
},
170170
});
171+
const objectWithNullProto = Object.create(null);
172+
objectWithNullProto.string = 'abc';
173+
objectWithNullProto.number = 123;
174+
objectWithNullProto.boolean = true;
171175

172176
act(() =>
173177
ReactDOM.render(
@@ -184,6 +188,7 @@ describe('InspectedElementContext', () => {
184188
map={mapShallow}
185189
map_of_maps={mapOfMaps}
186190
object_of_objects={objectOfObjects}
191+
object_with_null_proto={objectWithNullProto}
187192
react_element={<span />}
188193
regexp={/abc/giu}
189194
set={setShallow}
@@ -212,6 +217,7 @@ describe('InspectedElementContext', () => {
212217
map,
213218
map_of_maps,
214219
object_of_objects,
220+
object_with_null_proto,
215221
react_element,
216222
regexp,
217223
set,
@@ -273,10 +279,16 @@ describe('InspectedElementContext', () => {
273279
expect(object_of_objects.inner[meta.name]).toBe('');
274280
expect(object_of_objects.inner[meta.type]).toBe('object');
275281
expect(object_of_objects.inner[meta.preview_long]).toBe(
276-
'{boolean: true, number: 213, string: "abc"}',
282+
'{boolean: true, number: 123, string: "abc"}',
277283
);
278284
expect(object_of_objects.inner[meta.preview_short]).toBe('{…}');
279285

286+
expect(object_with_null_proto).toEqual({
287+
boolean: true,
288+
number: 123,
289+
string: 'abc',
290+
});
291+
280292
expect(react_element[meta.inspectable]).toBe(false);
281293
expect(react_element[meta.name]).toBe('span');
282294
expect(react_element[meta.type]).toBe('react_element');

packages/react-devtools-shared/src/utils.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -388,15 +388,15 @@ export function getDataType(data: Object): DataType {
388388
return data.constructor.hasOwnProperty('BYTES_PER_ELEMENT')
389389
? 'typed_array'
390390
: 'data_view';
391-
} else if (data.constructor.name === 'ArrayBuffer') {
391+
} else if (data.constructor && data.constructor.name === 'ArrayBuffer') {
392392
// HACK This ArrayBuffer check is gross; is there a better way?
393393
// We could try to create a new DataView with the value.
394394
// If it doesn't error, we know it's an ArrayBuffer,
395395
// but this seems kind of awkward and expensive.
396396
return 'array_buffer';
397397
} else if (typeof data[Symbol.iterator] === 'function') {
398398
return 'iterator';
399-
} else if (data.constructor.name === 'RegExp') {
399+
} else if (data.constructor && data.constructor.name === 'RegExp') {
400400
return 'regexp';
401401
} else if (Object.prototype.toString.call(data) === '[object Date]') {
402402
return 'date';

packages/react-devtools-shell/src/app/InspectableElements/UnserializableProps.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ const immutable = Immutable.fromJS({
2525
xyz: 1,
2626
},
2727
});
28+
const objectWithNullProto = Object.create(null);
29+
objectWithNullProto.foo = 'abc';
30+
objectWithNullProto.bar = 123;
2831

2932
export default function UnserializableProps() {
3033
return (
@@ -37,6 +40,7 @@ export default function UnserializableProps() {
3740
setOfSets={setOfSets}
3841
typedArray={typedArray}
3942
immutable={immutable}
43+
objectWithNullProto={objectWithNullProto}
4044
/>
4145
);
4246
}

0 commit comments

Comments
 (0)