I am studying the source code of V8 recently. There are 2 definitions of Isolate
class, one in v8::Isolate
and the other in v8::internal::Isolate
. It seems that v8::Isolate
uses v8::internal::Isolate
, but I could not understand the relationship between these 2 definitions/
I have tried to look into the class defitions of both, v8::Isolate
in https://github.com/v8/v8/blob/master/include/v8.h#L7391 and v8::internal::Isolate
in https://github.com/v8/v8/blob/master/src/execution/isolate.h#L452
but could not figure it out.
To be more specific, in v8::Isolate::New
(https://github.com/v8/v8/blob/master/src/api/api.cc#L7903), it returns a C++ object of type v8::Isolate
.
Isolate* Isolate::New(const Isolate::CreateParams& params) {
Isolate* isolate = Allocate();
Initialize(isolate, params);
return isolate;
}
But internally in Allocate
function, it returns an object of type v8::internal::Isolate
, and reinterpret_casted to v8::Isolate
Isolate* Isolate::Allocate() {
return reinterpret_cast<Isolate*>(i::Isolate::New());
}
How can object of class v8::Isolate
be casted from v8::internal::Isolate
?
Can anyone familiar with V8 give me some guidance?
It's a technique not unusual for libraries: v8::internal::Isolate
is the implementation, but its details are entirely encapsulated within the library, and hidden from the public API. v8::Isolate
is just an opaque reference. Observe how it has no fields; embedders know nothing about what it looks like in memory (or whether it has a memory representation at all -- as far as they are concerned, it could be something like a kernel's file descriptor). The reason for this encapsulation, of course, is to separate concerns, and to make components independent from each other: the library can change the internal definition of the class without embedders having to care (i.e. they can't possibly depend on the internal state, so it's guaranteed they won't be broken by changes; they don't even have to get recompiled because the public API [and ABI] doesn't change when the internal class layout changes).
Consider this reduced example demonstrating the principle:
/* public API */
class BlackBox {
public:
static BlackBox* Create();
void DoSomething();
}
void StandaloneFunction(BlackBox* box);
/* internal implementation */
class TheRealThing {
public:
TheRealThing(int value) : value_(value) {}
private:
int value_;
}
BlackBox* BlackBox::Create() {
TheRealThing* real = new TheRealThing(42);
return reinterpret_cast<BlackBox*>(real);
}
void BlackBox::DoSomething() {
TheRealThing* real = reinterpret_cast<TheRealThing*>(this);
printf("Hello %d\n", real->value_);
}
void StandaloneFunction(BlackBox* box) {
TheRealThing* real = reinterpret_cast<TheRealThing*>(box);
printf("Good bye %d\n", real->value_);
}