[V8] Hidden Class
V8은 JS 객체의 프로퍼티에 접근하기 위해 hidden class라는 매커니즘을 사용한다.
객체의 프로퍼티 관리 방법
JavaScript는 동적으로 객체의 프로퍼티를 추가할 수 있다. V8은 JS객체의 프로퍼티를 hidden class라는 C++객체(hidden class)를 사용해서 저장하고 빠르게 접근한다. Hidden class는 동적으로 생성된다.
객체 생성 과정
function Point(x, y) {
this.x = x;
this.y = y;
}
stage 1. initial hidden class
위와 같은 함수가 있을 때, new Point(x, y)
를 호출하면 Point
객체가 만들어진다. V8이 이 작업을 최초로 진행할 때,
V8은 Point
의 초기 hidden class(C0
라고 하자)를 생성한다. 아직 C0
에는 아무런 프로퍼티도 정의되어 있지 않다.
stage 2. add property x
Point
함수의 첫 번째 statement를 실행하면 x
프로퍼티가 생성된다. V8은 C0
를 기반으로
새로운 hidden class C1
을 생성한다. 그리고 C1
에 이 class는 프로퍼티 x
를 가진다는 정보를 기록한다.
C1
객체에서 x
의 값은 offset 0에 저장된다. 그리고 C0
에 해당 객체에 x
프로퍼티가 더해진다면
C0
대신 C1
이 사용되어야 함을 기록한다. 이 단계에서 Point
의 hidden class는 C1
이다.
stage 2. add property y
두 번째 statement(this.y = y
)가 실행되면 V8은 아래와 같은 작업을 한다.
- hidden class
C2
를 생성한다.C2
에 해당 객체가 프로퍼티y
를 가진다는 정보를 저장한다. C1
의 class transition indicating을 업데이트한다. 프로퍼티y
가C1
객체에 추가된 경우C1
대신C2
를 사용해야 한다는 정보를 추가한다.
프로퍼티 이름-offset 정보는 어떻게 저장하는걸까?
프로퍼티 이름에 대응되는 offset을 알려면 dynamic lookup이 필요한거 아닌가?
코드 분석
v8::internal::Map 클래스
src/objects/map.h
파일에 선언되어 있다.- hidden class를 표현한다.
NewMap 함수
Handle<Map> NewMap(DirectHandle<HeapObject> meta_map_holder,
InstanceType type, int instance_size,
ElementsKind elements_kind = TERMINAL_FAST_ELEMENTS_KIND,
int inobject_properties = 0,
AllocationType allocation_type = AllocationType::kMap);
src/heap/factory
- 메모리를 할당받고 새로운 Map(hidden class) 하나를 초기화한다. (Map 인스턴스 생성)
gdb로 객체 생성 과정 분석
./out/x64.release/d8
- 다른 터미널에서
gdb -p <d8 pid>
.(gdb) break NewMap
.(gdb) continue
- d8> function Point(x, y) {this.x = x; this.y = y;}
- d8> let p1 = new Point(5, 5)
- (gdb) bt
#0 0x000061ab2ab5a0f4 in v8::internal::Factory::NewMap(v8::internal::DirectHandle<v8::internal::HeapObject>, v8::inte
rnal::InstanceType, int, v8::internal::ElementsKind, int, v8::internal::AllocationType) ()
#1 0x000061ab2ae84bb8 in v8::internal::Map::RawCopy(v8::internal::Isolate*, v8::internal::DirectHandle<v8::internal::
Map>, int, int) ()
#2 0x000061ab2ae8598d in v8::internal::Map::CopyReplaceDescriptors(v8::internal::Isolate*, v8::internal::DirectHandle
<v8::internal::Map>, v8::internal::DirectHandle<v8::internal::DescriptorArray>, v8::internal::TransitionFlag, v8::inte
rnal::MaybeDirectHandle<v8::internal::Name>, char const*, v8::internal::TransitionKindFlag) ()
#3 0x000061ab2ae81dbf in v8::internal::Map::CopyAddDescriptor(v8::internal::Isolate*, v8::internal::DirectHandle<v8::
internal::Map>, v8::internal::Descriptor*, v8::internal::TransitionFlag) ()
#4 0x000061ab2ae81bb2 in v8::internal::Map::CopyWithField(v8::internal::Isolate*, v8::internal::DirectHandle<v8::inte
rnal::Map>, v8::internal::DirectHandle<v8::internal::Name>, v8::internal::DirectHandle<v8::internal::FieldType>, v8::i
nternal::PropertyAttributes, v8::internal::PropertyConstness, v8::internal::Representation, v8::internal::TransitionFl
ag) ()
#5 0x000061ab2ae87015 in v8::internal::Map::TransitionToDataProperty(v8::internal::Isolate*, v8::internal::DirectHand
le<v8::internal::Map>, v8::internal::DirectHandle<v8::internal::Name>, v8::internal::DirectHandle<v8::internal::Object
>, v8::internal::PropertyAttributes, v8::internal::PropertyConstness, v8::internal::StoreOrigin) ()
#6 0x000061ab2ae78f5f in v8::internal::LookupIterator::PrepareTransitionToDataProperty(v8::internal::DirectHandle<v8:
:internal::JSReceiver>, v8::internal::DirectHandle<v8::internal::Object>, v8::internal::PropertyAttributes, v8::intern
al::StoreOrigin) ()
#7 0x000061ab2aea92cc in v8::internal::Object::TransitionAndWriteDataProperty(v8::internal::LookupIterator*, v8::int$
rnal::DirectHandle<v8::internal::Object>, v8::internal::PropertyAttributes, v8::Maybe<v8::internal::ShouldThrow>, v8::
internal::StoreOrigin) ()
#8 0x000061ab2ae25571 in v8::internal::JSObject::AddProperty(v8::internal::Isolate*, v8::internal::DirectHandle<v8::i
--Type <RET> for more, q to quit, c to continue without paging--
nternal::JSObject>, v8::internal::DirectHandle<v8::internal::Name>, v8::internal::DirectHandle<v8::internal::Object>,
v8::internal::PropertyAttributes) ()
#9 0x000061ab2ab5d420 in v8::internal::Factory::NewFunctionPrototype(v8::internal::DirectHandle<v8::internal::JSFunct
ion>) ()
#10 0x000061ab2ae027f3 in v8::internal::JSFunction::EnsureHasInitialMap(v8::internal::DirectHandle<v8::internal::JSFun
ction>) ()
#11 0x000061ab2ae02cb2 in v8::internal::JSFunction::GetDerivedMap(v8::internal::Isolate*, v8::internal::DirectHandle<v
8::internal::JSFunction>, v8::internal::DirectHandle<v8::internal::JSReceiver>) ()
#12 0x000061ab2ae203eb in v8::internal::JSObject::New(v8::internal::DirectHandle<v8::internal::JSFunction>, v8::intern
al::DirectHandle<v8::internal::JSReceiver>, v8::internal::DirectHandle<v8::internal::AllocationSite>, v8::internal::Ne
wJSObjectType) ()