Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.

Commit 3c2da55

Browse files
committed
Bug 1715512 part 3 - Add SMDOC comment for property maps. r=jonco
Differential Revision: https://phabricator.services.mozilla.com/D117303
1 parent 2d38e8c commit 3c2da55

1 file changed

Lines changed: 183 additions & 0 deletions

File tree

js/src/vm/PropMap.h

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,189 @@
1010
#include "gc/Cell.h"
1111
#include "js/UbiNode.h"
1212

13+
// [SMDOC] Property Maps
14+
//
15+
// Property maps are used to store information about native object properties.
16+
// Each property map represents an ordered list of (PropertyKey, PropertyInfo)
17+
// tuples.
18+
//
19+
// Each property map can store up to 8 properties (see PropMap::Capacity). To
20+
// store more than eight properties, multiple maps must be linked together with
21+
// the |previous| pointer.
22+
//
23+
// Shapes and Property Maps
24+
// ------------------------
25+
// Native object shapes represent property information as a (PropMap*, length)
26+
// tuple. When there are no properties yet, the shape's map will be nullptr and
27+
// the length is zero.
28+
//
29+
// For example, consider the following objects:
30+
//
31+
// o1 = {x: 1, y: 2}
32+
// o2 = {x: 3, y: 4, z: 5}
33+
//
34+
// This is stored as follows:
35+
//
36+
// +-------------+ +--------------+ +-------------------+
37+
// | JSObject o1 | | Shape S1 | | PropMap M1 |
38+
// |-------------+ +--------------+ +-------------------+
39+
// | shape: S1 -+---> | map: M1 -+--+> | key 0: x (slot 0) |
40+
// | slot 0: 1 | | mapLength: 2 | | | key 1: y (slot 1) |
41+
// | slot 1: 2 | +--------------+ | | key 2: z (slot 2) |
42+
// +-------------+ | | ... |
43+
// | +-------------------+
44+
// |
45+
// +-------------+ +--------------+ |
46+
// | JSObject o2 | | Shape S2 | |
47+
// |-------------+ +--------------+ |
48+
// | shape: S2 -+---> | map: M1 -+--+
49+
// | slot 0: 3 | | mapLength: 3 |
50+
// | slot 1: 4 | +--------------+
51+
// | slot 2: 5 |
52+
// +-------------+
53+
//
54+
// There's a single map M1 shared by shapes S1 and S2. Shape S1 includes only
55+
// the first two properties and shape S2 includes all three properties.
56+
//
57+
// Class Hierarchy
58+
// ---------------
59+
// Property maps have the following C++ class hierarchy:
60+
//
61+
// PropMap (abstract)
62+
// |
63+
// +-- SharedPropMap (abstract)
64+
// | |
65+
// | +-- CompactPropMap
66+
// | |
67+
// | +-- NormalPropMap
68+
// |
69+
// +-- DictionaryPropMap
70+
//
71+
// * PropMap: base class. It has a flags word and an array of PropertyKeys.
72+
//
73+
// * SharedPropMap: base class for all shared property maps. See below for more
74+
// information on shared maps.
75+
//
76+
// * CompactPropMap: a shared map that stores its information more compactly
77+
// than the other maps. It saves four words by not storing a
78+
// PropMapTable, previous pointer, and by using a more compact
79+
// PropertyInfo type for slot numbers that fit in one byte.
80+
//
81+
// * NormalPropMap: a shared map, used when CompactPropMap can't be used.
82+
//
83+
// * DictionaryPropMap: an unshared map (used by a single object/shape). See
84+
// below for more information on dictionary maps.
85+
//
86+
// Secondary hierarchy
87+
// -------------------
88+
// NormalPropMap and DictionaryPropMap store property information in the same
89+
// way. This means property lookups don't have to distinguish between these two
90+
// types. This is represented with a second class hierarchy:
91+
//
92+
// PropMap (abstract)
93+
// |
94+
// +-- CompactPropMap
95+
// |
96+
// +-- LinkedPropMap (NormalPropMap or DictionaryPropMap)
97+
//
98+
// Property lookup and property iteration are very performance sensitive and use
99+
// this Compact vs Linked "view" so that they don't have to handle the three map
100+
// types separately.
101+
//
102+
// LinkedPropMap also stores the PropMapTable and a pointer to the |previous|
103+
// map. Compact maps don't have these fields.
104+
//
105+
// To summarize these map types:
106+
//
107+
// +-------------------+-------------+--------+
108+
// | Concrete type | Shared/tree | Linked |
109+
// +-------------------+-------------+--------+
110+
// | CompactPropMap | yes | no |
111+
// | NormalPropMap | yes | yes |
112+
// | DictionaryPropMap | no | yes |
113+
// +-------------------+-------------+--------+
114+
//
115+
// PropMapTable
116+
// ------------
117+
// Finding the PropertyInfo for a particular PropertyKey requires a linear
118+
// search if the map is small. For larger maps we can create a PropMapTable, a
119+
// hash table that maps from PropertyKey to PropMap + index, to speed up
120+
// property lookups.
121+
//
122+
// To save memory, property map tables can be discarded on GC and recreated when
123+
// needed. AutoKeepPropMapTables can be used to avoid discarding tables in a
124+
// particular zone. Methods to access a PropMapTable take either an
125+
// AutoCheckCannotGC or AutoKeepPropMapTables argument, to help ensure tables
126+
// are not purged while we're using them.
127+
//
128+
// Shared Property Maps
129+
// --------------------
130+
// Shared property maps can be shared per-Zone by objects with the same property
131+
// keys, flags, and slot numbers. To make this work, shared maps form a tree:
132+
//
133+
// - Each Zone has a table that maps from first PropertyKey + PropertyInfo to
134+
// a SharedPropMap that begins with that property. This is used to lookup the
135+
// the map to use when adding the first property.
136+
// See ShapeZone::initialPropMaps.
137+
//
138+
// - When adding a property other than the first one, the property is stored in
139+
// the next entry of the same map when possible. If the map is full or the
140+
// next entry already stores a different property, a child map is created and
141+
// linked to the parent map.
142+
//
143+
// For example, imagine we want to create these objects:
144+
//
145+
// o1 = {x: 1, y: 2, z: 3}
146+
// o2 = {x: 1, y: 2, foo: 4}
147+
//
148+
// This will result in the following maps being created:
149+
//
150+
// +---------------------+ +---------------------+
151+
// | SharedPropMap M1 | | SharedPropMap M2 |
152+
// +---------------------+ +---------------------+
153+
// | Child M2 (index 1) -+--> | Parent M1 (index 1) |
154+
// +---------------------+ +---------------------+
155+
// | 0: x | | 0: x |
156+
// | 1: y | | 1: y |
157+
// | 2: z | | 2: foo |
158+
// | ... | | ... |
159+
// +---------------------+ +---------------------+
160+
//
161+
// M1 is the map used for initial property "x". Properties "y" and "z" can be
162+
// stored inline. When later adding "foo" following "y", the map has to be
163+
// forked: a child map M2 is created and M1 remembers this transition at
164+
// property index 1 so that M2 will be used the next time properties "x", "y",
165+
// and "foo" are added to an object.
166+
//
167+
// Shared maps contain a TreeData struct that stores the parent and children
168+
// links for the SharedPropMap tree. The parent link is a tagged pointer that
169+
// stores both the parent map and the property index of the last used property
170+
// in the parent map before the branch. The children are stored similarly: the
171+
// parent map can store a single child map and index, or a set of children.
172+
// See SharedChildrenPtr.
173+
//
174+
// Looking up a child map can then be done based on the index of the last
175+
// property in the parent map and the new property's key and flags. So for the
176+
// example above, the lookup key for M1 => M2 is (index 1, "foo", <flags>).
177+
//
178+
// Note: shared maps can have both a |previous| map and a |parent| map. They are
179+
// equal when the previous map was full, but can be different maps when
180+
// branching in the middle of a map like in the example above: M2 has parent M1
181+
// but does not have a |previous| map (because it only has three properties).
182+
//
183+
// Dictionary Property Maps
184+
// ------------------------
185+
// Certain operations can't be implemented (efficiently) for shared property
186+
// maps, for example changing or deleting a property other than the last one.
187+
// When this happens the map is copied as a DictionaryPropMap.
188+
//
189+
// Dictionary maps are unshared so can be mutated in place (after generating a
190+
// new shape for the object).
191+
//
192+
// Unlike shared maps, dictionary maps can have holes between two property keys
193+
// after removing a property. When there are more holes than properties, the
194+
// map is compacted. See DictionaryPropMap::maybeCompact.
195+
13196
namespace js {
14197

15198
class DictionaryPropMap;

0 commit comments

Comments
 (0)