Why?
JavaScript là một ngôn ngữ lập trình động và do đó mà chúng ta có thể thêm hoặc xóa các thuộc tính của object trong quá trình thực thi.
Ví dụ, trong đoạn code bên dưới, thuộc tính year
được thêm vào object myCar
sau khi nó được khởi tạo:
var car = function(make,model) {
this.make = make;
this.model = model;
}
var myCar = new car(honda,accord);
myCar.year = 2005;
Theo mô tả của ECMAScript specification, mỗi object ở bên trong JS được xem như là một hash table hoặc dictionary của các thuộc tính. Tuy nhiên, việc lưu trữ các thuộc tính trong các cấu trúc dữ liệu này làm cho việc truy xuất tốn nhiều chi phí hơn so với các ngôn ngữ non-dynamic chẳng hạn như Java.
Info
Cụ thể, trong Java, tất cả các thuộc tính được xác định trước khi biên dịch và chúng nằm liền kề nhau ở trong bộ nhớ đi kèm với một offset được xác định bởi kiểu dữ liệu của thuộc tính. Do nằm cạnh nhau, địa chỉ của thuộc tính được xác định thông qua một instruction còn ở trong JavaScript thì cần phải dùng nhiều instruction hơn. Dẫn đến, việc truy xuất ở trong JavaScript sẽ chậm hơn so với Java.
Để giảm thời gian truy xuất thuộc tính trong JavaScript, V8 sử dụng các hidden class (hay map), là các bố cục object cố định tương tự như trong Java nhưng được tạo ra lúc chương trình được thực thi. Mọi object đều được gắn với một hidden class và mỗi offset của từng thuộc tính được tạo ra bởi Torque 1.
Structure
Map sẽ chứa những thành phần sau 2:
- Kích thước của object (instance size).
- Kiểu dữ liệu động của object (instance type) chẳng hạn như
String
,JSArray
,HeapNumber
, etc 3. - Prototype của object, nếu có.
- Con trỏ trỏ đến map cha (back pointer) (xem thêm Map Transition) hoặc constructor.
- Mảng chứa metadata của các thuộc tính (instance descriptors).
Cấu trúc của map:
+---------------+-------------------------------------------------+
| _ Type _ | _ Description _ |
+---------------+-------------------------------------------------+
| TaggedPointer | map - Always a pointer to the MetaMap root |
+---------------+-------------------------------------------------+
| Int | The first int field |
`---+----------+-------------------------------------------------+
| Byte | [instance_size] |
+----------+-------------------------------------------------+
| Byte | If Map for a primitive type: |
| | native context index for constructor fn |
| | If Map for an Object type: |
| | inobject properties start offset in words |
+----------+-------------------------------------------------+
| Byte | [used_or_unused_instance_size_in_words] |
| | For JSObject in fast mode this byte encodes |
| | the size of the object that includes only |
| | the used property fields or the slack size |
| | in properties backing store. |
+----------+-------------------------------------------------+
| Byte | [visitor_id] |
+----+----------+-------------------------------------------------+
| Int | The second int field |
`---+----------+-------------------------------------------------+
| Short | [instance_type] |
+----------+-------------------------------------------------+
| Byte | [bit_field] |
| | - has_non_instance_prototype (bit 0) |
| | - is_callable (bit 1) |
| | - has_named_interceptor (bit 2) |
| | - has_indexed_interceptor (bit 3) |
| | - is_undetectable (bit 4) |
| | - is_access_check_needed (bit 5) |
| | - is_constructor (bit 6) |
| | - has_prototype_slot (bit 7) |
+----------+-------------------------------------------------+
| Byte | [bit_field2] |
| | - new_target_is_base (bit 0) |
| | - is_immutable_proto (bit 1) |
| | - elements_kind (bits 2..7) |
+----+----------+-------------------------------------------------+
| Int | [bit_field3] |
| | - enum_length (bit 0..9) |
| | - number_of_own_descriptors (bit 10..19) |
| | - is_prototype_map (bit 20) |
| | - is_dictionary_map (bit 21) |
| | - owns_descriptors (bit 22) |
| | - is_in_retained_map_list (bit 23) |
| | - is_deprecated (bit 24) |
| | - is_unstable (bit 25) |
| | - is_migration_target (bit 26) |
| | - is_extensible (bit 28) |
| | - may_have_interesting_properties (bit 28) |
| | - construction_counter (bit 29..31) |
| | |
+*****************************************************************+
| Int | On systems with 64bit pointer types, there |
| | is an unused 32bits after bit_field3 |
+*****************************************************************+
| TaggedPointer | [prototype] |
+---------------+-------------------------------------------------+
| TaggedPointer | [constructor_or_back_pointer_or_native_context] |
+---------------+-------------------------------------------------+
| TaggedPointer | [instance_descriptors] |
+*****************************************************************+
| TaggedPointer | [dependent_code] |
+---------------+-------------------------------------------------+
| TaggedPointer | [prototype_validity_cell] |
+---------------+-------------------------------------------------+
| TaggedPointer | If Map is a prototype map: |
| | [prototype_info] |
| | Else: |
| | [raw_transitions] |
+---------------+-------------------------------------------------+
SRC: https://source.chromium.org/chromium/chromium/src/+/d48de87f87a943a1d210514aae7988185c2ba131:v8/src/objects/map.h;l=144
Minh họa:
Map Transition
Bất cứ khi nào chúng ta thêm (hoặc xóa) thuộc tính được đặt tên của một đối tượng JS thì map của nó sẽ thay đổi. Ta gọi những sự thay đổi đó là map transition.
Note
Việc thêm hoặc xóa thuộc tính được đánh chỉ số (indexed properties) không làm thay đổi map của object.
Xét đoạn script sau:
const object = {}
object.x = 1
object.y = 2
Khi object
được tạo ra, map hay hidden class của nó sẽ không chứa thuộc tính nào và ta gọi hidden class này là C0.
Sau khi dòng object.x = 1
được thực thi, V8 sẽ tạo ra một hidden class mới (ta gọi là C1) có thông tin về thuộc tính x
tại offset 0 (do là một in-object property nên x
được lưu trong vùng nhớ của object
). Con trỏ hidden class của object
sau đó sẽ trỏ đến C1. Ngoài ra, C1 cũng sẽ có một con trỏ trỏ về C0 và xem C0 như là hidden class cha của mình. Đồng thời, C0 sẽ thêm vào một phần tử ở trong mảng transitions
cho biết rằng nếu object được thêm vào thuộc tính a
thì hidden class của nó sẽ trở thành C1.
Cuối cùng, dòng object.y = 2
sẽ khiến V8 tạo ra một hidden class khác mà ta gọi là C2. Hidden class C2 sẽ có thông tin về thuộc tính x
tại offset 0 và thông tin về thuộc tính y
tại offset 1. Tương tự với mối quan hệ giữa C1 và C0, C2 sẽ có một con trỏ trỏ đến C1 và C1 sẽ có một phần tử trong transitions
cho biết nếu object với hidden class là C1 được thêm vào thuộc tính y
thì hidden class của nó sẽ trở thành C2.
Mối liên hệ giữa các hidden class chính là các map transition. Trong V8, map transition được biểu diễn như sau:
V8 version 11.6.0 (candidate)
d8> const object = {}
undefined
d8> object.x = 1
1
d8> %DebugPrint(object)
DebugPrint: 0x131c001cc789: [JS_OBJECT_TYPE]
- map: 0x131c000dc6a5 <Map[28](HOLEY_ELEMENTS)> [FastProperties] <--- current hidden class of object
- prototype: 0x131c000c4b79 <Object map = 0x131c000c41b5>
- elements: 0x131c00000219 <FixedArray[0]> [HOLEY_ELEMENTS]
- properties: 0x131c00000219 <FixedArray[0]>
- All own properties (excluding elements): {
0x131c00002bb9: [String] in ReadOnlySpace: #x: 1 (const data field 0), location: in-object
}
0x131c000dc6a5: [Map] in OldSpace
- type: JS_OBJECT_TYPE
- instance size: 28
- inobject properties: 4
- elements kind: HOLEY_ELEMENTS
- unused property fields: 3
- enum length: invalid
- stable_map
- back pointer: 0x131c000c49ad <Map[28](HOLEY_ELEMENTS)>
- prototype_validity cell: 0x131c000dc6ed <Cell value= 0>
- instance descriptors (own) #1: 0x131c001ce431 <DescriptorArray[1]>
- prototype: 0x131c000c4b79 <Object map = 0x131c000c41b5>
- constructor: 0x131c000c46bd <JSFunction Object (sfi = 0x131c0008bef5)>
- dependent code: 0x131c00000229 <Other heap object (WEAK_ARRAY_LIST_TYPE)>
- construction counter: 0
{x: 1}
d8> %DebugPrintPtr(0x131c000c49ad) <--- address of parent hidden class (back pointer)
DebugPrint: 0x131c000c49ad: [Map] in OldSpace
- type: JS_OBJECT_TYPE
- instance size: 28
- inobject properties: 4
- elements kind: HOLEY_ELEMENTS
- unused property fields: 4
- enum length: invalid
- back pointer: 0x131c00000251 <undefined>
- prototype_validity cell: 0x131c00000ab9 <Cell value= 1>
- instance descriptors (own) #0: 0x131c00000289 <DescriptorArray[0]>
- transitions #1: 0x131c000dc6cd <TransitionArray[6]>Transition array #1:
0x131c00002bb9: [String] in ReadOnlySpace: #x: (transition to (const data field, attrs: [WEC]) @ Any) -> 0x131c000dc6a5 <Map[28](HOLEY_ELEMENTS)>
- prototype: 0x131c000c4b79 <Object map = 0x131c000c41b5>
- constructor: 0x131c000c46bd <JSFunction Object (sfi = 0x131c0008bef5)>
- dependent code: 0x131c00000229 <Other heap object (WEAK_ARRAY_LIST_TYPE)>
- construction counter: 0
0x131c00000061: [Map] in ReadOnlySpace
- type: MAP_TYPE
- instance size: 40
- elements kind: HOLEY_ELEMENTS
- unused property fields: 0
- enum length: invalid
- stable_map
- non-extensible
- back pointer: 0x131c00000251 <undefined>
- prototype_validity cell: 0
- instance descriptors (own) #0: 0x131c00000289 <DescriptorArray[0]>
- prototype: 0x131c00000235 <null>
- constructor: 0x131c00000235 <null>
- dependent code: 0x131c00000229 <Other heap object (WEAK_ARRAY_LIST_TYPE)>
- construction counter: 0
21010980817325
Có thể thấy, hidden class cha mà back pointer
trỏ đến có một phần tử trong mảng transitions
cho biết nếu như ta thêm vào hidden class cha thuộc tính x
thì hidden class của object sẽ chuyển sang hidden class hiện tại.
Mảng transitions
có thể có nhiều phần tử trong trường hợp chuỗi hidden class bị rẽ nhánh. Cụ thể, nếu object có hidden class là C1 và thêm vào thuộc tính z
thay vì thuộc tính y
để tạo ra C2 thì V8 sẽ tạo ra một hidden class khác mà ta gọi là C2b. Khi đó, C2b sẽ có back pointer
trỏ đến C1 giống như C2. Đồng thời, mảng transitions
của C1 cũng có thêm một phần tử cho biết nếu như object có hidden class là C1 và thêm vào thuộc tính z
thì hidden class của object sẽ thành C2b.
Như vậy, hai object chỉ có cùng map hay hidden class khi chúng có cùng các thuộc tính với cùng thứ tự.
Nếu ta xóa một thuộc tính được thêm vào cuối cùng thì V8 sẽ chuyển hidden class của object hiện tại thành hidden class cha. Trường hợp xóa một thuộc tính ở giữa thì V8 từ bỏ việc duy trì các map transition và lưu các thuộc tính ở trong dictionary.
Instance Descriptors
Là một mảng lưu metadata của các named property trong object. Việc sử dụng instance desciptors sẽ giúp tiết kiệm bộ nhớ do mảng này được chia sẻ bởi các object có cùng map.
Xét object sau:
const object = {};
object.a = 1;
%DebugPrint(object);
Thông tin của object
:
d8> %DebugPrint(object);
DebugPrint: 0x2cb3001cc789: [JS_OBJECT_TYPE]
- map: 0x2cb3000dc6a5 <Map[28](HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x2cb3000c4b79 <Object map = 0x2cb3000c41b5>
- elements: 0x2cb300000219 <FixedArray[0]> [HOLEY_ELEMENTS]
- properties: 0x2cb300000219 <FixedArray[0]>
- All own properties (excluding elements): {
0x2cb300002a49: [String] in ReadOnlySpace: #a: 1 (const data field 0), location: in-object
}
0x2cb3000dc6a5: [Map] in OldSpace
- type: JS_OBJECT_TYPE
- instance size: 28
- inobject properties: 4
- elements kind: HOLEY_ELEMENTS
- unused property fields: 3
- enum length: invalid
- stable_map
- back pointer: 0x2cb3000c49ad <Map[28](HOLEY_ELEMENTS)>
- prototype_validity cell: 0x2cb3000dc6ed <Cell value= 0>
- instance descriptors (own) #1: 0x2cb3001ce431 <DescriptorArray[1]> <----
- prototype: 0x2cb3000c4b79 <Object map = 0x2cb3000c41b5>
- constructor: 0x2cb3000c46bd <JSFunction Object (sfi = 0x2cb30008bef5)>
- dependent code: 0x2cb300000229 <Other heap object (WEAK_ARRAY_LIST_TYPE)>
- construction counter: 0
{a: 1}
Dùng hàm %DebugPrintPtr
của V8 với đối số là địa chỉ của instance descriptors để in ra thông tin của nó:
d8> %DebugPrintPtr(0x2cb3001ce431)
DebugPrint: 0x2cb3001ce431: [DescriptorArray]
- map: 0x2cb300000129 <Map(DESCRIPTOR_ARRAY_TYPE)>
- enum_cache: 1
- keys: 0x2cb3000dc821 <FixedArray[1]>
- indices: 0x2cb3000dc82d <FixedArray[1]>
- nof slack descriptors: 0
- nof descriptors: 1
- raw gc state: mc epoch 0, marked 0, delta 0
[0]: 0x2cb300002a49: [String] in ReadOnlySpace: #a (const data field 0:s, p: 0, attrs: [WEC]) @ Any
0x2cb300000129: [Map] in ReadOnlySpace
- type: DESCRIPTOR_ARRAY_TYPE
- instance size: variable
- elements kind: HOLEY_ELEMENTS
- unused property fields: 0
- enum length: invalid
- stable_map
- non-extensible
- back pointer: 0x2cb300000251 <undefined>
- prototype_validity cell: 0
- instance descriptors (own) #0: 0x2cb300000289 <DescriptorArray[0]>
- prototype: 0x2cb300000235 <null>
- constructor: 0x2cb300000235 <null>
- dependent code: 0x2cb300000229 <Other heap object (WEAK_ARRAY_LIST_TYPE)>
- construction counter: 0
49147312661553
Một số thông tin trong output trên về instance descriptors của object
:
- Nó có map là
DESCRIPTOR_ARRAY_TYPE
. - Nó có thông tin về thuộc tính
a
với property attribute (xem thêm Property Attributes).
Instance descriptors còn được chia sẻ giữa map cha và map con. Xét ví dụ sau:
const object1 = {}
object1.a = 1
const object2 = {}
object2.a = 1
object2.b = 1
d8> %DebugPrint(object1)
DebugPrint: 0x23c5001cc789: [JS_OBJECT_TYPE]
- map: 0x23c5000dc6a5 <Map[28](HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x23c5000c4b79 <Object map = 0x23c5000c41b5>
- elements: 0x23c500000219 <FixedArray[0]> [HOLEY_ELEMENTS]
- properties: 0x23c500000219 <FixedArray[0]>
- All own properties (excluding elements): {
0x23c500002a49: [String] in ReadOnlySpace: #a: 1 (const data field 0), location: in-object
}
0x23c5000dc6a5: [Map] in OldSpace
- type: JS_OBJECT_TYPE
- instance size: 28
- inobject properties: 4
- elements kind: HOLEY_ELEMENTS
- unused property fields: 3
- enum length: 1
- back pointer: 0x23c5000c49ad <Map[28](HOLEY_ELEMENTS)>
- prototype_validity cell: 0x23c5000dc6ed <Cell value= 0>
- instance descriptors #1: 0x23c5001ce6bd <DescriptorArray[2]>
- transitions #1: 0x23c5000dcbf5 <Map[28](HOLEY_ELEMENTS)>
0x23c500002a59: [String] in ReadOnlySpace: #b: (transition to (const data field, attrs: [WEC]) @ Any) -> 0x23c5000dcbf5 <Map[28](HOLEY_ELEMENTS)>
- prototype: 0x23c5000c4b79 <Object map = 0x23c5000c41b5>
- constructor: 0x23c5000c46bd <JSFunction Object (sfi = 0x23c50008bef5)>
- dependent code: 0x23c500000229 <Other heap object (WEAK_ARRAY_LIST_TYPE)>
- construction counter: 0
{a: 1}
d8> %DebugPrint(object2)
DebugPrint: 0x23c5001ce629: [JS_OBJECT_TYPE]
- map: 0x23c5000dcbf5 <Map[28](HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x23c5000c4b79 <Object map = 0x23c5000c41b5>
- elements: 0x23c500000219 <FixedArray[0]> [HOLEY_ELEMENTS]
- properties: 0x23c500000219 <FixedArray[0]>
- All own properties (excluding elements): {
0x23c500002a49: [String] in ReadOnlySpace: #a: 1 (const data field 0), location: in-object
0x23c500002a59: [String] in ReadOnlySpace: #b: 2 (const data field 1), location: in-object
}
0x23c5000dcbf5: [Map] in OldSpace
- type: JS_OBJECT_TYPE
- instance size: 28
- inobject properties: 4
- elements kind: HOLEY_ELEMENTS
- unused property fields: 2
- enum length: invalid
- stable_map
- back pointer: 0x23c5000dc6a5 <Map[28](HOLEY_ELEMENTS)>
- prototype_validity cell: 0x23c5000dc6ed <Cell value= 0>
- instance descriptors (own) #2: 0x23c5001ce6bd <DescriptorArray[2]> <--- same as object1
- prototype: 0x23c5000c4b79 <Object map = 0x23c5000c41b5>
- constructor: 0x23c5000c46bd <JSFunction Object (sfi = 0x23c50008bef5)>
- dependent code: 0x23c500000229 <Other heap object (WEAK_ARRAY_LIST_TYPE)>
- construction counter: 0
{a: 1, b: 2}
Có thể thấy, instance descriptors của object1
và object2
là giống nhau mặc dù object1
không có thuộc tính b
.
Cấu trúc của instance descriptors:
Header:
[16:0 bits]: number_of_all_descriptors (including slack)
[32:16 bits]: number_of_descriptors
[48:32 bits]: raw_number_of_marked_descriptors (used by GC)
[64:48 bits]: alignment filler
[kEnumCacheOffset]: enum cache
Elements:
[kHeaderSize + 0]: first key (and internalized String)
[kHeaderSize + 1]: first descriptor details (see PropertyDetails)
[kHeaderSize + 2]: first value for constants / Smi(1) when not used
Slack:
[kHeaderSize + number of descriptors * 3]: start of slack
The "value" fields store either values or field types. A field type is either
FieldType::None(), FieldType::Any() or a weak reference to a Map. All other
references are strong.
Giá trị in ra khi debug:
d8> %DebugPrintPtr(0x23c5001ce6bd)
DebugPrint: 0x23c5001ce6bd: [DescriptorArray]
- map: 0x23c500000129 <Map(DESCRIPTOR_ARRAY_TYPE)>
- enum_cache: 2
- keys: 0x23c5000dcf4d <FixedArray[2]>
- indices: 0x23c5000dcf5d <FixedArray[2]>
- nof slack descriptors: 0
- nof descriptors: 2
- raw gc state: mc epoch 0, marked 0, delta 0
[0]: 0x23c500002a49: [String] in ReadOnlySpace: #a (const data field 0:s, p: 1, attrs: [WEC]) @ Any
[1]: 0x23c500002a59: [String] in ReadOnlySpace: #b (const data field 1:s, p: 0, attrs: [WEC]) @ Any
0x23c500000129: [Map] in ReadOnlySpace
- type: DESCRIPTOR_ARRAY_TYPE
- instance size: variable
- elements kind: HOLEY_ELEMENTS
- unused property fields: 0
- enum length: invalid
- stable_map
- non-extensible
- back pointer: 0x23c500000251 <undefined>
- prototype_validity cell: 0
- instance descriptors (own) #0: 0x23c500000289 <DescriptorArray[0]>
- prototype: 0x23c500000235 <null>
- constructor: 0x23c500000235 <null>
- dependent code: 0x23c500000229 <Other heap object (WEAK_ARRAY_LIST_TYPE)>
- construction counter: 0
39329017423549
Enum Cache
Là một cấu trúc dữ liệu tồn tại bên trong instance descriptors chứa các named property của object.
Cấu trúc của enum cache bên trong instance descriptor:
> const obj = {a:1,b:2}
undefined
...
> %DebugPrintPtr(0x3c5901055db9)
DebugPrint: 0x3c5901055db9: [DescriptorArray] in OldSpace
- map: 0x2f7356647761 <Map>
- enum_cache: 2
- keys: 0x06d609142ee9 <FixedArray[2]>
- indices: 0x06d609142f09 <FixedArray[2]>
- nof slack descriptors: 0
- nof descriptors: 2
- raw marked descriptors: mc epoch 1, marked 2
[0]: 0x2f735664a011: [String] in ReadOnlySpace: #a (const data field 0:s, p: 0, attrs: [WEC]) @ Any
[1]: 0x2f735664a0d1: [String] in ReadOnlySpace: #b (const data field 1:s, p: 1, attrs: [WEC]) @ Any
Có thể thấy, enum cache sẽ bao gồm hai con trỏ của hai mảng là keys
và indices
.
Khi JavaScript lặp qua các thuộc tính chẳng hạn như sử dụng for in
, Object.keys
hoặc Object.getOwnPropertyNames
, V8 sẽ cần phải trích xuất và trả về một danh sách các tên thuộc tính. Thao tác này sẽ làm giảm hiệu năng nếu được lặp lại nhiều lần, đặc biệt là đối với các object có nhiều thuộc tính.
Vì lý do đó, khi các thuộc tính được enumerate lần đầu tiên, V8 sẽ lưu các thuộc tính này vào enum cache. Đối với những lần enumerate sau, nếu map của object không đổi, V8 sẽ dùng enum cache có sẵn để truy xuất thuộc tính.
Chi tiết hơn, thay vì gọi hàm JSLoadProperty
để truy xuất thuộc tính đã được enumerated, V8 sẽ sử dụng các index trong mảng indices
của enum cache (thông qua hàm LoadFieldByIndex
) để truy xuất thuộc tính 4.
Ví dụ, xét đoạn script sau:
const object1 = {};
object1.a = 1;
object1.b = 2;
function test(callback) {
for (let key in object1) {
console.log(object1[key]);
}
}
%PrepareFunctionForOptimization(test);
test();
test();
%OptimizeFunctionOnNextCall(test);
test();
Đồ thị sea-of-nodes của V8 - Turbo Fan khi chưa tối ưu sẽ có hàm JSLoadProperty
trước khi gọi hàm JSCall
để thực thi hàmconsole.log
:
Đồ thị sea-of-nodes của Turbo Fan sau khi tối ưu (ở bước TypedLowering) sẽ không có hàm JSLoadProperty
và thay vào đó là hàm LoadFieldByIndex
trước khi gọi hàm console.log
:
Hàm chịu trách nhiệm chuyển JSLoadProperty
thành LoadFieldByIndex
là JSNativeContextSpecialization::ReduceJSLoadPropertyWithEnumeratedKey
.
Tuy nhiên, nếu như có thuộc tính được thêm vào hoặc xóa khỏi object, map và instance descriptors sẽ thay đổi. Điều này khiến cho enum cache cũng bị thay đổi và sẽ được cập nhật trong lần enumerate tiếp theo.
Related
list
from outgoing([[Hidden Classes]])
sort file.ctime asc
Resources
- https://www.youtube.com/watch?v=FrufJFBSoQY&t=607s
- Performance tips for JavaScript in V8 | Articles | web.dev
- Javascript Hidden Classes and Inline Caching in V8 (richardartoul.github.io)
- https://mrale.ph/blog/2012/06/03/explaining-js-vms-in-js-inline-caches.html
- https://mrale.ph/blog/2015/01/11/whats-up-with-monomorphism.html
- https://techburstmag.com/dev/javascript-optimization-inline-caches/
- https://v8.dev/blog/fast-properties
- What do Java objects look like in memory during run-time? – Program Creek
- https://cwresearchlab.co.kr/entry/CVE-2023-4427-PoC-Out-of-bounds-memory-access-in-V8
Footnotes
-
tìm ở đường dẫn
delete
ở trong V8. ↩ -
thông tin về cấu trúc của map có thể xem ở
/src/objects/map.h
hoặc/torque-generated/src/objects/*.tq.inc
ở trong mã nguồn của V8. ↩ -
các kiểu dữ liệu của object trong V8 được liệt kê ở
/src/objects/objects.h
. ↩ -
tham khảo thêm comment bên trong hàm ReduceJSLoadPropertyWithEnumeratedKey ↩