From 5ae73c9ea3a0a4e70300a3790952f9c02d835b8b Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Thu, 2 Jan 2020 07:49:54 -0800 Subject: [PATCH] Added missing guard to DevTools for Objects with null proto This prevents a runtime error from occurring when these objects are inspected. --- .../inspectedElementContext-test.js.snap | 5 +++++ .../__tests__/inspectedElementContext-test.js | 16 ++++++++++++++-- .../__snapshots__/inspectElement-test.js.snap | 5 +++++ .../src/__tests__/legacy/inspectElement-test.js | 16 ++++++++++++++-- packages/react-devtools-shared/src/utils.js | 4 ++-- .../InspectableElements/UnserializableProps.js | 4 ++++ 6 files changed, 44 insertions(+), 6 deletions(-) diff --git a/packages/react-devtools-shared/src/__tests__/__snapshots__/inspectedElementContext-test.js.snap b/packages/react-devtools-shared/src/__tests__/__snapshots__/inspectedElementContext-test.js.snap index 3cf933f607d41..71e3cf4e627fc 100644 --- a/packages/react-devtools-shared/src/__tests__/__snapshots__/inspectedElementContext-test.js.snap +++ b/packages/react-devtools-shared/src/__tests__/__snapshots__/inspectedElementContext-test.js.snap @@ -510,6 +510,11 @@ exports[`InspectedElementContext should support complex data types: 1: Inspected "object_of_objects": { "inner": {} }, + "object_with_null_proto": { + "string": "abc", + "number": 123, + "boolean": true + }, "react_element": {}, "regexp": {}, "set": { diff --git a/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js b/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js index e6d3d8303a47c..05a5f0dc123e4 100644 --- a/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js +++ b/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js @@ -524,7 +524,7 @@ describe('InspectedElementContext', () => { const setOfSets = new Set([new Set(['a', 'b', 'c']), new Set([1, 2, 3])]); const mapOfMaps = new Map([['first', mapShallow], ['second', mapShallow]]); const objectOfObjects = { - inner: {string: 'abc', number: 213, boolean: true}, + inner: {string: 'abc', number: 123, boolean: true}, }; const typedArray = Int8Array.from([100, -100, 0]); const arrayBuffer = typedArray.buffer; @@ -537,6 +537,10 @@ describe('InspectedElementContext', () => { xyz: 1, }, }); + const objectWithNullProto = Object.create(null); + objectWithNullProto.string = 'abc'; + objectWithNullProto.number = 123; + objectWithNullProto.boolean = true; const container = document.createElement('div'); await utils.actAsync(() => @@ -554,6 +558,7 @@ describe('InspectedElementContext', () => { map={mapShallow} map_of_maps={mapOfMaps} object_of_objects={objectOfObjects} + object_with_null_proto={objectWithNullProto} react_element={} regexp={/abc/giu} set={setShallow} @@ -604,6 +609,7 @@ describe('InspectedElementContext', () => { map, map_of_maps, object_of_objects, + object_with_null_proto, react_element, regexp, set, @@ -691,10 +697,16 @@ describe('InspectedElementContext', () => { expect(object_of_objects.inner[meta.name]).toBe(''); expect(object_of_objects.inner[meta.type]).toBe('object'); expect(object_of_objects.inner[meta.preview_long]).toBe( - '{boolean: true, number: 213, string: "abc"}', + '{boolean: true, number: 123, string: "abc"}', ); expect(object_of_objects.inner[meta.preview_short]).toBe('{…}'); + expect(object_with_null_proto).toEqual({ + boolean: true, + number: 123, + string: 'abc', + }); + expect(react_element[meta.inspectable]).toBe(false); expect(react_element[meta.name]).toBe('span'); expect(react_element[meta.type]).toBe('react_element'); diff --git a/packages/react-devtools-shared/src/__tests__/legacy/__snapshots__/inspectElement-test.js.snap b/packages/react-devtools-shared/src/__tests__/legacy/__snapshots__/inspectElement-test.js.snap index 6c1d827808dd8..17e3a43d357c4 100644 --- a/packages/react-devtools-shared/src/__tests__/legacy/__snapshots__/inspectElement-test.js.snap +++ b/packages/react-devtools-shared/src/__tests__/legacy/__snapshots__/inspectElement-test.js.snap @@ -151,6 +151,11 @@ Object { "object_of_objects": { "inner": {} }, + "object_with_null_proto": { + "string": "abc", + "number": 123, + "boolean": true + }, "react_element": {}, "regexp": {}, "set": { diff --git a/packages/react-devtools-shared/src/__tests__/legacy/inspectElement-test.js b/packages/react-devtools-shared/src/__tests__/legacy/inspectElement-test.js index 7b06f257d5e07..d294044362033 100644 --- a/packages/react-devtools-shared/src/__tests__/legacy/inspectElement-test.js +++ b/packages/react-devtools-shared/src/__tests__/legacy/inspectElement-test.js @@ -155,7 +155,7 @@ describe('InspectedElementContext', () => { const setOfSets = new Set([new Set(['a', 'b', 'c']), new Set([1, 2, 3])]); const mapOfMaps = new Map([['first', mapShallow], ['second', mapShallow]]); const objectOfObjects = { - inner: {string: 'abc', number: 213, boolean: true}, + inner: {string: 'abc', number: 123, boolean: true}, }; const typedArray = Int8Array.from([100, -100, 0]); const arrayBuffer = typedArray.buffer; @@ -168,6 +168,10 @@ describe('InspectedElementContext', () => { xyz: 1, }, }); + const objectWithNullProto = Object.create(null); + objectWithNullProto.string = 'abc'; + objectWithNullProto.number = 123; + objectWithNullProto.boolean = true; act(() => ReactDOM.render( @@ -184,6 +188,7 @@ describe('InspectedElementContext', () => { map={mapShallow} map_of_maps={mapOfMaps} object_of_objects={objectOfObjects} + object_with_null_proto={objectWithNullProto} react_element={} regexp={/abc/giu} set={setShallow} @@ -212,6 +217,7 @@ describe('InspectedElementContext', () => { map, map_of_maps, object_of_objects, + object_with_null_proto, react_element, regexp, set, @@ -273,10 +279,16 @@ describe('InspectedElementContext', () => { expect(object_of_objects.inner[meta.name]).toBe(''); expect(object_of_objects.inner[meta.type]).toBe('object'); expect(object_of_objects.inner[meta.preview_long]).toBe( - '{boolean: true, number: 213, string: "abc"}', + '{boolean: true, number: 123, string: "abc"}', ); expect(object_of_objects.inner[meta.preview_short]).toBe('{…}'); + expect(object_with_null_proto).toEqual({ + boolean: true, + number: 123, + string: 'abc', + }); + expect(react_element[meta.inspectable]).toBe(false); expect(react_element[meta.name]).toBe('span'); expect(react_element[meta.type]).toBe('react_element'); diff --git a/packages/react-devtools-shared/src/utils.js b/packages/react-devtools-shared/src/utils.js index 1b55d86c4a418..f9662ce5c71f4 100644 --- a/packages/react-devtools-shared/src/utils.js +++ b/packages/react-devtools-shared/src/utils.js @@ -388,7 +388,7 @@ export function getDataType(data: Object): DataType { return data.constructor.hasOwnProperty('BYTES_PER_ELEMENT') ? 'typed_array' : 'data_view'; - } else if (data.constructor.name === 'ArrayBuffer') { + } else if (data.constructor && data.constructor.name === 'ArrayBuffer') { // HACK This ArrayBuffer check is gross; is there a better way? // We could try to create a new DataView with the value. // If it doesn't error, we know it's an ArrayBuffer, @@ -396,7 +396,7 @@ export function getDataType(data: Object): DataType { return 'array_buffer'; } else if (typeof data[Symbol.iterator] === 'function') { return 'iterator'; - } else if (data.constructor.name === 'RegExp') { + } else if (data.constructor && data.constructor.name === 'RegExp') { return 'regexp'; } else if (Object.prototype.toString.call(data) === '[object Date]') { return 'date'; diff --git a/packages/react-devtools-shell/src/app/InspectableElements/UnserializableProps.js b/packages/react-devtools-shell/src/app/InspectableElements/UnserializableProps.js index effe7f14a348f..388149d0e763e 100644 --- a/packages/react-devtools-shell/src/app/InspectableElements/UnserializableProps.js +++ b/packages/react-devtools-shell/src/app/InspectableElements/UnserializableProps.js @@ -25,6 +25,9 @@ const immutable = Immutable.fromJS({ xyz: 1, }, }); +const objectWithNullProto = Object.create(null); +objectWithNullProto.foo = 'abc'; +objectWithNullProto.bar = 123; export default function UnserializableProps() { return ( @@ -37,6 +40,7 @@ export default function UnserializableProps() { setOfSets={setOfSets} typedArray={typedArray} immutable={immutable} + objectWithNullProto={objectWithNullProto} /> ); }