{"id":13523515,"url":"https://github.com/danbev/learning-v8","last_synced_at":"2025-05-14T02:09:55.597Z","repository":{"id":38984398,"uuid":"57430512","full_name":"danbev/learning-v8","owner":"danbev","description":"Project for learning V8 internals","archived":false,"fork":false,"pushed_at":"2024-11-15T15:58:42.000Z","size":1760,"stargazers_count":2638,"open_issues_count":7,"forks_count":240,"subscribers_count":77,"default_branch":"master","last_synced_at":"2025-04-13T04:55:31.437Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danbev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["danbev"]}},"created_at":"2016-04-30T06:44:17.000Z","updated_at":"2025-04-12T23:35:37.000Z","dependencies_parsed_at":"2024-04-12T03:40:32.807Z","dependency_job_id":"c395190e-a572-4b91-9872-bdee3673f2a9","html_url":"https://github.com/danbev/learning-v8","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danbev%2Flearning-v8","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danbev%2Flearning-v8/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danbev%2Flearning-v8/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danbev%2Flearning-v8/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danbev","download_url":"https://codeload.github.com/danbev/learning-v8/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254053321,"owners_count":22006717,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T06:01:00.787Z","updated_at":"2025-05-14T02:09:50.586Z","avatar_url":"https://github.com/danbev.png","language":"C++","readme":"## Learning Google V8\nThe sole purpose of this project is to aid me in leaning Google's V8 JavaScript engine.\n\n\n### Contents\n1. [Introduction](./notes/intro.md)\n1. [Address](#address)\n1. [TaggedImpl](#taggedimpl)\n1. [Object](#object)\n1. [Handle](#handle)\n1. [FunctionTemplate](#functiontemplate)\n1. [ObjectTemplate](#objecttemplate)\n1. [Small Integers](#small-integers)\n1. [String types](./notes/string.md)\n1. [Roots](#roots)\n1. [Heap](./notes/heap.md)\n1. [Builtins](#builtins)\n1. [Compiler pipeline](#compiler-pipeline)\n1. [CodeStubAssembler](#codestubassembler)\n1. [Torque](#torque)\n1. [WebAssembly](#webassembly)\n1. [Promises](./notes/promises.md)\n1. [Snapshots](./notes/snapshots.md)\n1. [V8 Build artifacts](#v8-build-artifacts)\n1. [V8 Startup walkthrough](#startup-walk-through)\n1. [Building V8](#building-v8)\n1. [Contributing a change](#contributing-a-change)\n1. [Debugging](#debugging)\n1. [Building chromium](#building-chromium)\n1. [Goma chromium](#goma)\n1. [EcmaScript notes](./notes/ecmaspec.md)\n1. [GN notes](./notes/gn.md)\n\n### Isolate\nAn Isolate is an independant copy of the V8 runtime which includes its own heap.\nTwo different Isolates can run in parallel and can be seen as entirely different\nsandboxed instances of a V8 runtime.\n\n### Context\nTo allow separate JavaScript applications to run in the same isolate a context\nmust be specified for each one.  This is to avoid them interfering with each\nother, for example by changing the builtin objects provided.\n\n### Template\nThis is the super class of both ObjecTemplate and FunctionTemplate. Remember\nthat in JavaScript a function can have fields just like objects.\n\n```c++\nclass V8_EXPORT Template : public Data {\n public:\n  void Set(Local\u003cName\u003e name, Local\u003cData\u003e value,\n           PropertyAttribute attributes = None);\n  void SetPrivate(Local\u003cPrivate\u003e name, Local\u003cData\u003e value,\n                  PropertyAttribute attributes = None);\n  V8_INLINE void Set(Isolate* isolate, const char* name, Local\u003cData\u003e value);\n\n  void SetAccessorProperty(\n     Local\u003cName\u003e name,\n     Local\u003cFunctionTemplate\u003e getter = Local\u003cFunctionTemplate\u003e(),\n     Local\u003cFunctionTemplate\u003e setter = Local\u003cFunctionTemplate\u003e(),\n     PropertyAttribute attribute = None,\n     AccessControl settings = DEFAULT);\n```\nThe `Set` function can be used to have an name and a value set on an instance\ncreated from this template.\nThe `SetAccessorProperty` is for properties that are get/set using functions.\n\n```c++\nenum PropertyAttribute {\n  /** None. **/\n  None = 0,\n  /** ReadOnly, i.e., not writable. **/\n  ReadOnly = 1 \u003c\u003c 0,\n  /** DontEnum, i.e., not enumerable. **/\n  DontEnum = 1 \u003c\u003c 1,\n  /** DontDelete, i.e., not configurable. **/\n  DontDelete = 1 \u003c\u003c 2\n};\n\nenum AccessControl {\n  DEFAULT               = 0,\n  ALL_CAN_READ          = 1,\n  ALL_CAN_WRITE         = 1 \u003c\u003c 1,\n  PROHIBITS_OVERWRITING = 1 \u003c\u003c 2\n};\n```\n\n\n### ObjectTemplate\nThese allow you to create JavaScript objects without a dedicated constructor.\nWhen an instance is created using an ObjectTemplate the new instance will have\nthe properties and functions configured on the ObjectTemplate.\n\nThis would be something like:\n```js\nconst obj = {};\n```\nThis class is declared in include/v8.h and extends Template:\n```c++\nclass V8_EXPORT ObjectTemplate : public Template { \n  ...\n}\nclass V8_EXPORT Template : public Data {\n  ...\n}\nclass V8_EXPORT Data {\n private:                                                                       \n  Data();                                                                       \n};\n```\nWe create an instance of ObjectTemplate and we can add properties to it that\nall instance created using this ObjectTemplate instance will have. This is done\nby calling `Set` which is member of the `Template` class. You specify a\nLocal\u003cName\u003e for the property. `Name` is a superclass for `Symbol` and `String`\nwhich can be both be used as names for a property.\n\nThe implementation for `Set` can be found in `src/api/api.cc`:\n```c++\nvoid Template::Set(v8::Local\u003cName\u003e name, v8::Local\u003cData\u003e value, v8::PropertyAttribute attribute) {\n  ...\n\n  i::ApiNatives::AddDataProperty(isolate, templ, Utils::OpenHandle(*name),           \n                                 value_obj,                                     \n                                 static_cast\u003ci::PropertyAttributes\u003e(attribute));\n}\n```\n\nThere is an example in [objecttemplate_test.cc](./test/objecttemplate_test.cc)\n\n### FunctionTemplate\nIs a template that is used to create functions and like ObjectTemplate it inherits\nfrom Template:\n```c++\nclass V8_EXPORT FunctionTemplate : public Template {\n}\n```\nRememeber that a function in javascript can have properties just like object.\n\nThere is an example in [functiontemplate_test.cc](./test/functiontemplate_test.cc)\n\nAn instance of a function template can be created using:\n```c++\n  Local\u003cFunctionTemplate\u003e ft = FunctionTemplate::New(isolate_, function_callback, data);\n  Local\u003cFunction\u003e function = ft-\u003eGetFunction(context).ToLocalChecked();\n```\nAnd the function can be called using:\n```c++\n  MaybeLocal\u003cValue\u003e ret = function-\u003eCall(context, recv, 0, nullptr);\n```\nFunction::Call can be found in `src/api/api.cc`: \n```c++\n  bool has_pending_exception = false;\n  auto self = Utils::OpenHandle(this);                                               \n  i::Handle\u003ci::Object\u003e recv_obj = Utils::OpenHandle(*recv);                          \n  i::Handle\u003ci::Object\u003e* args = reinterpret_cast\u003ci::Handle\u003ci::Object\u003e*\u003e(argv);   \n  Local\u003cValue\u003e result;                                                               \n  has_pending_exception = !ToLocal\u003cValue\u003e(                                           \n      i::Execution::Call(isolate, self, recv_obj, argc, args), \u0026result);\n```\nNotice that the return value of `Call` which is a `MaybeHandle\u003cObject\u003e` will be\npassed to ToLocal\u003cValue\u003e which is defined in `api.h`:\n```c++\ntemplate \u003cclass T\u003e                                                              \ninline bool ToLocal(v8::internal::MaybeHandle\u003cv8::internal::Object\u003e maybe,      \n                    Local\u003cT\u003e* local) {                                          \n  v8::internal::Handle\u003cv8::internal::Object\u003e handle;                            \n  if (maybe.ToHandle(\u0026handle)) {                                                   \n    *local = Utils::Convert\u003cv8::internal::Object, T\u003e(handle);                   \n    return true;                                                                \n  }                                                                                \n  return false;                                                                 \n```\nSo lets take a look at `Execution::Call` which can be found in `execution/execution.cc`\nand it calls:\n```c++\nreturn Invoke(isolate, InvokeParams::SetUpForCall(isolate, callable, receiver, argc, argv));\n```\n`SetUpForCall` will return an `InvokeParams`.\nTODO: Take a closer look at InvokeParams.\n```c++\nV8_WARN_UNUSED_RESULT MaybeHandle\u003cObject\u003e Invoke(Isolate* isolate,              \n                                                 const InvokeParams\u0026 params) {\n```\n```c++\nHandle\u003cObject\u003e receiver = params.is_construct                             \n                                    ? isolate-\u003efactory()-\u003ethe_hole_value()         \n                                    : params.receiver; \n```\nIn our case `is_construct` is false as we are not using `new` and the receiver,\nthe `this` in the function should be set to the receiver that we passed in. After\nthat we have `Builtins::InvokeApiFunction` \n```c++\nauto value = Builtins::InvokeApiFunction(                                 \n          isolate, params.is_construct, function, receiver, params.argc,        \n          params.argv, Handle\u003cHeapObject\u003e::cast(params.new_target)); \n```\n\n```c++\nresult = HandleApiCallHelper\u003cfalse\u003e(isolate, function, new_target,        \n                                    fun_data, receiver, arguments);\n```\n\n`api-arguments-inl.h` has:\n```c++\nFunctionCallbackArguments::Call(CallHandlerInfo handler) {\n  ...\n  ExternalCallbackScope call_scope(isolate, FUNCTION_ADDR(f));                  \n  FunctionCallbackInfo\u003cv8::Value\u003e info(values_, argv_, argc_);                  \n  f(info);\n  return GetReturnValue\u003cObject\u003e(isolate);\n}\n```\nThe call to f(info) is what invokes the callback, which is just a normal\nfunction call. \n\nBack in `HandleApiCallHelper` we have:\n```c++\nHandle\u003cObject\u003e result = custom.Call(call_data);                             \n                                                                                \nRETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);\n```\n`RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION` expands to:\n```c++\nHandle\u003cObject\u003e result = custom.Call(call_data);                             \ndo { \n  Isolate* __isolate__ = (isolate); \n  ((void) 0); \n  if (__isolate__-\u003ehas_scheduled_exception()) { \n    __isolate__-\u003ePromoteScheduledException(); \n    return MaybeHandle\u003cObject\u003e(); \n  }\n} while (false);\n```\nNotice that if there was an exception an empty object is returned.\nLater in `Invoke` in `execution.cc`a:\n```c++\n  auto value = Builtins::InvokeApiFunction(                                 \n          isolate, params.is_construct, function, receiver, params.argc,        \n          params.argv, Handle\u003cHeapObject\u003e::cast(params.new_target));            \n  bool has_exception = value.is_null();                                     \n  if (has_exception) {                                                      \n    if (params.message_handling == Execution::MessageHandling::kReport) {   \n      isolate-\u003eReportPendingMessages();                                     \n    }                                                                       \n    return MaybeHandle\u003cObject\u003e();                                           \n  } else {                                                                  \n    isolate-\u003eclear_pending_message();                                       \n  }                                                                         \n  return value;                         \n```\nLooking at this is looks like passing back an empty object will cause an \nexception to be triggered?\n\n### Address\n`Address` can be found in `include/v8-internal.h`:\n\n```c++\ntypedef uintptr_t Address;\n```\n`uintptr_t` is an optional type specified in `cstdint` and is capable of storing\na data pointer. It is an unsigned integer type that any valid pointer to void\ncan be converted to this type (and back).\n\n### TaggedImpl\nThis class is declared in `src/objects/tagged-impl.h and has a single private\nmember which is declared as:\n```c++\n public\n  constexpr StorageType ptr() const { return ptr_; }\n private:\n  StorageType ptr_;\n```\nAn instance can be created using:\n```c++\n  i::TaggedImpl\u003ci::HeapObjectReferenceType::STRONG, i::Address\u003e  tagged{};\n```\nStorage type can also be `Tagged_t` which is defined in globals.h:\n```c++\n using Tagged_t = uint32_t;\n```\nIt looks like it can be a different value when using pointer compression.\n\nSee [tagged_test.cc](./test/tagged_test.cc) for an example.\n\n### Object\nThis class extends TaggedImpl:\n```c++\nclass Object : public TaggedImpl\u003cHeapObjectReferenceType::STRONG, Address\u003e {       \n```\nAn Object can be created using the default constructor, or by passing in an \nAddress which will delegate to TaggedImpl constructors. Object itself does\nnot have any members (apart from `ptr_` which is inherited from TaggedImpl that is). \nSo if we create an Object on the stack this is like a pointer/reference to\nan object: \n```\n+------+\n|Object|\n|------|\n|ptr_  |----\u003e\n+------+\n```\nNow, `ptr_` is a StorageType so it could be a `Smi` in which case it would just\ncontains the value directly, for example a small integer:\n```\n+------+\n|Object|\n|------|\n|  18  |\n+------+\n```\nSee [object_test.cc](./test/object_test.cc) for an example.\n\n### ObjectSlot\n```c++\n  i::Object obj{18};\n  i::FullObjectSlot slot{\u0026obj};\n```\n\n```\n+----------+      +---------+\n|ObjectSlot|      | Object  |\n|----------|      |---------|\n| address  | ---\u003e |   18    |\n+----------+      +---------+\n```\nSee [objectslot_test.cc](./test/objectslot_test.cc) for an example.\n\n### Maybe\nA Maybe is like an optional which can either hold a value or nothing.\n```c++\ntemplate \u003cclass T\u003e                                                              \nclass Maybe {\n public:\n  V8_INLINE bool IsNothing() const { return !has_value_; }                      \n  V8_INLINE bool IsJust() const { return has_value_; }\n  ...\n\n private:\n  bool has_value_;                                                              \n  T value_; \n}\n```\nI first thought that name `Just` was a little confusing but if you read this\nlike:\n```c++\n  bool cond = true;\n  Maybe\u003cint\u003e maybe = cond ? Just\u003cint\u003e(10) : Nothing\u003cint\u003e();\n```\nI think it makes more sense. There are functions that check if the Maybe is\nnothing and crash the process if so. You can also check and return the value\nby using `FromJust`. \n\nThe usage of Maybe is where api calls can fail and returning Nothing is a way\nof signaling this.\n\nSee [maybe_test.cc](./test/maybe_test.cc) for an example.\n\n### MaybeLocal\n```c++\ntemplate \u003cclass T\u003e                                                              \nclass MaybeLocal {\n public:                                                                        \n  V8_INLINE MaybeLocal() : val_(nullptr) {} \n  V8_INLINE Local\u003cT\u003e ToLocalChecked();\n  V8_INLINE bool IsEmpty() const { return val_ == nullptr; }\n  template \u003cclass S\u003e                                                            \n  V8_WARN_UNUSED_RESULT V8_INLINE bool ToLocal(Local\u003cS\u003e* out) const {           \n    out-\u003eval_ = IsEmpty() ? nullptr : this-\u003eval_;                               \n    return !IsEmpty();                                                          \n  }    \n\n private:\n  T* val_;\n```\n`ToLocalChecked` will crash the process if `val_` is a nullptr. If you want to\navoid a crash one can use `ToLocal`.\n\nSee [maybelocal_test.cc](./test/maybelocal_test.cc) for an example.\n\n### Data\nIs the super class of all objects that can exist the V8 heap:\n```c++\nclass V8_EXPORT Data {                                                          \n private:                                                                       \n  Data();                                                                       \n};\n```\n\n### Value\nValue extends `Data` and adds a number of methods that check if a Value\nis of a certain type, like `IsUndefined()`, `IsNull`, `IsNumber` etc.\nIt also has useful methods to convert to a Local\u003cT\u003e, for example:\n```c++\nV8_WARN_UNUSED_RESULT MaybeLocal\u003cNumber\u003e ToNumber(Local\u003cContext\u003e context) const;\nV8_WARN_UNUSED_RESULT MaybeLocal\u003cString\u003e ToNumber(Local\u003cString\u003e context) const;\n...\n```\n\n\n### Handle\nA Handle is similar to a Object and ObjectSlot in that it also contains\nan Address member (called `location_` and declared in `HandleBase`), but with the\ndifference is that Handles acts as a layer of abstraction and can be relocated\nby the garbage collector.\nCan be found in `src/handles/handles.h`.\n\n```c++\nclass HandleBase {  \n ...\n protected:\n  Address* location_; \n}\ntemplate \u003ctypename T\u003e                                                           \nclass Handle final : public HandleBase {\n  ...\n}\n```\n\n```\n+----------+                  +--------+         +---------+\n|  Handle  |                  | Object |         |   int   |\n|----------|      +-----+     |--------|         |---------|\n|*location_| ---\u003e |\u0026ptr_| --\u003e | ptr_   | -----\u003e  |     5   |\n+----------+      +-----+     +--------+         +---------+\n```\n```console\n(gdb) p handle\n$8 = {\u003cv8::internal::HandleBase\u003e = {location_ = 0x7ffdf81d60c0}, \u003cNo data fields\u003e}\n```\nNotice that `location_` contains a pointer:\n```console\n(gdb) p /x *(int*)0x7ffdf81d60c0\n$9 = 0xa9d330\n```\nAnd this is the same as the value in obj:\n```console\n(gdb) p /x obj.ptr_\n$14 = 0xa9d330\n```\nAnd we can access the int using any of the pointers:\n```console\n(gdb) p /x *value\n$16 = 0x5\n(gdb) p /x *obj.ptr_\n$17 = 0x5\n(gdb) p /x *(int*)0x7ffdf81d60c0\n$18 = 0xa9d330\n(gdb) p /x *(*(int*)0x7ffdf81d60c0)\n$19 = 0x5\n```\n\nSee [handle_test.cc](./test/handle_test.cc) for an example.\n\n### HandleScope\nContains a number of Local/Handle's (think pointers to objects but is managed\nby V8) and will take care of deleting the Local/Handles for us. HandleScopes\nare stack allocated\n\nWhen ~HandleScope is called all handles created within that scope are removed\nfrom the stack maintained by the HandleScope which makes objects to which the\nhandles point being eligible for deletion from the heap by the GC.\n\nA HandleScope only has three members:\n```c++\n  internal::Isolate* isolate_;\n  internal::Address* prev_next_;\n  internal::Address* prev_limit_;\n```\n\nLets take a closer look at what happens when we construct a HandleScope:\n```c++\n  v8::HandleScope handle_scope{isolate_};\n```\nThe constructor call will end up in `src/api/api.cc` and the constructor simply\ndelegates to `Initialize`:\n```c++\nHandleScope::HandleScope(Isolate* isolate) { Initialize(isolate); }\n\nvoid HandleScope::Initialize(Isolate* isolate) {\n  i::Isolate* internal_isolate = reinterpret_cast\u003ci::Isolate*\u003e(isolate);\n  ...\n  i::HandleScopeData* current = internal_isolate-\u003ehandle_scope_data();\n  isolate_ = internal_isolate;\n  prev_next_ = current-\u003enext;\n  prev_limit_ = current-\u003elimit;\n  current-\u003elevel++;\n}\n```\nEvery `v8::internal::Isolate` has member of type HandleScopeData:\n```c++\nHandleScopeData* handle_scope_data() { return \u0026handle_scope_data_; }\nHandleScopeData handle_scope_data_;\n```\nHandleScopeData is a struct defined in `src/handles/handles.h`:\n```c++\nstruct HandleScopeData final {\n  Address* next;\n  Address* limit;\n  int level;\n  int sealed_level;\n  CanonicalHandleScope* canonical_scope;\n\n  void Initialize() {\n    next = limit = nullptr;\n    sealed_level = level = 0;\n    canonical_scope = nullptr;\n  }\n};\n```\nNotice that there are two pointers (Address*) to next and a limit. When a \nHandleScope is Initialized the current handle_scope_data will be retrieved \nfrom the internal isolate. The HandleScope instance that is getting created\nstores the next/limit pointers of the current isolate so that they can be restored\nwhen this HandleScope is closed (see CloseScope).\n\nSo with a HandleScope created, how does a Local\u003cT\u003e interact with this instance?  \n\nWhen a Local\u003cT\u003e is created this will/might go through FactoryBase::NewStruct\nwhich will allocate a new Map and then create a Handle for the InstanceType\nbeing created:\n```c++\nHandle\u003cStruct\u003e str = handle(Struct::cast(result), isolate()); \n```\nThis will land in the constructor Handle\u003cT\u003esrc/handles/handles-inl.h\n```c++\ntemplate \u003ctypename T\u003e                                                           \nHandle\u003cT\u003e::Handle(T object, Isolate* isolate): HandleBase(object.ptr(), isolate) {}\n\nHandleBase::HandleBase(Address object, Isolate* isolate)                        \n    : location_(HandleScope::GetHandle(isolate, object)) {}\n```\nNotice that `object.ptr()` is used to pass the Address to HandleBase.\nAnd also notice that HandleBase sets its location_ to the result of HandleScope::GetHandle.\n\n```c++\nAddress* HandleScope::GetHandle(Isolate* isolate, Address value) {              \n  DCHECK(AllowHandleAllocation::IsAllowed());                                   \n  HandleScopeData* data = isolate-\u003ehandle_scope_data();                         \n  CanonicalHandleScope* canonical = data-\u003ecanonical_scope;                      \n  return canonical ? canonical-\u003eLookup(value) : CreateHandle(isolate, value);   \n}\n```\nWhich will call `CreateHandle` in this case and this function will retrieve the\ncurrent isolate's handle_scope_data:\n```c++\n  HandleScopeData* data = isolate-\u003ehandle_scope_data();                         \n  Address* result = data-\u003enext;                                                 \n  if (result == data-\u003elimit) {                                                  \n    result = Extend(isolate);                                                   \n  }     \n```\nIn this case both next and limit will be 0x0 so Extend will be called.\nExtend will also get the isolates handle_scope_data and check the current level\nand after that get the isolates HandleScopeImplementer:\n```c++\n  HandleScopeImplementer* impl = isolate-\u003ehandle_scope_implementer();           \n```\n`HandleScopeImplementer` is declared in `src/api/api.h`\n\nHandleScope:CreateHandle will get the handle_scope_data from the isolate:\n```c++\nAddress* HandleScope::CreateHandle(Isolate* isolate, Address value) {\n  HandleScopeData* data = isolate-\u003ehandle_scope_data();\n  if (result == data-\u003elimit) {\n    result = Extend(isolate);\n  }\n  // Update the current next field, set the value in the created handle,        \n  // and return the result.\n  data-\u003enext = reinterpret_cast\u003cAddress*\u003e(reinterpret_cast\u003cAddress\u003e(result) + sizeof(Address));\n  *result = value;\n  return result;\n}                         \n```\nNotice that `data-\u003enext` is set to the address passed in + the size of an\nAddress.\n\n\nThe destructor for HandleScope will call CloseScope.\nSee [handlescope_test.cc](./test/handlescope_test.cc) for an example.\n\n### EscapableHandleScope\nLocal handles are located on the stack and are deleted when the appropriate\ndestructor is called. If there is a local HandleScope then it will take care\nof this when the scope returns. When there are no references left to a handle\nit can be garbage collected. This means if a function has a HandleScope and\nwants to return a handle/local it will not be available after the function\nreturns. This is what EscapableHandleScope is for, it enable the value to be\nplaced in the enclosing handle scope to allow it to survive. When the enclosing\nHandleScope goes out of scope it will be cleaned up.\n\n```c++\nclass V8_EXPORT EscapableHandleScope : public HandleScope {                        \n public:                                                                           \n  explicit EscapableHandleScope(Isolate* isolate);\n  V8_INLINE ~EscapableHandleScope() = default;\n  template \u003cclass T\u003e\n  V8_INLINE Local\u003cT\u003e Escape(Local\u003cT\u003e value) {\n    internal::Address* slot = Escape(reinterpret_cast\u003cinternal::Address*\u003e(*value));\n    return Local\u003cT\u003e(reinterpret_cast\u003cT*\u003e(slot));\n  }\n\n  template \u003cclass T\u003e\n  V8_INLINE MaybeLocal\u003cT\u003e EscapeMaybe(MaybeLocal\u003cT\u003e value) {\n    return Escape(value.FromMaybe(Local\u003cT\u003e()));\n  }\n\n private:\n  ...\n  internal::Address* escape_slot_;\n};\n```\n\nFrom `api.cc`\n```c++\nEscapableHandleScope::EscapableHandleScope(Isolate* v8_isolate) {\n  i::Isolate* isolate = reinterpret_cast\u003ci::Isolate*\u003e(v8_isolate);\n  escape_slot_ = CreateHandle(isolate, i::ReadOnlyRoots(isolate).the_hole_value().ptr());\n  Initialize(v8_isolate);\n}\n```\nSo when an EscapableHandleScope is created it will create a handle with the\nhole value and store it in the `escape_slot_` which is of type Address. This\nHandle will be created in the current HandleScope, and EscapableHandleScope\ncan later set a value for that pointer/address which it want to be escaped.\nLater when that HandleScope goes out of scope it will be cleaned up.\nIt then calls Initialize just like a normal HandleScope would.\n\n```c++\ni::Address* HandleScope::CreateHandle(i::Isolate* isolate, i::Address value) {\n  return i::HandleScope::CreateHandle(isolate, value);\n}\n```\nFrom `handles-inl.h`:\n```c++\nAddress* HandleScope::CreateHandle(Isolate* isolate, Address value) {\n  DCHECK(AllowHandleAllocation::IsAllowed());\n  HandleScopeData* data = isolate-\u003ehandle_scope_data();\n  Address* result = data-\u003enext;\n  if (result == data-\u003elimit) {\n    result = Extend(isolate);\n  }\n  // Update the current next field, set the value in the created handle,\n  // and return the result.\n  DCHECK_LT(reinterpret_cast\u003cAddress\u003e(result),\n            reinterpret_cast\u003cAddress\u003e(data-\u003elimit));\n  data-\u003enext = reinterpret_cast\u003cAddress*\u003e(reinterpret_cast\u003cAddress\u003e(result) +\n                                          sizeof(Address));\n  *result = value;\n  return result;\n}\n```\n\nWhen Escape is called the following happens (v8.h):\n```c++\ntemplate \u003cclass T\u003e\n  V8_INLINE Local\u003cT\u003e Escape(Local\u003cT\u003e value) {\n    internal::Address* slot = Escape(reinterpret_cast\u003cinternal::Address*\u003e(*value));\n    return Local\u003cT\u003e(reinterpret_cast\u003cT*\u003e(slot));\n  }\n```\nAn the EscapeableHandleScope::Escape (api.cc):\n```c++\ni::Address* EscapableHandleScope::Escape(i::Address* escape_value) {\n  i::Heap* heap = reinterpret_cast\u003ci::Isolate*\u003e(GetIsolate())-\u003eheap();\n  Utils::ApiCheck(i::Object(*escape_slot_).IsTheHole(heap-\u003eisolate()),\n                  \"EscapableHandleScope::Escape\", \"Escape value set twice\");\n  if (escape_value == nullptr) {\n    *escape_slot_ = i::ReadOnlyRoots(heap).undefined_value().ptr();\n    return nullptr;\n  }\n  *escape_slot_ = *escape_value;\n  return escape_slot_;\n}\n```\nIf the escape_value is null, the `escape_slot` that is a pointer into the \nparent HandleScope is set to the undefined_value() instead of the hole value\nwhich is was previously, and nullptr will be returned. This returned \naddress/pointer will then be returned after being casted to T*.\nNext, we take a look at what happens when the EscapableHandleScope goes out of\nscope. This will call HandleScope::~HandleScope which makes sense as any other\nLocal handles should be cleaned up.\n\n`Escape` copies the value of its argument into the enclosing scope, deletes alli\nits local handles, and then gives back the new handle copy which can safely be\nreturned.\n\n### HeapObject\nTODO:\n\n### Local\nHas a single member `val_` which is of type pointer to `T`:\n```c++\ntemplate \u003cclass T\u003e class Local { \n...\n private:\n  T* val_\n}\n```\nNotice that this is a pointer to T. We could create a local using:\n```c++\n  v8::Local\u003cv8::Value\u003e empty_value;\n```\n\nSo a Local contains a pointer to type T. We can access this pointer using\n`operator-\u003e` and `operator*`.\n\nWe can cast from a subtype to a supertype using Local::Cast:\n```c++\nv8::Local\u003cv8::Number\u003e nr = v8::Local\u003cv8::Number\u003e(v8::Number::New(isolate_, 12));\nv8::Local\u003cv8::Value\u003e val = v8::Local\u003cv8::Value\u003e::Cast(nr);\n```\nAnd there is also the \n```c++\nv8::Local\u003cv8::Value\u003e val2 = nr.As\u003cv8::Value\u003e();\n```\n\nSee [local_test.cc](./test/local_test.cc) for an example.\n\n### PrintObject\nUsing _v8_internal_Print_Object from c++:\n```console\n$ nm -C libv8_monolith.a | grep Print_Object\n0000000000000000 T _v8_internal_Print_Object(void*)\n```\nNotice that this function does not have a namespace.\nWe can use this as:\n```c++\nextern void _v8_internal_Print_Object(void* object);\n\n_v8_internal_Print_Object(*((v8::internal::Object**)(*global)));\n```\nLets take a closer look at the above:\n```c++\n  v8::internal::Object** gl = ((v8::internal::Object**)(*global));\n```\nWe use the dereference operator to get the value of a Local (*global), which is\njust of type `T*`, a pointer to the type the Local:\n```c++\ntemplate \u003cclass T\u003e\nclass Local {\n  ...\n private:\n  T* val_;\n}\n```\n\nWe are then casting that to be of type pointer-to-pointer to Object.\n```\n  gl**        Object*         Object\n+-----+      +------+      +-------+\n|     |-----\u003e|      |-----\u003e|       |\n+-----+      +------+      +-------+\n```\nAn instance of `v8::internal::Object` only has a single data member which is a\nfield named `ptr_` of type `Address`:\n\n`src/objects/objects.h`:\n```c++\nclass Object : public TaggedImpl\u003cHeapObjectReferenceType::STRONG, Address\u003e {\n public:\n  constexpr Object() : TaggedImpl(kNullAddress) {}\n  explicit constexpr Object(Address ptr) : TaggedImpl(ptr) {}\n\n#define IS_TYPE_FUNCTION_DECL(Type) \\\n  V8_INLINE bool Is##Type() const;  \\\n  V8_INLINE bool Is##Type(const Isolate* isolate) const;\n  OBJECT_TYPE_LIST(IS_TYPE_FUNCTION_DECL)\n  HEAP_OBJECT_TYPE_LIST(IS_TYPE_FUNCTION_DECL)\n  IS_TYPE_FUNCTION_DECL(HashTableBase)\n  IS_TYPE_FUNCTION_DECL(SmallOrderedHashTable)\n#undef IS_TYPE_FUNCTION_DECL\n  V8_INLINE bool IsNumber(ReadOnlyRoots roots) const;\n}\n```\nLets take a look at one of these functions and see how it is implemented. For\nexample in the OBJECT_TYPE_LIST we have:\n```c++\n#define OBJECT_TYPE_LIST(V) \\\n  V(LayoutDescriptor)       \\\n  V(Primitive)              \\\n  V(Number)                 \\\n  V(Numeric)\n```\nSo the object class will have a function that looks like:\n```c++\ninline bool IsNumber() const;\ninline bool IsNumber(const Isolate* isolate) const;\n```\nAnd in src/objects/objects-inl.h we will have the implementations:\n```c++\nbool Object::IsNumber() const {\n  return IsHeapObject() \u0026\u0026 HeapObject::cast(*this).IsNumber();\n}\n```\n`IsHeapObject` is defined in TaggedImpl:\n```c++\n  constexpr inline bool IsHeapObject() const { return IsStrong(); }\n\n  constexpr inline bool IsStrong() const {\n#if V8_HAS_CXX14_CONSTEXPR\n    DCHECK_IMPLIES(!kCanBeWeak, !IsSmi() == HAS_STRONG_HEAP_OBJECT_TAG(ptr_));\n#endif\n    return kCanBeWeak ? HAS_STRONG_HEAP_OBJECT_TAG(ptr_) : !IsSmi();\n  }\n```\n\nThe macro can be found in src/common/globals.h:\n```c++\n#define HAS_STRONG_HEAP_OBJECT_TAG(value)                          \\\n  (((static_cast\u003ci::Tagged_t\u003e(value) \u0026 ::i::kHeapObjectTagMask) == \\\n    ::i::kHeapObjectTag))\n```\nSo we are casting `ptr_` which is of type Address into type `Tagged_t` which\nis defined in src/common/global.h and can be different depending on if compressed\npointers are used or not. If they are  not supported it is the same as Address:\n``` \nusing Tagged_t = Address;\n```\n\n`src/objects/tagged-impl.h`:\n```c++\ntemplate \u003cHeapObjectReferenceType kRefType, typename StorageType\u003e\nclass TaggedImpl {\n\n  StorageType ptr_;\n}\n```\nThe HeapObjectReferenceType can be either WEAK or STRONG. And the storage type\nis `Address` in this case. So Object itself only has one member that is inherited\nfrom its only super class and this is `ptr_`.\n\nSo the following is telling the compiler to treat the value of our Local,\n`*global`, as a pointer (which it already is) to a pointer that points to\na memory location that adhers to the layout of an `v8::internal::Object` type,\nwhich we know now has a `prt_` member. And we want to dereference it and pass\nit into the function.\n```c++\n_v8_internal_Print_Object(*((v8::internal::Object**)(*global)));\n```\n\n### ObjectTemplate\nBut I'm still missing the connection between ObjectTemplate and object.\nWhen we create it we use:\n```c++\nLocal\u003cObjectTemplate\u003e global = ObjectTemplate::New(isolate);\n```\nIn `src/api/api.cc` we have:\n```c++\nstatic Local\u003cObjectTemplate\u003e ObjectTemplateNew(\n    i::Isolate* isolate, v8::Local\u003cFunctionTemplate\u003e constructor,\n    bool do_not_cache) {\n  i::Handle\u003ci::Struct\u003e struct_obj = isolate-\u003efactory()-\u003eNewStruct(\n      i::OBJECT_TEMPLATE_INFO_TYPE, i::AllocationType::kOld);\n  i::Handle\u003ci::ObjectTemplateInfo\u003e obj = i::Handle\u003ci::ObjectTemplateInfo\u003e::cast(struct_obj);\n  InitializeTemplate(obj, Consts::OBJECT_TEMPLATE);\n  int next_serial_number = 0;\n  if (!constructor.IsEmpty())\n    obj-\u003eset_constructor(*Utils::OpenHandle(*constructor));\n  obj-\u003eset_data(i::Smi::zero());\n  return Utils::ToLocal(obj);\n}\n```\nWhat is a `Struct` in this context?  \n`src/objects/struct.h`\n```c++\n#include \"torque-generated/class-definitions-tq.h\"\n\nclass Struct : public TorqueGeneratedStruct\u003cStruct, HeapObject\u003e {\n public:\n  inline void InitializeBody(int object_size);\n  void BriefPrintDetails(std::ostream\u0026 os);\n  TQ_OBJECT_CONSTRUCTORS(Struct)\n```\nNotice that the include is specifying `torque-generated` include which can be\nfound `out/x64.release_gcc/gen/torque-generated/class-definitions-tq`. So, somewhere\nthere must be an call to the `torque` executable which generates the Code Stub\nAssembler C++ headers and sources before compiling the main source files. There is\nand there is a section about this in `Building V8`.\nThe macro `TQ_OBJECT_CONSTRUCTORS` can be found in `src/objects/object-macros.h`\nand expands to:\n```c++\n  constexpr Struct() = default;\n\n protected:\n  template \u003ctypename TFieldType, int kFieldOffset\u003e\n  friend class TaggedField;\n\n  inline explicit Struct(Address ptr);\n```\n\nSo what does the TorqueGeneratedStruct look like?\n```\ntemplate \u003cclass D, class P\u003e\nclass TorqueGeneratedStruct : public P {\n public:\n```\nWhere D is Struct and P is HeapObject in this case. But the above is the declartion\nof the type but what we have in the .h file is what was generated. \n\nThis type is defined in `src/objects/struct.tq`:\n```\n@abstract                                                                       \n@generatePrint                                                                  \n@generateCppClass                                                               \nextern class Struct extends HeapObject {                                        \n} \n```\n\n`NewStruct` can be found in `src/heap/factory-base.cc`\n```c++\ntemplate \u003ctypename Impl\u003e\nHandleFor\u003cImpl, Struct\u003e FactoryBase\u003cImpl\u003e::NewStruct(\n    InstanceType type, AllocationType allocation) {\n  Map map = Map::GetStructMap(read_only_roots(), type);\n  int size = map.instance_size();\n  HeapObject result = AllocateRawWithImmortalMap(size, allocation, map);\n  HandleFor\u003cImpl, Struct\u003e str = handle(Struct::cast(result), isolate());\n  str-\u003eInitializeBody(size);\n  return str;\n}\n```\nEvery object that is stored on the v8 heap has a Map (`src/objects/map.h`) that\ndescribes the structure of the object being stored.\n```c++\nclass Map : public HeapObject {\n```\n\n```console\n1725\t  return Utils::ToLocal(obj);\n(gdb) p obj\n$6 = {\u003cv8::internal::HandleBase\u003e = {location_ = 0x30b5160}, \u003cNo data fields\u003e}\n```\nSo this is the connection, what we see as a Local\u003cObjectTemplate\u003e is a HandleBase.\nTODO: dig into this some more when I have time.\n\n\n```console\n(lldb) expr gl\n(v8::internal::Object **) $0 = 0x00000000020ee160\n(lldb) memory read -f x -s 8 -c 1 gl\n0x020ee160: 0x00000aee081c0121\n\n(lldb) memory read -f x -s 8 -c 1 *gl\n0xaee081c0121: 0x0200000002080433\n```\n\n\nYou can reload `.lldbinit` using the following command:\n```console\n(lldb) command source ~/.lldbinit\n```\nThis can be useful when debugging a lldb command. You can set a breakpoint\nand break at that location and make updates to the command and reload without\nhaving to restart lldb.\n\nCurrently, the lldb-commands.py that ships with v8 contains an extra operation\nof the parameter pased to `ptr_arg_cmd`:\n```python\ndef ptr_arg_cmd(debugger, name, param, cmd):                                    \n  if not param:                                                                 \n    print(\"'{}' requires an argument\".format(name))                             \n    return                                                                      \n  param = '(void*)({})'.format(param)                                           \n  no_arg_cmd(debugger, cmd.format(param)) \n```\nNotice that `param` is the object that we want to print, for example lets say\nit is a local named obj:\n```\nparam = \"(void*)(obj)\"\n```\nThis will then be \"passed\"/formatted into the command string:\n```\n\"_v8_internal_Print_Object(*(v8::internal::Object**)(*(void*)(obj))\")\n```\n\n#### Threads\nV8 is single threaded (the execution of the functions of the stack) but there\nare supporting threads used for garbage collection, profiling (IC, and perhaps\nother things) (I think).\nLets see what threads there are:\n\n    $ LD_LIBRARY_PATH=../v8_src/v8/out/x64.release_gcc/ lldb ./hello-world \n    (lldb) br s -n main\n    (lldb) r\n    (lldb) thread list\n    thread #1: tid = 0x2efca6, 0x0000000100001e16 hello-world`main(argc=1, argv=0x00007fff5fbfee98) + 38 at hello-world.cc:40, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1\n\nSo at startup there is only one thread which is what we expected. Lets skip ahead to where we create the platform:\n\n    Platform* platform = platform::CreateDefaultPlatform();\n    ...\n    DefaultPlatform* platform = new DefaultPlatform(idle_task_support, tracing_controller);\n    platform-\u003eSetThreadPoolSize(thread_pool_size);\n\n    (lldb) fr v thread_pool_size\n    (int) thread_pool_size = 0\n\nNext there is a check for 0 and the number of processors -1 is used as the size of the thread pool:\n\n    (lldb) fr v thread_pool_size\n    (int) thread_pool_size = 7\n\nThis is all that `SetThreadPoolSize` does. After this we have:\n\n    platform-\u003eEnsureInitialized();\n\n    for (int i = 0; i \u003c thread_pool_size_; ++i)\n      thread_pool_.push_back(new WorkerThread(\u0026queue_));\n\n`new WorkerThread` will create a new pthread (on my system which is MacOSX):\n\n    result = pthread_create(\u0026data_-\u003ethread_, \u0026attr, ThreadEntry, this);\n\nThreadEntry can be found in src/base/platform/platform-posix.\n\n\n### International Component for Unicode (ICU)\nInternational Components for Unicode (ICU) deals with internationalization (i18n).\nICU provides support locale-sensitve string comparisons, date/time/number/currency formatting\netc. \n\nThere is an optional API called ECMAScript 402 which V8 suppports and which is enabled by\ndefault. [i18n-support](https://github.com/v8/v8/wiki/i18n-support) says that even if your application does \nnot use ICU you still need to call InitializeICU :\n\n    V8::InitializeICU();\n\n### Local\n\n```c++\nLocal\u003cString\u003e script_name = ...;\n```\nSo what is script_name. Well it is an object reference that is managed by the v8 GC.\nThe GC needs to be able to move things (pointers around) and also track if\nthings should be GC'd. Local handles as opposed to persistent handles are light\nweight and mostly used local operations. These handles are managed by\nHandleScopes so you must have a handlescope on the stack and the local is only\nvalid as long as the handlescope is valid. This uses Resource Acquisition Is\nInitialization (RAII) so when the HandleScope instance goes out of scope it\nwill remove all the Local instances.\n\nThe `Local` class (in `include/v8.h`) only has one member which is of type\npointer to the type `T`. So for the above example it would be:\n```c++\n  String* val_;\n```\nYou can find the available operations for a Local in `include/v8.h`.\n\n```shell\n(lldb) p script_name.IsEmpty()\n(bool) $12 = false\n````\n\nA Local\u003cT\u003e has overloaded a number of operators, for example -\u003e:\n```shell\n(lldb) p script_name-\u003eLength()\n(int) $14 = 7\n````\nWhere Length is a method on the v8 String class.\n\nThe handle stack is not part of the C++ call stack, but the handle scopes are\nembedded in the C++ stack. Handle scopes can only be stack-allocated, not\nallocated with new.\n\n### Persistent\nhttps://v8.dev/docs/embed:\nPersistent handles provide a reference to a heap-allocated JavaScript Object, \njust like a local handle. There are two flavors, which differ in the lifetime\nmanagement of the reference they handle. Use a persistent handle when you need\nto keep a reference to an object for more than one function call, or when handle\nlifetimes do not correspond to C++ scopes. Google Chrome, for example, uses\npersistent handles to refer to Document Object Model (DOM) nodes.\n\nA persistent handle can be made weak, using PersistentBase::SetWeak, to trigger\na callback from the garbage collector when the only references to an object are\nfrom weak persistent handles.\n\n\nA UniquePersistent\u003cSomeType\u003e handle relies on C++ constructors and destructors\nto manage the lifetime of the underlying object.\nA Persistent\u003cSomeType\u003e can be constructed with its constructor, but must be\nexplicitly cleared with Persistent::Reset.\n\nSo how is a persistent object created?  \nLet's write a test and find out (`test/persistent-object_text.cc`):\n```console\n$ make test/persistent-object_test\n$ ./test/persistent-object_test --gtest_filter=PersistentTest.value\n```\nNow, to create an instance of Persistent we need a Local\u003cT\u003e instance or the\nPersistent instance will just be empty.\n```c++\nLocal\u003cObject\u003e o = Local\u003cObject\u003e::New(isolate_, Object::New(isolate_));\n```\n`Local\u003cObject\u003e::New` can be found in `src/api/api.cc`:\n```c++\nLocal\u003cv8::Object\u003e v8::Object::New(Isolate* isolate) {\n  i::Isolate* i_isolate = reinterpret_cast\u003ci::Isolate*\u003e(isolate);\n  LOG_API(i_isolate, Object, New);\n  ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);\n  i::Handle\u003ci::JSObject\u003e obj =\n      i_isolate-\u003efactory()-\u003eNewJSObject(i_isolate-\u003eobject_function());\n  return Utils::ToLocal(obj);\n}\n```\nThe first thing that happens is that the public Isolate pointer is cast to an\npointer to the internal `Isolate` type.\n`LOG_API` is a macro in the same source file (src/api/api.cc):\n```c++\n#define LOG_API(isolate, class_name, function_name)                           \\\n  i::RuntimeCallTimerScope _runtime_timer(                                    \\\n      isolate, i::RuntimeCallCounterId::kAPI_##class_name##_##function_name); \\\n  LOG(isolate, ApiEntryCall(\"v8::\" #class_name \"::\" #function_name))\n```\nIf our case the preprocessor would expand that to:\n```c++\n  i::RuntimeCallTimerScope _runtime_timer(\n      isolate, i::RuntimeCallCounterId::kAPI_Object_New);\n  LOG(isolate, ApiEntryCall(\"v8::Object::New))\n```\n`LOG` is a macro that can be found in `src/log.h`:\n```c++\n#define LOG(isolate, Call)                              \\\n  do {                                                  \\\n    v8::internal::Logger* logger = (isolate)-\u003elogger(); \\\n    if (logger-\u003eis_logging()) logger-\u003eCall;             \\\n  } while (false)\n```\nAnd this would expand to:\n```c++\n  v8::internal::Logger* logger = isolate-\u003elogger();\n  if (logger-\u003eis_logging()) logger-\u003eApiEntryCall(\"v8::Object::New\");\n```\nSo with the LOG_API macro expanded we have:\n```c++\nLocal\u003cv8::Object\u003e v8::Object::New(Isolate* isolate) {\n  i::Isolate* i_isolate = reinterpret_cast\u003ci::Isolate*\u003e(isolate);\n  i::RuntimeCallTimerScope _runtime_timer( isolate, i::RuntimeCallCounterId::kAPI_Object_New);\n  v8::internal::Logger* logger = isolate-\u003elogger();\n  if (logger-\u003eis_logging()) logger-\u003eApiEntryCall(\"v8::Object::New\");\n\n  ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);\n  i::Handle\u003ci::JSObject\u003e obj =\n      i_isolate-\u003efactory()-\u003eNewJSObject(i_isolate-\u003eobject_function());\n  return Utils::ToLocal(obj);\n}\n```\nNext we have `ENTER_V8_NO_SCRIPT_NO_EXCEPTION`:\n```c++\n#define ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate)                    \\\n  i::VMState\u003cv8::OTHER\u003e __state__((isolate));                       \\\n  i::DisallowJavascriptExecutionDebugOnly __no_script__((isolate)); \\\n  i::DisallowExceptions __no_exceptions__((isolate))\n```\nSo with the macros expanded we have:\n```c++\nLocal\u003cv8::Object\u003e v8::Object::New(Isolate* isolate) {\n  i::Isolate* i_isolate = reinterpret_cast\u003ci::Isolate*\u003e(isolate);\n  i::RuntimeCallTimerScope _runtime_timer( isolate, i::RuntimeCallCounterId::kAPI_Object_New);\n  v8::internal::Logger* logger = isolate-\u003elogger();\n  if (logger-\u003eis_logging()) logger-\u003eApiEntryCall(\"v8::Object::New\");\n\n  i::VMState\u003cv8::OTHER\u003e __state__(i_isolate));\n  i::DisallowJavascriptExecutionDebugOnly __no_script__(i_isolate);\n  i::DisallowExceptions __no_exceptions__(i_isolate));\n\n  i::Handle\u003ci::JSObject\u003e obj =\n      i_isolate-\u003efactory()-\u003eNewJSObject(i_isolate-\u003eobject_function());\n\n  return Utils::ToLocal(obj);\n}\n```\nTODO: Look closer at `VMState`.  \n\nFirst, `i_isolate-\u003eobject_function()` is called and the result passed to\n`NewJSObject`. `object_function` is generated by a macro named \n`NATIVE_CONTEXT_FIELDS`:\n```c++\n#define NATIVE_CONTEXT_FIELD_ACCESSOR(index, type, name)     \\\n  Handle\u003ctype\u003e Isolate::name() {                             \\\n    return Handle\u003ctype\u003e(raw_native_context()-\u003ename(), this); \\\n  }                                                          \\\n  bool Isolate::is_##name(type* value) {                     \\\n    return raw_native_context()-\u003eis_##name(value);           \\\n  }\nNATIVE_CONTEXT_FIELDS(NATIVE_CONTEXT_FIELD_ACCESSOR)\n```\n`NATIVE_CONTEXT_FIELDS` is a macro in `src/contexts` and it c\n```c++\n#define NATIVE_CONTEXT_FIELDS(V)                                               \\\n...                                                                            \\\n  V(OBJECT_FUNCTION_INDEX, JSFunction, object_function)                        \\\n```\n\n```c++\n  Handle\u003ctype\u003e Isolate::object_function() {\n    return Handle\u003cJSFunction\u003e(raw_native_context()-\u003eobject_function(), this);\n  }\n\n  bool Isolate::is_object_function(JSFunction* value) {\n    return raw_native_context()-\u003eis_object_function(value);\n  }\n```\nI'm not clear on the different types of context, there is a native context, a \"normal/public\" context.\nIn `src/contexts-inl.h` we have the native_context function:\n```c++\nContext* Context::native_context() const {\n  Object* result = get(NATIVE_CONTEXT_INDEX);\n  DCHECK(IsBootstrappingOrNativeContext(this-\u003eGetIsolate(), result));\n  return reinterpret_cast\u003cContext*\u003e(result);\n}\n```\n`Context` extends `FixedArray` so the get function is the get function of FixedArray and `NATIVE_CONTEXT_INDEX` \nis the index into the array where the native context is stored.\n\nNow, lets take a closer look at `NewJSObject`. If you search for NewJSObject in `src/heap/factory.cc`:\n```c++\nHandle\u003cJSObject\u003e Factory::NewJSObject(Handle\u003cJSFunction\u003e constructor, PretenureFlag pretenure) {\n  JSFunction::EnsureHasInitialMap(constructor);\n  Handle\u003cMap\u003e map(constructor-\u003einitial_map(), isolate());\n  return NewJSObjectFromMap(map, pretenure);\n}\n```\n`NewJSObjectFromMap` \n```c++\n...\n  HeapObject* obj = AllocateRawWithAllocationSite(map, pretenure, allocation_site);\n```\nSo we have created a new map\n\n### Map\nSo an HeapObject contains a pointer to a Map, or rather has a function that \nreturns a pointer to Map. I can't see any member map in the HeapObject class.\n\nLets take a look at when a map is created.\n```console\n(lldb) br s -f map_test.cc -l 63\n```\n\n```c++\nHandle\u003cMap\u003e Factory::NewMap(InstanceType type,\n                            int instance_size,\n                            ElementsKind elements_kind,\n                            int inobject_properties) {\n  HeapObject* result = isolate()-\u003eheap()-\u003eAllocateRawWithRetryOrFail(Map::kSize, MAP_SPACE);\n  result-\u003eset_map_after_allocation(*meta_map(), SKIP_WRITE_BARRIER);\n  return handle(InitializeMap(Map::cast(result), type, instance_size,\n                              elements_kind, inobject_properties),\n                isolate());\n}\n```\nWe can see that the above is calling `AllocateRawWithRetryOrFail` on the heap \ninstance passing a size of `88` and specifying the `MAP_SPACE`:\n```c++\nHeapObject* Heap::AllocateRawWithRetryOrFail(int size, AllocationSpace space,\n                                             AllocationAlignment alignment) {\n  AllocationResult alloc;\n  HeapObject* result = AllocateRawWithLigthRetry(size, space, alignment);\n  if (result) return result;\n\n  isolate()-\u003ecounters()-\u003egc_last_resort_from_handles()-\u003eIncrement();\n  CollectAllAvailableGarbage(GarbageCollectionReason::kLastResort);\n  {\n    AlwaysAllocateScope scope(isolate());\n    alloc = AllocateRaw(size, space, alignment);\n  }\n  if (alloc.To(\u0026result)) {\n    DCHECK(result != exception());\n    return result;\n  }\n  // TODO(1181417): Fix this.\n  FatalProcessOutOfMemory(\"CALL_AND_RETRY_LAST\");\n  return nullptr;\n}\n```\nThe default value for `alignment` is `kWordAligned`. Reading the docs in the header it says that this function\nwill try to perform an allocation of size `88` in the `MAP_SPACE` and if it fails a full GC will be performed\nand the allocation retried.\nLets take a look at `AllocateRawWithLigthRetry`:\n```c++\n  AllocationResult alloc = AllocateRaw(size, space, alignment);\n```\n`AllocateRaw` can be found in `src/heap/heap-inl.h`. There are different paths that will be taken depending on the\n`space` parameteter. Since it is `MAP_SPACE` in our case we will focus on that path:\n```c++\nAllocationResult Heap::AllocateRaw(int size_in_bytes, AllocationSpace space, AllocationAlignment alignment) {\n  ...\n  HeapObject* object = nullptr;\n  AllocationResult allocation;\n  if (OLD_SPACE == space) {\n  ...\n  } else if (MAP_SPACE == space) {\n    allocation = map_space_-\u003eAllocateRawUnaligned(size_in_bytes);\n  }\n  ...\n}\n```\n`map_space_` is a private member of Heap (src/heap/heap.h):\n```c++\nMapSpace* map_space_;\n```\n`AllocateRawUnaligned` can be found in `src/heap/spaces-inl.h`:\n```c++\nAllocationResult PagedSpace::AllocateRawUnaligned( int size_in_bytes, UpdateSkipList update_skip_list) {\n  if (!EnsureLinearAllocationArea(size_in_bytes)) {\n    return AllocationResult::Retry(identity());\n  }\n\n  HeapObject* object = AllocateLinearly(size_in_bytes);\n  MSAN_ALLOCATED_UNINITIALIZED_MEMORY(object-\u003eaddress(), size_in_bytes);\n  return object;\n}\n```\nThe default value for `update_skip_list` is `UPDATE_SKIP_LIST`.\nSo lets take a look at `AllocateLinearly`:\n```c++\nHeapObject* PagedSpace::AllocateLinearly(int size_in_bytes) {\n  Address current_top = allocation_info_.top();\n  Address new_top = current_top + size_in_bytes;\n  allocation_info_.set_top(new_top);\n  return HeapObject::FromAddress(current_top);\n}\n```\nRecall that `size_in_bytes` in our case is `88`.\n```console\n(lldb) expr current_top\n(v8::internal::Address) $5 = 24847457492680\n(lldb) expr new_top\n(v8::internal::Address) $6 = 24847457492768\n(lldb) expr new_top - current_top\n(unsigned long) $7 = 88\n```\nNotice that first the top is set to the new_top and then the current_top is returned and that will be a pointer\nto the start of the object in memory (which in this case is of v8::internal::Map which is also of type HeapObject).\nI've been wondering why Map (and other HeapObject) don't have any member fields and only/mostly \ngetters/setters for the various fields that make up an object. Well the answer is that pointers to instances of\nfor example Map point to the first memory location of the instance. And the getters/setter functions use indexed\nto read/write to memory locations. The indexes are mostly in the form of enum fields that define the memory layout\nof the type.\n\nNext, in `AllocateRawUnaligned` we have the `MSAN_ALLOCATED_UNINITIALIZED_MEMORY` macro:\n```c++\n  MSAN_ALLOCATED_UNINITIALIZED_MEMORY(object-\u003eaddress(), size_in_bytes);\n```\n`MSAN_ALLOCATED_UNINITIALIZED_MEMORY` can be found in `src/msan.h` and `ms` stands for `Memory Sanitizer` and \nwould only be used if `V8_US_MEMORY_SANITIZER` is defined.\nThe returned `object` will be used to construct an `AllocationResult` when returned.\nBack in `AllocateRaw` we have:\n```c++\nif (allocation.To(\u0026object)) {\n    ...\n    OnAllocationEvent(object, size_in_bytes);\n  }\n\n  return allocation;\n```\nThis will return us in `AllocateRawWithLightRetry`:\n```c++\nAllocationResult alloc = AllocateRaw(size, space, alignment);\nif (alloc.To(\u0026result)) {\n  DCHECK(result != exception());\n  return result;\n}\n```\nThis will return us back in `AllocateRawWithRetryOrFail`:\n```c++\n  HeapObject* result = AllocateRawWithLigthRetry(size, space, alignment);\n  if (result) return result;\n```\nAnd that return will return to `NewMap` in `src/heap/factory.cc`:\n```c++\n  result-\u003eset_map_after_allocation(*meta_map(), SKIP_WRITE_BARRIER);\n  return handle(InitializeMap(Map::cast(result), type, instance_size,\n                              elements_kind, inobject_properties),\n                isolate());\n```\n`InitializeMap`:\n```c++\n  map-\u003eset_instance_type(type);\n  map-\u003eset_prototype(*null_value(), SKIP_WRITE_BARRIER);\n  map-\u003eset_constructor_or_backpointer(*null_value(), SKIP_WRITE_BARRIER);\n  map-\u003eset_instance_size(instance_size);\n  if (map-\u003eIsJSObjectMap()) {\n    DCHECK(!isolate()-\u003eheap()-\u003eInReadOnlySpace(map));\n    map-\u003eSetInObjectPropertiesStartInWords(instance_size / kPointerSize - inobject_properties);\n    DCHECK_EQ(map-\u003eGetInObjectProperties(), inobject_properties);\n    map-\u003eset_prototype_validity_cell(*invalid_prototype_validity_cell());\n  } else {\n    DCHECK_EQ(inobject_properties, 0);\n    map-\u003eset_inobject_properties_start_or_constructor_function_index(0);\n    map-\u003eset_prototype_validity_cell(Smi::FromInt(Map::kPrototypeChainValid));\n  }\n  map-\u003eset_dependent_code(DependentCode::cast(*empty_fixed_array()), SKIP_WRITE_BARRIER);\n  map-\u003eset_weak_cell_cache(Smi::kZero);\n  map-\u003eset_raw_transitions(MaybeObject::FromSmi(Smi::kZero));\n  map-\u003eSetInObjectUnusedPropertyFields(inobject_properties);\n  map-\u003eset_instance_descriptors(*empty_descriptor_array());\n\n  map-\u003eset_visitor_id(Map::GetVisitorId(map));\n  map-\u003eset_bit_field(0);\n  map-\u003eset_bit_field2(Map::IsExtensibleBit::kMask);\n  int bit_field3 = Map::EnumLengthBits::encode(kInvalidEnumCacheSentinel) |\n                   Map::OwnsDescriptorsBit::encode(true) |\n                   Map::ConstructionCounterBits::encode(Map::kNoSlackTracking);\n  map-\u003eset_bit_field3(bit_field3);\n  map-\u003eset_elements_kind(elements_kind); //HOLEY_ELEMENTS\n  map-\u003eset_new_target_is_base(true);\n  isolate()-\u003ecounters()-\u003emaps_created()-\u003eIncrement();\n  if (FLAG_trace_maps) LOG(isolate(), MapCreate(map));\n  return map;\n```\n\nCreating a new map ([map_test.cc](./test/map_test.cc):\n```c++\n  i::Handle\u003ci::Map\u003e map = i::Map::Create(asInternal(isolate_), 10);\n  std::cout \u003c\u003c map-\u003einstance_type() \u003c\u003c '\\n';\n```\n`Map::Create` can be found in objects.cc:\n```c++\nHandle\u003cMap\u003e Map::Create(Isolate* isolate, int inobject_properties) {\n  Handle\u003cMap\u003e copy = Copy(handle(isolate-\u003eobject_function()-\u003einitial_map()), \"MapCreate\");\n```\nSo, the first thing that will happen is `isolate-\u003eobject_function()` will be called. This is function\nthat is generated by the preprocessor.\n\n```c++\n// from src/context.h\n#define NATIVE_CONTEXT_FIELDS(V)                                               \\\n  ...                                                                          \\\n  V(OBJECT_FUNCTION_INDEX, JSFunction, object_function)                        \\\n\n// from src/isolate.h\n#define NATIVE_CONTEXT_FIELD_ACCESSOR(index, type, name)     \\\n  Handle\u003ctype\u003e Isolate::name() {                             \\\n    return Handle\u003ctype\u003e(raw_native_context()-\u003ename(), this); \\\n  }                                                          \\\n  bool Isolate::is_##name(type* value) {                     \\\n    return raw_native_context()-\u003eis_##name(value);           \\\n  }\nNATIVE_CONTEXT_FIELDS(NATIVE_CONTEXT_FIELD_ACCESSOR)\n```\n`object_function()` will become:\n```c++\n  Handle\u003cJSFunction\u003e Isolate::object_function() {\n    return Handle\u003cJSFunction\u003e(raw_native_context()-\u003eobject_function(), this);\n  }\n```\n\nLets look closer at `JSFunction::initial_map()` in in object-inl.h:\n```c++\nMap* JSFunction::initial_map() {\n  return Map::cast(prototype_or_initial_map());\n}\n```\n\n`prototype_or_initial_map` is generated by a macro:\n```c++\nACCESSORS_CHECKED(JSFunction, prototype_or_initial_map, Object,\n                  kPrototypeOrInitialMapOffset, map()-\u003ehas_prototype_slot())\n```\n`ACCESSORS_CHECKED` can be found in `src/objects/object-macros.h`:\n```c++\n#define ACCESSORS_CHECKED(holder, name, type, offset, condition) \\\n  ACCESSORS_CHECKED2(holder, name, type, offset, condition, condition)\n\n#define ACCESSORS_CHECKED2(holder, name, type, offset, get_condition, \\\n                           set_condition)                             \\\n  type* holder::name() const {                                        \\\n    type* value = type::cast(READ_FIELD(this, offset));               \\\n    DCHECK(get_condition);                                            \\\n    return value;                                                     \\\n  }                                                                   \\\n  void holder::set_##name(type* value, WriteBarrierMode mode) {       \\\n    DCHECK(set_condition);                                            \\\n    WRITE_FIELD(this, offset, value);                                 \\\n    CONDITIONAL_WRITE_BARRIER(GetHeap(), this, offset, value, mode);  \\\n  }\n\n#define FIELD_ADDR(p, offset) \\\n  (reinterpret_cast\u003cAddress\u003e(p) + offset - kHeapObjectTag)\n\n#define READ_FIELD(p, offset) \\\n  (*reinterpret_cast\u003cObject* const*\u003e(FIELD_ADDR(p, offset)))\n```\nThe preprocessor will expand `prototype_or_initial_map` to:\n```c++\n  JSFunction* JSFunction::prototype_or_initial_map() const {\n    JSFunction* value = JSFunction::cast(\n        (*reinterpret_cast\u003cObject* const*\u003e(\n            (reinterpret_cast\u003cAddress\u003e(this) + kPrototypeOrInitialMapOffset - kHeapObjectTag))))\n    DCHECK(map()-\u003ehas_prototype_slot());\n    return value;\n  }\n```\nNotice that `map()-\u003ehas_prototype_slot())` will be called first which looks like this:\n```c++\nMap* HeapObject::map() const {\n  return map_word().ToMap();\n}\n```\n__TODO: Add notes about MapWord__  \n```c++\nMapWord HeapObject::map_word() const {\n  return MapWord(\n      reinterpret_cast\u003cuintptr_t\u003e(RELAXED_READ_FIELD(this, kMapOffset)));\n}\n```\nFirst thing that will happen is `RELAXED_READ_FIELD(this, kMapOffset)`\n```c++\n#define RELAXED_READ_FIELD(p, offset)           \\\n  reinterpret_cast\u003cObject*\u003e(base::Relaxed_Load( \\\n      reinterpret_cast\u003cconst base::AtomicWord*\u003e(FIELD_ADDR(p, offset))))\n\n#define FIELD_ADDR(p, offset) \\\n  (reinterpret_cast\u003cAddress\u003e(p) + offset - kHeapObjectTag)\n```\nThis will get expanded by the preprocessor to:\n```c++\n  reinterpret_cast\u003cObject*\u003e(base::Relaxed_Load(\n      reinterpret_cast\u003cconst base::AtomicWord*\u003e(\n          (reinterpret_cast\u003cAddress\u003e(this) + kMapOffset - kHeapObjectTag)))\n```\n`src/base/atomicops_internals_portable.h`:\n```c++\ninline Atomic8 Relaxed_Load(volatile const Atomic8* ptr) {\n  return __atomic_load_n(ptr, __ATOMIC_RELAXED);\n}\n```\nSo this will do an atomoic load of the ptr with the memory order of __ATOMIC_RELELAXED.\n\n\n`ACCESSORS_CHECKED` also generates a `set_prototyp_or_initial_map`:\n```c++\n  void JSFunction::set_prototype_or_initial_map(JSFunction* value, WriteBarrierMode mode) {\n    DCHECK(map()-\u003ehas_prototype_slot());\n    WRITE_FIELD(this, kPrototypeOrInitialMapOffset, value);\n    CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kPrototypeOrInitialMapOffset, value, mode);\n  }\n```\nWhat does `WRITE_FIELD` do?  \n```c++\n#define WRITE_FIELD(p, offset, value)                             \\\n  base::Relaxed_Store(                                            \\\n      reinterpret_cast\u003cbase::AtomicWord*\u003e(FIELD_ADDR(p, offset)), \\\n      reinterpret_cast\u003cbase::AtomicWord\u003e(value));\n```\nWhich would expand into:\n```c++\n  base::Relaxed_Store(                                            \\\n      reinterpret_cast\u003cbase::AtomicWord*\u003e(\n          (reinterpret_cast\u003cAddress\u003e(this) + kPrototypeOrInitialMapOffset - kHeapObjectTag)\n      reinterpret_cast\u003cbase::AtomicWord\u003e(value));\n```\n\nLets take a look at what `instance_type` does:\n```c++\nInstanceType Map::instance_type() const {\n  return static_cast\u003cInstanceType\u003e(READ_UINT16_FIELD(this, kInstanceTypeOffset));\n}\n```\n\nTo see what the above is doing we can do the same thing in the debugger:\nNote that I got `11` below from `map-\u003ekInstanceTypeOffset - i::kHeapObjectTag`\n```console\n(lldb) memory read -f u -c 1 -s 8 `*map + 11`\n0x6d4e6609ed4: 585472345729139745\n(lldb) expr static_cast\u003cInstanceType\u003e(585472345729139745)\n(v8::internal::InstanceType) $34 = JS_OBJECT_TYPE\n```\n\nTake `map-\u003ehas_non_instance_prototype()`:\n```console\n(lldb) br s -n has_non_instance_prototype\n(lldb) expr -i 0 -- map-\u003ehas_non_instance_prototype()\n```\nThe above command will break in `src/objects/map-inl.h`:\n```c++\nBIT_FIELD_ACCESSORS(Map, bit_field, has_non_instance_prototype, Map::HasNonInstancePrototypeBit)\n\n// src/objects/object-macros.h\n#define BIT_FIELD_ACCESSORS(holder, field, name, BitField)      \\\n  typename BitField::FieldType holder::name() const {           \\\n    return BitField::decode(field());                           \\\n  }                                                             \\\n  void holder::set_##name(typename BitField::FieldType value) { \\\n    set_##field(BitField::update(field(), value));              \\\n  }\n```\nThe preprocessor will expand that to:\n```c++\n  typename Map::HasNonInstancePrototypeBit::FieldType Map::has_non_instance_prototype() const {\n    return Map::HasNonInstancePrototypeBit::decode(bit_field());\n  }                                                             \\\n  void holder::set_has_non_instance_prototype(typename BitField::FieldType value) { \\\n    set_bit_field(Map::HasNonInstancePrototypeBit::update(bit_field(), value));              \\\n  }\n```\nSo where can we find `Map::HasNonInstancePrototypeBit`?  \nIt is generated by a macro in `src/objects/map.h`:\n```c++\n// Bit positions for |bit_field|.\n#define MAP_BIT_FIELD_FIELDS(V, _)          \\\n  V(HasNonInstancePrototypeBit, bool, 1, _) \\\n  ...\n  DEFINE_BIT_FIELDS(MAP_BIT_FIELD_FIELDS)\n#undef MAP_BIT_FIELD_FIELDS\n\n#define DEFINE_BIT_FIELDS(LIST_MACRO) \\\n  DEFINE_BIT_RANGES(LIST_MACRO)       \\\n  LIST_MACRO(DEFINE_BIT_FIELD_TYPE, LIST_MACRO##_Ranges)\n\n#define DEFINE_BIT_RANGES(LIST_MACRO)                               \\\n  struct LIST_MACRO##_Ranges {                                      \\\n    enum { LIST_MACRO(DEFINE_BIT_FIELD_RANGE_TYPE, _) kBitsCount }; \\\n  };\n\n#define DEFINE_BIT_FIELD_RANGE_TYPE(Name, Type, Size, _) \\\n  k##Name##Start, k##Name##End = k##Name##Start + Size - 1,\n```\nAlright, lets see what preprocessor expands that to:\n```c++\n  struct MAP_BIT_FIELD_FIELDS_Ranges {\n    enum { \n      kHasNonInstancePrototypeBitStart, \n      kHasNonInstancePrototypeBitEnd = kHasNonInstancePrototypeBitStart + 1 - 1,\n      ... // not showing the rest of the entries.\n      kBitsCount \n    };\n  };\n\n```\nSo this would create a struct with an enum and it could be accessed using:\n`i::Map::MAP_BIT_FIELD_FIELDS_Ranges::kHasNonInstancePrototypeBitStart`\nThe next part of the macro is\n```c++\n  LIST_MACRO(DEFINE_BIT_FIELD_TYPE, LIST_MACRO##_Ranges)\n\n#define DEFINE_BIT_FIELD_TYPE(Name, Type, Size, RangesName) \\\n  typedef BitField\u003cType, RangesName::k##Name##Start, Size\u003e Name;\n```\nWhich will get expanded to:\n```c++\n  typedef BitField\u003cHasNonInstancePrototypeBit, MAP_BIT_FIELD_FIELDS_Ranges::kHasNonInstancePrototypeBitStart, 1\u003e HasNonInstancePrototypeBit;\n```\nSo this is how `HasNonInstancePrototypeBit` is declared and notice that it is of type `BitField` which can be\nfound in `src/utils.h`:\n```c++\ntemplate\u003cclass T, int shift, int size\u003e\nclass BitField : public BitFieldBase\u003cT, shift, size, uint32_t\u003e { };\n\ntemplate\u003cclass T, int shift, int size, class U\u003e\nclass BitFieldBase {\n public:\n  typedef T FieldType;\n```\n\nMap::HasNonInstancePrototypeBit::decode(bit_field());\nfirst bit_field is called:\n```c++\nbyte Map::bit_field() const { return READ_BYTE_FIELD(this, kBitFieldOffset); }\n\n```\nAnd the result of that is passed to `Map::HasNonInstancePrototypeBit::decode`:\n\n```console\n(lldb) br s -n bit_field\n(lldb) expr -i 0 --  map-\u003ebit_field()\n```\n\n```c++\nbyte Map::bit_field() const { return READ_BYTE_FIELD(this, kBitFieldOffset); }\n```\nSo, `this` is the current Map instance, and we are going to read from.\n```c++\n#define READ_BYTE_FIELD(p, offset) \\\n  (*reinterpret_cast\u003cconst byte*\u003e(FIELD_ADDR(p, offset)))\n\n#define FIELD_ADDR(p, offset) \\\n  (reinterpret_cast\u003cAddress\u003e(p) + offset - kHeapObjectTag)\n```\nWhich will get expanded to:\n```c++\nbyte Map::bit_field() const { \n  return *reinterpret_cast\u003cconst byte*\u003e(\n      reinterpret_cast\u003cAddress\u003e(this) + kBitFieldOffset - kHeapObjectTag)\n}\n```\n\nThe instance_size is the instance_size_in_words \u003c\u003c kPointerSizeLog2 (3 on my machine):\n```console\n(lldb) memory read -f x -s 1 -c 1 *map+8\n0x24d1cd509ed1: 0x03\n(lldb) expr 0x03 \u003c\u003c 3\n(int) $2 = 24\n(lldb) expr map-\u003einstance_size()\n(int) $3 = 24\n```\n`i::HeapObject::kHeaderSize` is 8 on my system  which is used in the `DEFINE_FIELD_OFFSET_CONSTANTS:\n```c++\n#define MAP_FIELDS(V)\nV(kInstanceSizeInWordsOffset, kUInt8Size)\nV(kInObjectPropertiesStartOrConstructorFunctionIndexOffset, kUInt8Size)\n...\nDEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize, MAP_FIELDS)\n```\n\nSo we can use this information to read the `inobject_properties_start_or_constructor_function_index` directly from memory using:\n```console\n(lldb) expr map-\u003einobject_properties_start_or_constructor_function_index()\n(lldb) memory read -f x -s 1 -c 1 map+9\nerror: invalid start address expression.\nerror: address expression \"map+9\" evaluation failed\n(lldb) memory read -f x -s 1 -c 1 *map+9\n0x17b027209ed2: 0x03\n```\n\nInspect the visitor_id (which is the last of the first byte):\n```console\nlldb) memory read -f x -s 1 -c 1 *map+10\n0x17b027209ed3: 0x15\n(lldb) expr (int) 0x15\n(int) $8 = 21\n(lldb) expr map-\u003evisitor_id()\n(v8::internal::VisitorId) $11 = kVisitJSObjectFast\n(lldb) expr (int) $11\n(int) $12 = 21\n```\n\nInspect the instance_type (which is part of the second byte):\n```console\n(lldb) expr map-\u003einstance_type()\n(v8::internal::InstanceType) $41 = JS_OBJECT_TYPE\n(lldb) expr v8::internal::InstanceType::JS_OBJECT_TYPE\n(uint16_t) $35 = 1057\n(lldb) memory read -f x -s 2 -c 1 *map+11\n0x17b027209ed4: 0x0421\n(lldb) expr (int)0x0421\n(int) $40 = 1057\n```\nNotice that `instance_type` is a short so that will take up 2 bytes\n```console\n(lldb) expr map-\u003ehas_non_instance_prototype()\n(bool) $60 = false\n(lldb) expr map-\u003eis_callable()\n(bool) $46 = false\n(lldb) expr map-\u003ehas_named_interceptor()\n(bool) $51 = false\n(lldb) expr map-\u003ehas_indexed_interceptor()\n(bool) $55 = false\n(lldb) expr map-\u003eis_undetectable()\n(bool) $56 = false\n(lldb) expr map-\u003eis_access_check_needed()\n(bool) $57 = false\n(lldb) expr map-\u003eis_constructor()\n(bool) $58 = false\n(lldb) expr map-\u003ehas_prototype_slot()\n(bool) $59 = false\n```\n\nVerify that the above is correct:\n```console\n(lldb) expr map-\u003ehas_non_instance_prototype()\n(bool) $44 = false\n(lldb) memory read -f x -s 1 -c 1 *map+13\n0x17b027209ed6: 0x00\n\n(lldb) expr map-\u003eset_has_non_instance_prototype(true)\n(lldb) memory read -f x -s 1 -c 1 *map+13\n0x17b027209ed6: 0x01\n\n(lldb) expr map-\u003eset_has_prototype_slot(true)\n(lldb) memory read -f x -s 1 -c 1 *map+13\n0x17b027209ed6: 0x81\n```\n\n\nInspect second int field (bit_field2):\n```console\n(lldb) memory read -f x -s 1 -c 1 *map+14\n0x17b027209ed7: 0x19\n(lldb) expr map-\u003eis_extensible()\n(bool) $78 = true\n(lldb) expr -- 0x19 \u0026 (1 \u003c\u003c 0)\n(bool) $90 = 1\n\n(lldb) expr map-\u003eis_prototype_map()\n(bool) $79 = false\n\n(lldb) expr map-\u003eis_in_retained_map_list()\n(bool) $80 = false\n\n(lldb) expr map-\u003eelements_kind()\n(v8::internal::ElementsKind) $81 = HOLEY_ELEMENTS\n(lldb) expr v8::internal::ElementsKind::HOLEY_ELEMENTS\n(int) $133 = 3\n(lldb) expr  0x19 \u003e\u003e 3\n(int) $134 = 3\n```\n\nInspect third int field (bit_field3):\n```console\n(lldb) memory read -f b -s 4 -c 1 *map+15\n0x17b027209ed8: 0b00001000001000000000001111111111\n(lldb) memory read -f x -s 4 -c 1 *map+15\n0x17b027209ed8: 0x082003ff\n```\n\nSo we know that a Map instance is a pointer allocated by the Heap and with a specific \nsize. Fields are accessed using indexes (remember there are no member fields in the Map class).\nWe also know that all HeapObject have a Map. The Map is sometimes referred to as the HiddenClass\nand sometimes the shape of an object. If two objects have the same properties they would share \nthe same Map. This makes sense and I've see blog post that show this but I'd like to verify\nthis to fully understand it.\nI'm going to try to match https://v8project.blogspot.com/2017/08/fast-properties.html with \nthe code.\n\nSo, lets take a look at adding a property to a JSObject. We start by creating a new Map and then \nuse it to create a new JSObject:\n```c++\n  i::Handle\u003ci::Map\u003e map = factory-\u003eNewMap(i::JS_OBJECT_TYPE, 32);\n  i::Handle\u003ci::JSObject\u003e js_object = factory-\u003eNewJSObjectFromMap(map);\n\n  i::Handle\u003ci::String\u003e prop_name = factory-\u003eInternalizeUtf8String(\"prop_name\");\n  i::Handle\u003ci::String\u003e prop_value = factory-\u003eInternalizeUtf8String(\"prop_value\");\n  i::JSObject::AddProperty(js_object, prop_name, prop_value, i::NONE);  \n```\nLets take a closer look at `AddProperty` and how it interacts with the Map. This function can be\nfound in `src/objects.cc`:\n```c++\nvoid JSObject::AddProperty(Handle\u003cJSObject\u003e object, Handle\u003cName\u003e name,\n                           Handle\u003cObject\u003e value,\n                           PropertyAttributes attributes) {\n  LookupIterator it(object, name, object, LookupIterator::OWN_SKIP_INTERCEPTOR);\n  CHECK_NE(LookupIterator::ACCESS_CHECK, it.state());\n```\nFirst we have the LookupIterator constructor (`src/lookup.h`) but since this is a new property which \nwe know does not exist it will not find any property.\n```c++\nCHECK(AddDataProperty(\u0026it, value, attributes, kThrowOnError,\n                        CERTAINLY_NOT_STORE_FROM_KEYED)\n            .IsJust());\n```\n```c++\n  Handle\u003cJSReceiver\u003e receiver = it-\u003eGetStoreTarget\u003cJSReceiver\u003e();\n  ...\n  it-\u003eUpdateProtector();\n  // Migrate to the most up-to-date map that will be able to store |value|\n  // under it-\u003ename() with |attributes|.\n  it-\u003ePrepareTransitionToDataProperty(receiver, value, attributes, store_mode);\n  DCHECK_EQ(LookupIterator::TRANSITION, it-\u003estate());\n  it-\u003eApplyTransitionToDataProperty(receiver);\n\n  // Write the property value.\n  it-\u003eWriteDataValue(value, true);\n```\n`PrepareTransitionToDataProperty`:\n```c++\n  Representation representation = value-\u003eOptimalRepresentation();\n  Handle\u003cFieldType\u003e type = value-\u003eOptimalType(isolate, representation);\n  maybe_map = Map::CopyWithField(map, name, type, attributes, constness,\n  representation, flag);\n```\n`Map::CopyWithField`:\n```c++\n  Descriptor d = Descriptor::DataField(name, index, attributes, constness, representation, wrapped_type);\n```\nLets take a closer look the Decriptor which can be found in `src/property.cc`:\n```c++\nDescriptor Descriptor::DataField(Handle\u003cName\u003e key, int field_index,\n                                 PropertyAttributes attributes,\n                                 PropertyConstness constness,\n                                 Representation representation,\n                                 MaybeObjectHandle wrapped_field_type) {\n  DCHECK(wrapped_field_type-\u003eIsSmi() || wrapped_field_type-\u003eIsWeakHeapObject());\n  PropertyDetails details(kData, attributes, kField, constness, representation,\n                          field_index);\n  return Descriptor(key, wrapped_field_type, details);\n}\n```\n`Descriptor` is declared in `src/property.h` and describes the elements in a instance-descriptor array. These\nare returned when calling `map-\u003einstance_descriptors()`. Let check some of the arguments:\n```console\n(lldb) job *key\n#prop_name\n(lldb) expr attributes\n(v8::internal::PropertyAttributes) $27 = NONE\n(lldb) expr constness\n(v8::internal::PropertyConstness) $28 = kMutable\n(lldb) expr representation\n(v8::internal::Representation) $29 = (kind_ = '\\b')\n```\nThe Descriptor class contains three members:\n```c++\n private:\n  Handle\u003cName\u003e key_;\n  MaybeObjectHandle value_;\n  PropertyDetails details_;\n```\nLets take a closer look `PropertyDetails` which only has a single member named `value_`\n```c++\n  uint32_t value_;\n```\nIt also declares a number of classes the extend BitField, for example:\n```c++\nclass KindField : public BitField\u003cPropertyKind, 0, 1\u003e {};\nclass LocationField : public BitField\u003cPropertyLocation, KindField::kNext, 1\u003e {};\nclass ConstnessField : public BitField\u003cPropertyConstness, LocationField::kNext, 1\u003e {};\nclass AttributesField : public BitField\u003cPropertyAttributes, ConstnessField::kNext, 3\u003e {};\nclass PropertyCellTypeField : public BitField\u003cPropertyCellType, AttributesField::kNext, 2\u003e {};\nclass DictionaryStorageField : public BitField\u003cuint32_t, PropertyCellTypeField::kNext, 23\u003e {};\n\n// Bit fields for fast objects.\nclass RepresentationField : public BitField\u003cuint32_t, AttributesField::kNext, 4\u003e {};\nclass DescriptorPointer : public BitField\u003cuint32_t, RepresentationField::kNext, kDescriptorIndexBitCount\u003e {};\nclass FieldIndexField : public BitField\u003cuint32_t, DescriptorPointer::kNext, kDescriptorIndexBitCount\u003e {\n\nenum PropertyKind { kData = 0, kAccessor = 1 };\nenum PropertyLocation { kField = 0, kDescriptor = 1 };\nenum class PropertyConstness { kMutable = 0, kConst = 1 };\nenum PropertyAttributes {\n  NONE = ::v8::None,\n  READ_ONLY = ::v8::ReadOnly,\n  DONT_ENUM = ::v8::DontEnum,\n  DONT_DELETE = ::v8::DontDelete,\n  ALL_ATTRIBUTES_MASK = READ_ONLY | DONT_ENUM | DONT_DELETE,\n  SEALED = DONT_DELETE,\n  FROZEN = SEALED | READ_ONLY,\n  ABSENT = 64,  // Used in runtime to indicate a property is absent.\n  // ABSENT can never be stored in or returned from a descriptor's attributes\n  // bitfield.  It is only used as a return value meaning the attributes of\n  // a non-existent property.\n};\nenum class PropertyCellType {\n  // Meaningful when a property cell does not contain the hole.\n  kUndefined,     // The PREMONOMORPHIC of property cells.\n  kConstant,      // Cell has been assigned only once.\n  kConstantType,  // Cell has been assigned only one type.\n  kMutable,       // Cell will no longer be tracked as constant.\n  // Meaningful when a property cell contains the hole.\n  kUninitialized = kUndefined,  // Cell has never been initialized.\n  kInvalidated = kConstant,     // Cell has been deleted, invalidated or never\n                                // existed.\n  // For dictionaries not holding cells.\n  kNoCell = kMutable,\n};\n\n\ntemplate\u003cclass T, int shift, int size\u003e\nclass BitField : public BitFieldBase\u003cT, shift, size, uint32_t\u003e { };\n```\nThe Type T of KindField will be `PropertyKind`, the `shift` will be 0 , and the `size` 1.\nNotice that `LocationField` is using `KindField::kNext` as its shift. This is a static class constant\nof type `uint32_t` and is defined as:\n```c++\nstatic const U kNext = kShift + kSize;\n```\nSo `LocationField` would get the value from KindField which should be:\n```c++\nclass LocationField : public BitField\u003cPropertyLocation, 1, 1\u003e {};\n```\n\nThe constructor for PropertyDetails looks like this:\n```c++\nPropertyDetails(PropertyKind kind, PropertyAttributes attributes, PropertyCellType cell_type, int dictionary_index = 0) {\n    value_ = KindField::encode(kind) | LocationField::encode(kField) |\n             AttributesField::encode(attributes) |\n             DictionaryStorageField::encode(dictionary_index) |\n             PropertyCellTypeField::encode(cell_type);\n  }\n```\nSo what does KindField::encode(kind) actualy do then?\n```console\n(lldb) expr static_cast\u003cuint32_t\u003e(kind())\n(uint32_t) $36 = 0\n(lldb) expr static_cast\u003cuint32_t\u003e(kind()) \u003c\u003c 0\n(uint32_t) $37 = 0\n```\nThis value is later returned by calling `kind()`:\n```c++\nPropertyKind kind() const { return KindField::decode(value_); }\n```\nSo we have all this information about this property, its type (Representation), constness, if it is \nread-only, enumerable, deletable, sealed, frozen. After that little detour we are back in `Descriptor::DataField`:\n```c++\n  return Descriptor(key, wrapped_field_type, details);\n```\nHere we are using the key (name of the property), the wrapped_field_type, and PropertyDetails we created.\nWhat is `wrapped_field_type` again?  \nIf we back up a few frames back into `Map::TransitionToDataProperty` we can see that the type passed in\nis taken from the following code:\n```c++\n  Representation representation = value-\u003eOptimalRepresentation();\n  Handle\u003cFieldType\u003e type = value-\u003eOptimalType(isolate, representation);\n```\nSo this is only taking the type of the field:\n```console\n(lldb) expr representation.kind()\n(v8::internal::Representation::Kind) $51 = kHeapObject\n```\nThis makes sense as the map only deals with the shape of the propery and not the value.\nNext in `Map::CopyWithField` we have:\n```c++\n  Handle\u003cMap\u003e new_map = Map::CopyAddDescriptor(map, \u0026d, flag);\n```\n`CopyAddDescriptor` does:\n```c++\n  Handle\u003cDescriptorArray\u003e descriptors(map-\u003einstance_descriptors());\n \n  int nof = map-\u003eNumberOfOwnDescriptors();\n  Handle\u003cDescriptorArray\u003e new_descriptors = DescriptorArray::CopyUpTo(descriptors, nof, 1);\n  new_descriptors-\u003eAppend(descriptor);\n  \n  Handle\u003cLayoutDescriptor\u003e new_layout_descriptor =\n      FLAG_unbox_double_fields\n          ? LayoutDescriptor::New(map, new_descriptors, nof + 1)\n          : handle(LayoutDescriptor::FastPointerLayout(), map-\u003eGetIsolate());\n\n  return CopyReplaceDescriptors(map, new_descriptors, new_layout_descriptor,\n                                flag, descriptor-\u003eGetKey(), \"CopyAddDescriptor\",\n                                SIMPLE_PROPERTY_TRANSITION);\n```\nLets take a closer look at `LayoutDescriptor`\n\n```console\n(lldb) expr new_layout_descriptor-\u003ePrint()\nLayout descriptor: \u003call tagged\u003e\n```\nTODO: Take a closer look at LayoutDescritpor\n\nLater when actually adding the value in `Object::AddDataProperty`:\n```c++\n  it-\u003eWriteDataValue(value, true);\n```\nThis call will end up in `src/lookup.cc` and in our case the path will be the following call:\n```c++\n  JSObject::cast(*holder)-\u003eWriteToField(descriptor_number(), property_details_, *value);\n```\nTODO: Take a closer look at LookupIterator.\n`WriteToField` can be found in `src/objects-inl.h`:\n```c++\n  FieldIndex index = FieldIndex::ForDescriptor(map(), descriptor);\n```\n`FieldIndex::ForDescriptor` can be found in `src/field-index-inl.h`:\n```c++\ninline FieldIndex FieldIndex::ForDescriptor(const Map* map, int descriptor_index) {\n  PropertyDetails details = map-\u003einstance_descriptors()-\u003eGetDetails(descriptor_index);\n  int field_index = details.field_index();\n  return ForPropertyIndex(map, field_index, details.representation());\n}\n```\nNotice that this is calling `instance_descriptors()` on the passed-in map. This as we recall from earlier returns\nand DescriptorArray (which is a type of WeakFixedArray). A Descriptor array \n\nOur DecsriptorArray only has one entry:\n```console\n(lldb) expr map-\u003einstance_descriptors()-\u003enumber_of_descriptors()\n(int) $6 = 1\n(lldb) expr map-\u003einstance_descriptors()-\u003eGetKey(0)-\u003ePrint()\n#prop_name\n(lldb) expr map-\u003einstance_descriptors()-\u003eGetFieldIndex(0)\n(int) $11 = 0\n```\nWe can also use `Print` on the DescriptorArray:\n```console\nlldb) expr map-\u003einstance_descriptors()-\u003ePrint()\n\n  [0]: #prop_name (data field 0:h, p: 0, attrs: [WEC]) @ Any\n```\nIn our case we are accessing the PropertyDetails and then getting the `field_index` which I think tells us\nwhere in the object the value for this property is stored.\nThe last call in `ForDescriptor` is `ForProperty:\n```c++\ninline FieldIndex FieldIndex::ForPropertyIndex(const Map* map,\n                                               int property_index,\n                                               Representation representation) {\n  int inobject_properties = map-\u003eGetInObjectProperties();\n  bool is_inobject = property_index \u003c inobject_properties;\n  int first_inobject_offset;\n  int offset;\n  if (is_inobject) {\n    first_inobject_offset = map-\u003eGetInObjectPropertyOffset(0);\n    offset = map-\u003eGetInObjectPropertyOffset(property_index);\n  } else {\n    first_inobject_offset = FixedArray::kHeaderSize;\n    property_index -= inobject_properties;\n    offset = FixedArray::kHeaderSize + property_index * kPointerSize;\n  }\n  Encoding encoding = FieldEncoding(representation);\n  return FieldIndex(is_inobject, offset, encoding, inobject_properties,\n                    first_inobject_offset);\n}\n```\nI was expecting `inobject_propertis` to be 1 here but it is 0:\n```console\n(lldb) expr inobject_properties\n(int) $14 = 0\n```\nWhy is that, what am I missing?  \nThese in-object properties are stored directly on the object instance and not do not use\nthe properties array. All get back to an example of this later to clarify this.\nTODO: Add in-object properties example.\n\nBack in `JSObject::WriteToField`:\n```c++\n  RawFastPropertyAtPut(index, value);\n```\n\n```c++\nvoid JSObject::RawFastPropertyAtPut(FieldIndex index, Object* value) {\n  if (index.is_inobject()) {\n    int offset = index.offset();\n    WRITE_FIELD(this, offset, value);\n    WRITE_BARRIER(GetHeap(), this, offset, value);\n  } else {\n    property_array()-\u003eset(index.outobject_array_index(), value);\n  }\n}\n```\nIn our case we know that the index is not inobject()\n```console\n(lldb) expr index.is_inobject()\n(bool) $18 = false\n```\nSo, `property_array()-\u003eset()` will be called.\n```console\n(lldb) expr this\n(v8::internal::JSObject *) $21 = 0x00002c31c6a88b59\n```\nJSObject inherits from JSReceiver which is where the property_array() function is declared.\n```c++\n  inline PropertyArray* property_array() const;\n```\n```console\n(lldb) expr property_array()-\u003ePrint()\n0x2c31c6a88bb1: [PropertyArray]\n - map: 0x2c31f5603e21 \u003cMap\u003e\n - length: 3\n - hash: 0\n           0: 0x2c31f56025a1 \u003cOdd Oddball: uninitialized\u003e\n         1-2: 0x2c31f56026f1 \u003cundefined\u003e\n(lldb) expr index.outobject_array_index()\n(int) $26 = 0\n(lldb) expr value-\u003ePrint()\n#prop_value\n```\nLooking at the above values printed we should see the property be written to entry 0.\n```console\n(lldb) expr property_array()-\u003eget(0)-\u003ePrint()\n#uninitialized\n// after call to set\n(lldb) expr property_array()-\u003eget(0)-\u003ePrint()\n#prop_value\n```\n\n```console\n(lldb) expr map-\u003einstance_descriptors()\n(v8::internal::DescriptorArray *) $4 = 0x000039a927082339\n```\nSo a map has an pointer array of instance of DescriptorArray\n\n```console\n(lldb) expr map-\u003eGetInObjectProperties()\n(int) $19 = 1\n```\nEach Map has int that tells us the number of properties it has. This is the number specified when creating\na new Map, for example:\n```console\ni::Handle\u003ci::Map\u003e map = i::Map::Create(asInternal(isolate_), 1);\n```\nBut at this stage we don't really have any properties. The value for a property is associated with the actual\ninstance of the Object. What the Map specifies is index of the value for a particualar property. \n\n#### Creating a Map instance\nLets take a look at when a map is created.\n```console\n(lldb) br s -f map_test.cc -l 63\n```\n\n```c++\nHandle\u003cMap\u003e Factory::NewMap(InstanceType type,\n                            int instance_size,\n                            ElementsKind elements_kind,\n                            int inobject_properties) {\n  HeapObject* result = isolate()-\u003eheap()-\u003eAllocateRawWithRetryOrFail(Map::kSize, MAP_SPACE);\n  result-\u003eset_map_after_allocation(*meta_map(), SKIP_WRITE_BARRIER);\n  return handle(InitializeMap(Map::cast(result), type, instance_size,\n                              elements_kind, inobject_properties),\n                isolate());\n}\n```\nWe can see that the above is calling `AllocateRawWithRetryOrFail` on the heap instance passing a size of `88` and\nspecifying the `MAP_SPACE`:\n```c++\nHeapObject* Heap::AllocateRawWithRetryOrFail(int size, AllocationSpace space,\n                                             AllocationAlignment alignment) {\n  AllocationResult alloc;\n  HeapObject* result = AllocateRawWithLigthRetry(size, space, alignment);\n  if (result) return result;\n\n  isolate()-\u003ecounters()-\u003egc_last_resort_from_handles()-\u003eIncrement();\n  CollectAllAvailableGarbage(GarbageCollectionReason::kLastResort);\n  {\n    AlwaysAllocateScope scope(isolate());\n    alloc = AllocateRaw(size, space, alignment);\n  }\n  if (alloc.To(\u0026result)) {\n    DCHECK(result != exception());\n    return result;\n  }\n  // TODO(1181417): Fix this.\n  FatalProcessOutOfMemory(\"CALL_AND_RETRY_LAST\");\n  return nullptr;\n}\n```\nThe default value for `alignment` is `kWordAligned`. Reading the docs in the header it says that this function\nwill try to perform an allocation of size `88` in the `MAP_SPACE` and if it fails a full GC will be performed\nand the allocation retried.\nLets take a look at `AllocateRawWithLigthRetry`:\n```c++\n  AllocationResult alloc = AllocateRaw(size, space, alignment);\n```\n`AllocateRaw` can be found in `src/heap/heap-inl.h`. There are different paths that will be taken depending on the\n`space` parameteter. Since it is `MAP_SPACE` in our case we will focus on that path:\n```c++\nAllocationResult Heap::AllocateRaw(int size_in_bytes, AllocationSpace space, AllocationAlignment alignment) {\n  ...\n  HeapObject* object = nullptr;\n  AllocationResult allocation;\n  if (OLD_SPACE == space) {\n  ...\n  } else if (MAP_SPACE == space) {\n    allocation = map_space_-\u003eAllocateRawUnaligned(size_in_bytes);\n  }\n  ...\n}\n```\n`map_space_` is a private member of Heap (src/heap/heap.h):\n```c++\nMapSpace* map_space_;\n```\n`AllocateRawUnaligned` can be found in `src/heap/spaces-inl.h`:\n```c++\nAllocationResult PagedSpace::AllocateRawUnaligned( int size_in_bytes, UpdateSkipList update_skip_list) {\n  if (!EnsureLinearAllocationArea(size_in_bytes)) {\n    return AllocationResult::Retry(identity());\n  }\n\n  HeapObject* object = AllocateLinearly(size_in_bytes);\n  MSAN_ALLOCATED_UNINITIALIZED_MEMORY(object-\u003eaddress(), size_in_bytes);\n  return object;\n}\n```\nThe default value for `update_skip_list` is `UPDATE_SKIP_LIST`.\nSo lets take a look at `AllocateLinearly`:\n```c++\nHeapObject* PagedSpace::AllocateLinearly(int size_in_bytes) {\n  Address current_top = allocation_info_.top();\n  Address new_top = current_top + size_in_bytes;\n  allocation_info_.set_top(new_top);\n  return HeapObject::FromAddress(current_top);\n}\n```\nRecall that `size_in_bytes` in our case is `88`.\n```console\n(lldb) expr current_top\n(v8::internal::Address) $5 = 24847457492680\n(lldb) expr new_top\n(v8::internal::Address) $6 = 24847457492768\n(lldb) expr new_top - current_top\n(unsigned long) $7 = 88\n```\nNotice that first the top is set to the new_top and then the current_top is returned and that will be a pointer\nto the start of the object in memory (which in this case is of v8::internal::Map which is also of type HeapObject).\nI've been wondering why Map (and other HeapObject) don't have any member fields and only/mostly \ngetters/setters for the various fields that make up an object. Well the answer is that pointers to instances of\nfor example Map point to the first memory location of the instance. And the getters/setter functions use indexed\nto read/write to memory locations. The indexes are mostly in the form of enum fields that define the memory layout\nof the type.\n\nNext, in `AllocateRawUnaligned` we have the `MSAN_ALLOCATED_UNINITIALIZED_MEMORY` macro:\n```c++\n  MSAN_ALLOCATED_UNINITIALIZED_MEMORY(object-\u003eaddress(), size_in_bytes);\n```\n`MSAN_ALLOCATED_UNINITIALIZED_MEMORY` can be found in `src/msan.h` and `ms` stands for `Memory Sanitizer` and \nwould only be used if `V8_US_MEMORY_SANITIZER` is defined.\nThe returned `object` will be used to construct an `AllocationResult` when returned.\nBack in `AllocateRaw` we have:\n```c++\nif (allocation.To(\u0026object)) {\n    ...\n    OnAllocationEvent(object, size_in_bytes);\n  }\n\n  return allocation;\n```\nThis will return us in `AllocateRawWithLightRetry`:\n```c++\nAllocationResult alloc = AllocateRaw(size, space, alignment);\nif (alloc.To(\u0026result)) {\n  DCHECK(result != exception());\n  return result;\n}\n```\nThis will return us back in `AllocateRawWithRetryOrFail`:\n```c++\n  HeapObject* result = AllocateRawWithLigthRetry(size, space, alignment);\n  if (result) return result;\n```\nAnd that return will return to `NewMap` in `src/heap/factory.cc`:\n```c++\n  result-\u003eset_map_after_allocation(*meta_map(), SKIP_WRITE_BARRIER);\n  return handle(InitializeMap(Map::cast(result), type, instance_size,\n                              elements_kind, inobject_properties),\n                isolate());\n```\n`InitializeMap`:\n```c++\n  map-\u003eset_instance_type(type);\n  map-\u003eset_prototype(*null_value(), SKIP_WRITE_BARRIER);\n  map-\u003eset_constructor_or_backpointer(*null_value(), SKIP_WRITE_BARRIER);\n  map-\u003eset_instance_size(instance_size);\n  if (map-\u003eIsJSObjectMap()) {\n    DCHECK(!isolate()-\u003eheap()-\u003eInReadOnlySpace(map));\n    map-\u003eSetInObjectPropertiesStartInWords(instance_size / kPointerSize - inobject_properties);\n    DCHECK_EQ(map-\u003eGetInObjectProperties(), inobject_properties);\n    map-\u003eset_prototype_validity_cell(*invalid_prototype_validity_cell());\n  } else {\n    DCHECK_EQ(inobject_properties, 0);\n    map-\u003eset_inobject_properties_start_or_constructor_function_index(0);\n    map-\u003eset_prototype_validity_cell(Smi::FromInt(Map::kPrototypeChainValid));\n  }\n  map-\u003eset_dependent_code(DependentCode::cast(*empty_fixed_array()), SKIP_WRITE_BARRIER);\n  map-\u003eset_weak_cell_cache(Smi::kZero);\n  map-\u003eset_raw_transitions(MaybeObject::FromSmi(Smi::kZero));\n  map-\u003eSetInObjectUnusedPropertyFields(inobject_properties);\n  map-\u003eset_instance_descriptors(*empty_descriptor_array());\n\n  map-\u003eset_visitor_id(Map::GetVisitorId(map));\n  map-\u003eset_bit_field(0);\n  map-\u003eset_bit_field2(Map::IsExtensibleBit::kMask);\n  int bit_field3 = Map::EnumLengthBits::encode(kInvalidEnumCacheSentinel) |\n                   Map::OwnsDescriptorsBit::encode(true) |\n                   Map::ConstructionCounterBits::encode(Map::kNoSlackTracking);\n  map-\u003eset_bit_field3(bit_field3);\n  map-\u003eset_elements_kind(elements_kind); //HOLEY_ELEMENTS\n  map-\u003eset_new_target_is_base(true);\n  isolate()-\u003ecounters()-\u003emaps_created()-\u003eIncrement();\n  if (FLAG_trace_maps) LOG(isolate(), MapCreate(map));\n  return map;\n```\n\n### Context\nContext extends `FixedArray` (`src/context.h`). So an instance of this Context is a FixedArray and we can \nuse Get(index) etc to get entries in the array.\n\n### V8_EXPORT\nThis can be found in quite a few places in v8 source code. For example:\n\n    class V8_EXPORT ArrayBuffer : public Object {\n\nWhat is this?  \nIt is a preprocessor macro which looks like this:\n\n    #if V8_HAS_ATTRIBUTE_VISIBILITY \u0026\u0026 defined(V8_SHARED)\n    # ifdef BUILDING_V8_SHARED\n    #  define V8_EXPORT __attribute__ ((visibility(\"default\")))\n    # else\n    #  define V8_EXPORT\n    # endif\n    #else\n    # define V8_EXPORT\n    #endif \n\nSo we can see that if `V8_HAS_ATTRIBUTE_VISIBILITY`, and `defined(V8_SHARED)`, and also \nif `BUILDING_V8_SHARED`, `V8_EXPORT` is set to `__attribute__ ((visibility(\"default\"))`.\nBut in all other cases `V8_EXPORT` is empty and the preprocessor does not insert \nanything (nothing will be there come compile time). \nBut what about the `__attribute__ ((visibility(\"default\"))` what is this?  \n\nIn the GNU compiler collection (GCC) environment, the term that is used for\nexporting is visibility. As it applies to functions and variables in a shared\nobject, visibility refers to the ability of other shared objects to call a\nC/C++ function. Functions with default visibility have a global scope and can\nbe called from other shared objects. Functions with hidden visibility have a\nlocal scope and cannot be called from other shared objects.\n\nVisibility can be controlled by using either compiler options or visibility attributes.\nIn your header files, wherever you want an interface or API made public outside\nthe current Dynamic Shared Object (DSO) , place\n`__attribute__ ((visibility (\"default\")))` in struct, class and function\ndeclarations you wish to make public.  With `-fvisibility=hidden`, you are\ntelling GCC that every declaration not explicitly marked with a visibility\nattribute has a hidden visibility. There is such a flag in build/common.gypi\n\n\n### ToLocalChecked()\nYou'll see a few of these calls in the hello_world example:\n```c++\n  Local\u003cString\u003e source = String::NewFromUtf8(isolate, js, NewStringType::kNormal).ToLocalChecked();\n```\n\nNewFromUtf8 actually returns a Local\u003cString\u003e wrapped in a MaybeLocal which forces a check to see if \nthe Local\u003c\u003e is empty before using it. \nNewStringType is an enum which can be kNormalString (k for constant) or kInternalized.\n\nThe following is after running the preprocessor (clang -E src/api.cc):\n\n    # 5961 \"src/api.cc\"\n    Local\u003cString\u003e String::NewFromUtf8(Isolate* isolate,\n                                  const char* data,\n                                  NewStringType type,\n                                  int length) {\n      MaybeLocal\u003cString\u003e result; \n      if (length == 0) { \n        result = String::Empty(isolate); \n      } else if (length \u003e i::String::kMaxLength) { \n        result = MaybeLocal\u003cString\u003e(); \n      } else { \n        i::Isolate* i_isolate = reinterpret_cast\u003cinternal::Isolate*\u003e(isolate); \n        i::VMState\u003cv8::OTHER\u003e __state__((i_isolate)); \n        i::RuntimeCallTimerScope _runtime_timer( i_isolate, \u0026i::RuntimeCallStats::API_String_NewFromUtf8); \n        LOG(i_isolate, ApiEntryCall(\"v8::\" \"String\" \"::\" \"NewFromUtf8\")); \n        if (length \u003c 0) length = StringLength(data); \n        i::Handle\u003ci::String\u003e handle_result = NewString(i_isolate-\u003efactory(), static_cast\u003cv8::NewStringType\u003e(type), i::Vector\u003cconst char\u003e(data, length)) .ToHandleChecked(); \n        result = Utils::ToLocal(handle_result); \n     };\n     return result.FromMaybe(Local\u003cString\u003e());;\n    }\n\nI was wondering where the Utils::ToLocal was defined but could not find it until I found:\n\n    MAKE_TO_LOCAL(ToLocal, String, String)\n\n    #define MAKE_TO_LOCAL(Name, From, To)                                       \\\n    Local\u003cv8::To\u003e Utils::Name(v8::internal::Handle\u003cv8::internal::From\u003e obj) {   \\\n      return Convert\u003cv8::internal::From, v8::To\u003e(obj);                          \\\n    }\n\nThe above can be found in `src/api.h`. The same goes for `Local\u003cObject\u003e,\nLocal\u003cString\u003e` etc.\n\n\n### Small Integers\nReading through v8.h I came accross `// Tag information for Smi`\nSmi stands for small integers.\n\nA pointer is really just a integer that is treated like a memory address. We can\nuse that memory address to get the start of the data located in that memory slot.\nBut we can also just store an normal value like 18 in it. There might be cases\nwhere it does not make sense to store a small integer somewhere in the heap and\nhave a pointer to it, but instead store the value directly in the pointer itself.\nBut that only works for small integers so there needs to be away to know if the\nvalue we want is stored in the pointer or if we should follow the value stored to\nthe heap to get the value.\n\nA word on a 64 bit machine is 8 bytes (64 bits) and all of the pointers need to\nbe aligned to multiples of 8. So a pointer could be:\n```\n1000       = 8\n10000      = 16\n11000      = 24\n100000     = 32\n1000000000 = 512\n```\nRemember that we are talking about the pointers and not the values store at\nthe memory location they point to. We can see that there are always three bits\nthat are zero in the pointers. So we can use them for something else and just\nmask them out when using them as pointers.\n\nTagging involves borrowing one bit of the 32-bit, making it 31-bit and having\nthe leftover bit represent a tag. If the tag is zero then this is a plain value,\nbut if tag is 1 then the pointer must be followed.\nThis does not only have to be for numbers it could also be used for object (I think)\n\nInstead the small integer is represented by the 32 bits plus a pointer to the\n64-bit number. V8 needs to know if a value stored in memory represents a 32-bit\ninteger, or if it is really a 64-bit number, in which case it has to follow the\npointer to get the complete value. This is where the concept of tagging comes in.\n\n\n\n### Properties/Elements\nTake the following object:\n\n    { firstname: \"Jon\", lastname: \"Doe' }\n\nThe above object has two named properties. Named properties differ from integer indexed \nwhich is what you have when you are working with arrays.\n\nMemory layout of JavaScript Object:\n```\nProperties                  JavaScript Object               Elements\n+-----------+              +-----------------+         +----------------+\n|property1  |\u003c------+      | HiddenClass     |  +-----\u003e|                |\n+-----------+       |      +-----------------+  |      +----------------+\n|...        |       +------| Properties      |  |      | element1       |\u003c------+\n+-----------+              +-----------------+  |      +----------------+       |\n|...        |              | Elements        |--+      | ...            |       |\n+-----------+              +-----------------+         +----------------+       |\n|propertyN  | \u003c---------------------+                  | elementN       |       |\n+-----------+                       |                  +----------------+       |\n                                    |                                           |\n                                    |                                           |\n                                    |                                           | \nNamed properties:    { firstname: \"Jon\", lastname: \"Doe' } Indexed Properties: {1: \"Jon\", 2: \"Doe\"}\n```\nWe can see that properies and elements are stored in different data structures.\nElements are usually implemented as a plain array and the indexes can be used for fast access\nto the elements. \nBut for the properties this is not the case. Instead there is a mapping between the property names\nand the index into the properties.\n\nIn `src/objects/objects.h` we can find JSObject:\n\n    class JSObject: public JSReceiver {\n    ...\n    DECL_ACCESSORS(elements, FixedArrayBase)\n\n\nAnd looking a the `DECL_ACCESSOR` macro:\n\n    #define DECL_ACCESSORS(name, type)    \\\n      inline type* name() const;          \\\n      inline void set_##name(type* value, \\\n                             WriteBarrierMode mode = UPDATE_WRITE_BARRIER);\n\n    inline FixedArrayBase* name() const;\n    inline void set_elements(FixedArrayBase* value, WriteBarrierMode = UPDATE_WRITE_BARRIER)\n\nNotice that JSObject extends JSReceiver which is extended by all types that can have properties defined on them. I think this includes all JSObjects and JSProxy. It is in JSReceiver that the we find the properties array:\n\n    DECL_ACCESSORS(raw_properties_or_hash, Object)\n\nNow properties (named properties not elements) can be of different kinds internally. These work just\nlike simple dictionaries from the outside but a dictionary is only used in certain curcumstances\nat runtime.\n\n```\nProperties                  JSObject                    HiddenClass (Map)\n+-----------+              +-----------------+         +----------------+\n|property1  |\u003c------+      | HiddenClass     |--------\u003e| bit field1     |\n+-----------+       |      +-----------------+         +----------------+\n|...        |       +------| Properties      |         | bit field2     |\n+-----------+              +-----------------+         +----------------+\n|...        |              | Elements        |         | bit field3     |\n+-----------+              +-----------------+         +----------------+\n|propertyN  |              | property1       |         \n+-----------+              +-----------------+         \n                           | property2       |\n                           +-----------------+\n                           | ...             |\n                           +-----------------+\n\n```\n\n#### JSObject\nEach JSObject has as its first field a pointer to the generated HiddenClass. A hiddenclass contain mappings from property names to indices into the properties data type. When an instance of JSObject is created a `Map` is passed in.\nAs mentioned earlier JSObject inherits from JSReceiver which inherits from HeapObject\n\nFor example,in [jsobject_test.cc](./test/jsobject_test.cc) we first create a new Map using the internal Isolate Factory:\n\n    v8::internal::Handle\u003cv8::internal::Map\u003e map = factory-\u003eNewMap(v8::internal::JS_OBJECT_TYPE, 24);\n    v8::internal::Handle\u003cv8::internal::JSObject\u003e js_object = factory-\u003eNewJSObjectFromMap(map);\n    EXPECT_TRUE(js_object-\u003eHasFastProperties());\n\nWhen we call `js_object-\u003eHasFastProperties()` this will delegate to the map instance:\n\n    return !map()-\u003eis_dictionary_map();\n\nHow do you add a property to a JSObject instance?\nTake a look at [jsobject_test.cc](./test/jsobject_test.cc) for an example.\n\n\n### Caching\nAre ways to optimize polymorphic function calls in dynamic languages, for example JavaScript.\n\n#### Lookup caches\nSending a message to a receiver requires the runtime to find the correct target method using\nthe runtime type of the receiver. A lookup cache maps the type of the receiver/message name\npair to methods and stores the most recently used lookup results. The cache is first consulted\nand if there is a cache miss a normal lookup is performed and the result stored in the cache.\n\n#### Inline caches\nUsing a lookup cache as described above still takes a considerable amount of time since the\ncache must be probed for each message. It can be observed that the type of the target does often\nnot vary. If a call to type A is done at a particular call site it is very likely that the next\ntime it is called the type will also be A.\nThe method address looked up by the system lookup routine can be cached and the call instruction\ncan be overwritten. Subsequent calls for the same type can jump directly to the cached method and\ncompletely avoid the lookup. The prolog of the called method must verify that the receivers\ntype has not changed and do the lookup if it has changed (the type if incorrect, no longer A for\nexample).\n\nThe target methods address is stored in the callers code, or \"inline\" with the callers code, \nhence the name \"inline cache\".\n\nIf V8 is able to make a good assumption about the type of object that will be passed to a method,\nit can bypass the process of figuring out how to access the objects properties, and instead use the stored information from previous lookups to the objects hidden class.\n\n#### Polymorfic Inline cache (PIC)\nA polymorfic call site is one where there are many equally likely receiver types (and thus\ncall targets).\n\n- Monomorfic means there is only one receiver type\n- Polymorfic a few receiver types\n- Megamorfic very many receiver types\n\nThis type of caching extends inline caching to not just cache the last lookup, but cache\nall lookup results for a given polymorfic call site using a specially generated stub.\nLets say we have a method that iterates through a list of types and calls a method. If \nall the types are the same (monomorfic) a PIC acts just like an inline cache. The calls will\ndirectly call the target method (with the method prolog followed by the method body).\nIf a different type exists in the list there will be a cache miss in the prolog and the lookup\nroutine called. In normal inline caching this would rebind the call, replacing the call to this\ntypes target method. This would happen each time the type changes.\n\nWith PIC the cache miss handler will generate a small stub routine and rebinds the call to this\nstub. The stub will check if the receiver is of a type that it has seen before and branch to \nthe correct targets. Since the type of the target is already known at this point it can directly\nbranch to the target method body without the need for the prolog.\nIf the type has not been seen before it will be added to the stub to handle that type. Eventually\nthe stub will contain all types used and there will be no more cache misses/lookups.\n\nThe problem is that we don't have type information so methods cannot be called directly, but \ninstead be looked up. In a static language a virtual table might have been used. In JavaScript\nthere is no inheritance relationship so it is not possible to know a vtable offset ahead of time.\nWhat can be done is to observe and learn about the \"types\" used in the program. When an object\nis seen it can be stored and the target of that method call can be stored and inlined into that\ncall. Bascially the type will be checked and if that particular type has been seen before the\nmethod can just be invoked directly. But how do we check the type in a dynamic language? The\nanswer is hidden classes which allow the VM to quickly check an object against a hidden class.\n\nThe inline caching source are located in `src/ic`.\n\n## --trace-ic\n\n    $ out/x64.debug/d8 --trace-ic --trace-maps class.js\n\n    before\n    [TraceMaps: Normalize from= 0x19a314288b89 to= 0x19a31428aff9 reason= NormalizeAsPrototype ]\n    [TraceMaps: ReplaceDescriptors from= 0x19a31428aff9 to= 0x19a31428b051 reason= CopyAsPrototype ]\n    [TraceMaps: InitialMap map= 0x19a31428afa1 SFI= 34_Person ]\n\n    [StoreIC in ~Person+65 at class.js:2 (0-\u003e.) map=0x19a31428afa1 0x10e68ba83361 \u003cString[4]: name\u003e]\n    [TraceMaps: Transition from= 0x19a31428afa1 to= 0x19a31428b0a9 name= name ]\n    [StoreIC in ~Person+102 at class.js:3 (0-\u003e.) map=0x19a31428b0a9 0x2beaa25abd89 \u003cString[3]: age\u003e]\n    [TraceMaps: Transition from= 0x19a31428b0a9 to= 0x19a31428b101 name= age ]\n    [TraceMaps: SlowToFast from= 0x19a31428b051 to= 0x19a31428b159 reason= OptimizeAsPrototype ]\n    [StoreIC in ~Person+65 at class.js:2 (.-\u003e1) map=0x19a31428afa1 0x10e68ba83361 \u003cString[4]: name\u003e]\n    [StoreIC in ~Person+102 at class.js:3 (.-\u003e1) map=0x19a31428b0a9 0x2beaa25abd89 \u003cString[3]: age\u003e]\n    [LoadIC in ~+546 at class.js:9 (0-\u003e.) map=0x19a31428b101 0x10e68ba83361 \u003cString[4]: name\u003e]\n    [CallIC in ~+571 at class.js:9 (0-\u003e1) map=0x0 0x32f481082231 \u003cString[5]: print\u003e]\n    Daniel\n    [LoadIC in ~+642 at class.js:10 (0-\u003e.) map=0x19a31428b101 0x2beaa25abd89 \u003cString[3]: age\u003e]\n    [CallIC in ~+667 at class.js:10 (0-\u003e1) map=0x0 0x32f481082231 \u003cString[5]: print\u003e]\n    41\n    [LoadIC in ~+738 at class.js:11 (0-\u003e.) map=0x19a31428b101 0x10e68ba83361 \u003cString[4]: name\u003e]\n    [CallIC in ~+763 at class.js:11 (0-\u003e1) map=0x0 0x32f481082231 \u003cString[5]: print\u003e]\n    Tilda\n    [LoadIC in ~+834 at class.js:12 (0-\u003e.) map=0x19a31428b101 0x2beaa25abd89 \u003cString[3]: age\u003e]\n    [CallIC in ~+859 at class.js:12 (0-\u003e1) map=0x0 0x32f481082231 \u003cString[5]: print\u003e]\n    2\n    [CallIC in ~+927 at class.js:13 (0-\u003e1) map=0x0 0x32f481082231 \u003cString[5]: print\u003e]\n    after\n\nLoadIC (0-\u003e.) means that it has transitioned from unititialized state (0) to pre-monomophic state (.)\nmonomorphic state is specified with a `1`. These states can be found in [src/ic/ic.cc](https://github.com/v8/v8/blob/df1494d69deab472a1a709bd7e688297aa5cc655/src/ic/ic.cc#L33-L52).\nWhat we are doing caching knowledge about the layout of the previously seen object inside the StoreIC/LoadIC calls.\n\n    $ lldb -- out/x64.debug/d8 class.js\n\n#### HeapObject\nThis class describes heap allocated objects. It is in this class we find\ninformation regarding the type of object. This information is contained in\n`v8::internal::Map`.\n\n### v8::internal::Map\n`src/objects/map.h`  \n* `bit_field1`  \n* `bit_field2`\n* `bit field3` contains information about the number of properties that this Map has,\na pointer to an DescriptorArray. The DescriptorArray contains information like the name of the \nproperty, and the posistion where the value is stored in the JSObject.\nI noticed that this information available in src/objects/map.h. \n\n#### DescriptorArray\nCan be found in src/objects/descriptor-array.h. This class extends FixedArray and has the following\nentries:\n\n```\n[0] the number of descriptors it contains  \n[1] If uninitialized this will be Smi(0) otherwise an enum cache bridge which is a FixedArray of size 2: \n  [0] enum cache: FixedArray containing all own enumerable keys  \n  [1] either Smi(0) or a pointer to a FixedArray with indices  \n[2] first key (and internalized String  \n[3] first descriptor  \n```\n### Factory\nEach Internal Isolate has a Factory which is used to create instances. This is\nbecause all handles needs to be allocated using the factory (src/heap/factory.h)\n\n\n### Objects \nAll objects extend the abstract class Object (src/objects/objects.h).\n\n### Oddball\nThis class extends HeapObject and  describes `null`, `undefined`, `true`, and\n`false` objects.\n\n\n#### Map\nExtends HeapObject and all heap objects have a Map which describes the objects structure.\nThis is where you can find the size of the instance, access to the inobject_properties.\n\n### Compiler pipeline\nWhen a script is compiled all of the top level code is parsed. These are function declarartions (but not the function\nbodies). \n\n    function f1() {       \u003c- top level code\n      console.log('f1');  \u003c- non top level\n    }\n\n    function f2() {       \u003c- top level code\n      f1();               \u003c- non top level\n      console.logg('f2'); \u003c- non top level\n    }\n\n    f2();                 \u003c- top level code\n    var i = 10;           \u003c- top level code\n\nThe non top level code must be pre-parsed to check for syntax errors.\nThe top level code is parsed and compiles by the full-codegen compiler. This compiler does not perform any optimizations and\nit's only task is to generate machine code as quickly as possible (this is pre turbofan)\n\n    Source ------\u003e Parser  --------\u003e Full-codegen ---------\u003e Unoptimized Machine Code\n\nSo the whole script is parsed even though we only generated code for the top-level code. The pre-parse (the syntax checking)\nwas not stored in any way. The functions are lazy stubs that when/if the function gets called the function get compiled. This\nmeans that the function has to be parsed (again, the first time was the pre-parse remember).\n\nIf a function is determined to be hot it will be optimized by one of the two optimizing compilers crankshaft for older parts of JavaScript or Turbofan for Web Assembly (WASM) and some of the newer es6 features.\n\nThe first time V8 sees a function it will parse it into an AST but not do any further processing of that tree\nuntil that function is used. \n\n                         +-----\u003e Full-codegen -----\u003e Unoptimized code\n                        /                               \\/ /\\       \\\n    Parser  ------\u003e AST -------\u003e Cranshaft    -----\u003e Optimized code  |\n                        \\                                           /\n                         +-----\u003e Turbofan     -----\u003e Optimized code\n\nInline Cachine (IC) is done here which also help to gather type information.\nV8 also has a profiler thread which monitors which functions are hot and should be optimized. This profiling\nalso allows V8 to find out information about types using IC. This type information can then be fed to Crankshaft/Turbofan.\nThe type information is stored as a 8 bit value. \n\nWhen a function is optimized the unoptimized code cannot be thrown away as it might be needed since JavaScript is highly\ndynamic the optimzed function migth change and the in that case we fallback to the unoptimzed code. This takes up\nalot of memory which may be important for low end devices. Also the time spent in parsing (twice) takes time.\n\nThe idea with Ignition is to be an bytecode interpreter and to reduce memory consumption, the bytecode is very consice\ncompared to native code which can vary depending on the target platform.\nThe whole source can be parsed and compiled, compared to the current pipeline the has the pre-parse and parse stages mentioned above. So even unused functions will get compiled.\nThe bytecode becomes the source of truth instead of as before the AST.\n\n    Source ------\u003e Parser  --------\u003e Ignition-codegen ---------\u003e Bytecode ---------\u003e Turbofan ----\u003e Optimized Code ---+\n                                                                  /\\                                                  |\n                                                                   +--------------------------------------------------+\n\n    function bajja(a, b, c) {\n      var d = c - 100;\n      return a + d * b;\n    }\n\n    var result = bajja(2, 2, 150);\n    print(result); \n\n    $ ./d8 test.js --ignition  --print_bytecode\n\n    [generating bytecode for function: bajja]\n    Parameter count 4\n    Frame size 8\n     14 E\u003e 0x2eef8d9b103e @    0 : 7f                StackCheck\n     38 S\u003e 0x2eef8d9b103f @    1 : 03 64             LdaSmi [100]   // load 100\n     38 E\u003e 0x2eef8d9b1041 @    3 : 2b 02 02          Sub a2, [2]    // a2 is the third argument. a2 is an argument register\n           0x2eef8d9b1044 @    6 : 1f fa             Star r0        // r0 is a register for local variables. We only have one which is d\n     47 S\u003e 0x2eef8d9b1046 @    8 : 1e 03             Ldar a1        // LoaD accumulator from Register argument a1 which is b\n     60 E\u003e 0x2eef8d9b1048 @   10 : 2c fa 03          Mul r0, [3]    // multiply that is our local variable in r0\n     56 E\u003e 0x2eef8d9b104b @   13 : 2a 04 04          Add a0, [4]    // add that to our argument register 0 which is a \n     65 S\u003e 0x2eef8d9b104e @   16 : 83                Return         // return the value in the accumulator?\n\n\n### Abstract Syntax Tree (AST)\nIn src/ast/ast.h. You can print the ast using the `--print-ast` option for d8.\n\nLets take the following javascript and look at the ast:\n\n    const msg = 'testing';\n    console.log(msg);\n\n```\n$ d8 --print-ast simple.js\n[generating interpreter code for user-defined function: ]\n--- AST ---\nFUNC at 0\n. KIND 0\n. SUSPEND COUNT 0\n. NAME \"\"\n. INFERRED NAME \"\"\n. DECLS\n. . VARIABLE (0x7ffe5285b0f8) (mode = CONST) \"msg\"\n. BLOCK NOCOMPLETIONS at -1\n. . EXPRESSION STATEMENT at 12\n. . . INIT at 12\n. . . . VAR PROXY context[4] (0x7ffe5285b0f8) (mode = CONST) \"msg\"\n. . . . LITERAL \"testing\"\n. EXPRESSION STATEMENT at 23\n. . ASSIGN at -1\n. . . VAR PROXY local[0] (0x7ffe5285b330) (mode = TEMPORARY) \".result\"\n. . . CALL Slot(0)\n. . . . PROPERTY Slot(4) at 31\n. . . . . VAR PROXY Slot(2) unallocated (0x7ffe5285b3d8) (mode = DYNAMIC_GLOBAL) \"console\"\n. . . . . NAME log\n. . . . VAR PROXY context[4] (0x7ffe5285b0f8) (mode = CONST) \"msg\"\n. RETURN at -1\n. . VAR PROXY local[0] (0x7ffe5285b330) (mode = TEMPORARY) \".result\"\n```\nYou can find the declaration of EXPRESSION in ast.h.\n\n### Bytecode\nCan be found in `src/interpreter/bytecodes.h`\n\n* StackCheck checks that stack limits are not exceeded to guard against overflow.\n* `Star` Store content in accumulator regiser in register (the operand).\n* Ldar   LoaD accumulator from Register argument a1 which is b\n\nThe registers are not machine registers, apart from the accumlator as I\nunderstand it, but would instead be stack allocated.\n\n\n#### Parsing\nParsing is the parsing of the JavaScript and the generation of the abstract\nsyntax tree. That tree is then visited and bytecode generated from it. This\nsection tries to figure out where in the code these operations are performed.\n\nFor example, take the script example.\n\n    $ make run-script\n    $ lldb -- run-script\n    (lldb) br s -n main\n    (lldb) r\n\nLets take a look at the following line:\n\n    Local\u003cScript\u003e script = Script::Compile(context, source).ToLocalChecked();\n\nThis will land us in `api.cc`\n\n    ScriptCompiler::Source script_source(source);\n    return ScriptCompiler::Compile(context, \u0026script_source);\n\n    MaybeLocal\u003cScript\u003e ScriptCompiler::Compile(Local\u003cContext\u003e context, Source* source, CompileOptions options) {\n    ...\n    auto isolate = context-\u003eGetIsolate();\n    auto maybe = CompileUnboundInternal(isolate, source, options);\n\n`CompileUnboundInternal` will call `GetSharedFunctionInfoForScript` (in src/compiler.cc):\n\n    result = i::Compiler::GetSharedFunctionInfoForScript(\n          str, name_obj, line_offset, column_offset, source-\u003eresource_options,\n          source_map_url, isolate-\u003enative_context(), NULL, \u0026script_data, options,\n          i::NOT_NATIVES_CODE);\n\n    (lldb) br s -f compiler.cc -l 1259\n\n    LanguageMode language_mode = construct_language_mode(FLAG_use_strict);\n    (lldb) p language_mode\n    (v8::internal::LanguageMode) $10 = SLOPPY\n\n`LanguageMode` can be found in src/globals.h and it is an enum with three values:\n\n    enum LanguageMode : uint32_t { SLOPPY, STRICT, LANGUAGE_END };\n\n`SLOPPY` mode, I assume, is the mode when there is no `\"use strict\";`. Remember that this can go inside a function and does not\nhave to be at the top level of the file.\n\n    ParseInfo parse_info(script);\n\nThere is a [unit test](./test/ast_test.cc) that shows how a ParseInfo instance can be created\nand inspected.\n\nThis will call ParseInfo's constructor (in src/parsing/parse-info.cc), and which will call `ParseInfo::InitFromIsolate`:\n\n    DCHECK_NOT_NULL(isolate);\n    set_hash_seed(isolate-\u003eheap()-\u003eHashSeed());\n    set_stack_limit(isolate-\u003estack_guard()-\u003ereal_climit());\n    set_unicode_cache(isolate-\u003eunicode_cache());\n    set_runtime_call_stats(isolate-\u003ecounters()-\u003eruntime_call_stats());\n    set_ast_string_constants(isolate-\u003east_string_constants());\n\nI was curious about these ast_string_constants:\n\n    (lldb) p *ast_string_constants_\n    (const v8::internal::AstStringConstants) $58 = {\n      zone_ = {\n        allocation_size_ = 1312\n        segment_bytes_allocated_ = 8192\n        position_ = 0x0000000105052538 \u003cno value available\u003e\n        limit_ = 0x0000000105054000 \u003cno value available\u003e\n        allocator_ = 0x0000000103e00080\n        segment_head_ = 0x0000000105052000\n        name_ = 0x0000000101623a70 \"../../src/ast/ast-value-factory.h:365\"\n        sealed_ = false\n      }\n      string_table_ = {\n        v8::base::TemplateHashMapImpl\u003cvoid *, void *, v8::base::HashEqualityThenKeyMatcher\u003cvoid *, bool (*)(void *, void *)\u003e, v8::base::DefaultAllocationPolicy\u003e = {\n          map_ = 0x0000000105054000\n          capacity_ = 64\n          occupancy_ = 41\n          match_ = {\n            match_ = 0x000000010014b260 (libv8.dylib`v8::internal::AstRawString::Compare(void*, void*) at ast-value-factory.cc:122)\n          }\n        }\n      }\n      hash_seed_ = 500815076\n      anonymous_function_string_ = 0x0000000105052018\n      arguments_string_ = 0x0000000105052038\n      async_string_ = 0x0000000105052058\n      await_string_ = 0x0000000105052078\n      boolean_string_ = 0x0000000105052098\n      constructor_string_ = 0x00000001050520b8\n      default_string_ = 0x00000001050520d8\n      done_string_ = 0x00000001050520f8\n      dot_string_ = 0x0000000105052118\n      dot_for_string_ = 0x0000000105052138\n      dot_generator_object_string_ = 0x0000000105052158\n      dot_iterator_string_ = 0x0000000105052178\n      dot_result_string_ = 0x0000000105052198\n      dot_switch_tag_string_ = 0x00000001050521b8\n      dot_catch_string_ = 0x00000001050521d8\n      empty_string_ = 0x00000001050521f8\n      eval_string_ = 0x0000000105052218\n      function_string_ = 0x0000000105052238\n      get_space_string_ = 0x0000000105052258\n      length_string_ = 0x0000000105052278\n      let_string_ = 0x0000000105052298\n      name_string_ = 0x00000001050522b8\n      native_string_ = 0x00000001050522d8\n      new_target_string_ = 0x00000001050522f8\n      next_string_ = 0x0000000105052318\n      number_string_ = 0x0000000105052338\n      object_string_ = 0x0000000105052358\n      proto_string_ = 0x0000000105052378\n      prototype_string_ = 0x0000000105052398\n      return_string_ = 0x00000001050523b8\n      set_space_string_ = 0x00000001050523d8\n      star_default_star_string_ = 0x00000001050523f8\n      string_string_ = 0x0000000105052418\n      symbol_string_ = 0x0000000105052438\n      this_string_ = 0x0000000105052458\n      this_function_string_ = 0x0000000105052478\n      throw_string_ = 0x0000000105052498\n      undefined_string_ = 0x00000001050524b8\n      use_asm_string_ = 0x00000001050524d8\n      use_strict_string_ = 0x00000001050524f8\n      value_string_ = 0x0000000105052518\n    } \n\nSo these are constants that are set on the new ParseInfo instance using the values from the isolate. Not exactly sure what I \nwant with this but I might come back to it later.\nSo, we are back in ParseInfo's constructor:\n\n    set_allow_lazy_parsing();\n    set_toplevel();\n    set_script(script);\n\nScript is of type v8::internal::Script which can be found in src/object/script.h\n\nBack now in compiler.cc and the GetSharedFunctionInfoForScript function:\n\n    Zone compile_zone(isolate-\u003eallocator(), ZONE_NAME);\n\n    ...\n    if (parse_info-\u003eliteral() == nullptr \u0026\u0026 !parsing::ParseProgram(parse_info, isolate))\n\n`ParseProgram`:\n\n    Parser parser(info);\n    ...\n    FunctionLiteral* result = nullptr;\n    result = parser.ParseProgram(isolate, info);\n\n`parser.ParseProgram`: \n\n    Handle\u003cString\u003e source(String::cast(info-\u003escript()-\u003esource()));\n\n\n    (lldb) job *source\n    \"var user1 = new Person('Fletch');\\x0avar user2 = new Person('Dr.Rosen');\\x0aprint(\"user1 = \" + user1.name);\\x0aprint(\"user2 = \" + user2.name);\\x0a\\x0a\"\n\nSo here we can see our JavaScript as a String.\n\n    std::unique_ptr\u003cUtf16CharacterStream\u003e stream(ScannerStream::For(source));\n    scanner_.Initialize(stream.get(), info-\u003eis_module());\n    result = DoParseProgram(info);\n\n`DoParseProgram`:\n\n    (lldb) br s -f parser.cc -l 639\n    ...\n\n    this-\u003escope()-\u003eSetLanguageMode(info-\u003elanguage_mode());\n    ParseStatementList(body, Token::EOS, \u0026ok);\n\nThis call will land in parser-base.h and its `ParseStatementList` function.\n\n    (lldb) br s -f parser-base.h -l 4695\n\n    StatementT stat = ParseStatementListItem(CHECK_OK_CUSTOM(Return, kLazyParsingComplete));\n\n    result = CompileToplevel(\u0026parse_info, isolate, Handle\u003cSharedFunctionInfo\u003e::null());\n\nThis will land in `CompileTopelevel` (in the same file which is src/compiler.cc):\n\n    // Compile the code.\n    result = CompileUnoptimizedCode(parse_info, shared_info, isolate);\n\nThis will land in `CompileUnoptimizedCode` (in the same file which is src/compiler.cc):\n\n    // Prepare and execute compilation of the outer-most function.\n    std::unique_ptr\u003cCompilationJob\u003e outer_job(\n       PrepareAndExecuteUnoptimizedCompileJob(parse_info, parse_info-\u003eliteral(),\n                                              shared_info, isolate));\n\n\n    std::unique_ptr\u003cCompilationJob\u003e job(\n        interpreter::Interpreter::NewCompilationJob(parse_info, literal, isolate));\n    if (job-\u003ePrepareJob() == CompilationJob::SUCCEEDED \u0026\u0026\n        job-\u003eExecuteJob() == CompilationJob::SUCCEEDED) {\n      return job;\n    }\n\nPrepareJobImpl:\n\n    CodeGenerator::MakeCodePrologue(parse_info(), compilation_info(),\n                                    \"interpreter\");\n    return SUCCEEDED;\n\ncodegen.cc `MakeCodePrologue`:\n\ninterpreter.cc ExecuteJobImpl:\n\n    generator()-\u003eGenerateBytecode(stack_limit());    \n\nsrc/interpreter/bytecode-generator.cc\n\n     RegisterAllocationScope register_scope(this);\n\nThe bytecode is register based (if that is the correct term) and we had an example previously. I'm guessing \nthat this is what this call is about.\n\nVisitDeclarations will iterate over all the declarations in the file which in our case are:\n\n    var user1 = new Person('Fletch');\n    var user2 = new Person('Dr.Rosen');\n\n    (lldb) p *variable-\u003eraw_name()\n    (const v8::internal::AstRawString) $33 = {\n       = {\n        next_ = 0x000000010600a280\n        string_ = 0x000000010600a280\n      }\n      literal_bytes_ = (start_ = \"user1\", length_ = 5)\n      hash_field_ = 1303438034\n      is_one_byte_ = true\n      has_string_ = false\n    }\n\n    // Perform a stack-check before the body.\n    builder()-\u003eStackCheck(info()-\u003eliteral()-\u003estart_position());\n\nSo that call will output a stackcheck instruction, like in the example above:\n\n    14 E\u003e 0x2eef8d9b103e @    0 : 7f                StackCheck\n\n### Performance\nSay you have the expression x + y the full-codegen compiler might produce:\n\n    movq rax, x\n    movq rbx, y\n    callq RuntimeAdd\n\nIf x and y are integers just using the `add` operation would be much quicker:\n\n    movq rax, x\n    movq rbx, y\n    add rax, rbx\n\n\nRecall that functions are optimized so if the compiler has to bail out and unoptimize \npart of a function then the whole functions will be affected and it will go back to \nthe unoptimized version.\n\n## Bytecode\nThis section will examine the bytecode for the following JavaScript:\n\n    function beve() {\n      const p = new Promise((resolve, reject) =\u003e {\n        resolve('ok');\n      });\n\n      p.then(msg =\u003e {\n        console.log(msg);\n      });\n    }\n\n    beve(); \n\n    $ d8 --print-bytecode promise.js\n\nFirst have the main function which does not have a name:\n\n    [generating bytecode for function: ]\n    (The code that generated this can be found in src/objects.cc BytecodeArray::Dissassemble)\n    Parameter count 1\n    Frame size 32\n           // load what ever the FixedArray[4] is in the constant pool into the accumulator.\n           0x34423e7ac19e @    0 : 09 00             LdaConstant [0] \n           // store the FixedArray[4] in register r1\n           0x34423e7ac1a0 @    2 : 1e f9             Star r1\n           // store zero into the accumulator.\n           0x34423e7ac1a2 @    4 : 02                LdaZero\n           // store zero (the contents of the accumulator) into register r2.\n           0x34423e7ac1a3 @    5 : 1e f8             Star r2\n           // \n           0x34423e7ac1a5 @    7 : 1f fe f7          Mov \u003cclosure\u003e, r3\n           0x34423e7ac1a8 @   10 : 53 96 01 f9 03    CallRuntime [DeclareGlobalsForInterpreter], r1-r3\n      0 E\u003e 0x34423e7ac1ad @   15 : 90                StackCheck\n    141 S\u003e 0x34423e7ac1ae @   16 : 0a 01 00          LdaGlobal [1], [0]\n           0x34423e7ac1b1 @   19 : 1e f9             Star r1\n    141 E\u003e 0x34423e7ac1b3 @   21 : 4f f9 03          CallUndefinedReceiver0 r1, [3]\n           0x34423e7ac1b6 @   24 : 1e fa             Star r0\n    148 S\u003e 0x34423e7ac1b8 @   26 : 94                Return\n\n    Constant pool (size = 2)\n    0x34423e7ac149: [FixedArray] in OldSpace\n     - map = 0x344252182309 \u003cMap(HOLEY_ELEMENTS)\u003e\n     - length: 2\n           0: 0x34423e7ac069 \u003cFixedArray[4]\u003e\n           1: 0x34423e7abf59 \u003cString[4]: beve\u003e\n\n    Handler Table (size = 16) Load the global with name in constant pool entry \u003cname_index\u003e into the\n    // accumulator using FeedBackVector slot \u003cslot\u003e outside of a typeof\n\n* LdaConstant \u003cidx\u003e \nLoad the constant at index from the constant pool into the accumulator.  \n* Star \u003cdst\u003e\nStore the contents of the accumulator register in dst.  \n* Ldar \u003csrc\u003e\nLoad accumulator with value from register src.  \n* LdaGlobal \u003cidx\u003e \u003cslot\u003e\nLoad the global with name in constant pool entry idx into the accumulator using FeedBackVector slot  outside of a typeof.\n* Mov \u003cclosure\u003e, \u003cr3\u003e\nStore the value of register  \n\nYou can find the declarations for the these instructions in `src/interpreter/interpreter-generator.cc`.\n\n\n## Unified code generation architecture\n\n## FeedbackVector\nIs attached to every function and is responsible for recording and managing all execution feedback, which is information about types enabling. \nYou can find the declaration for this class in `src/feedback-vector.h`\n\n\n## BytecodeGenerator\nIs currently the only part of V8 that cares about the AST.\n\n## BytecodeGraphBuilder\nProduces high-level IR graph based on interpreter bytecodes.\n\n\n## TurboFan\nIs a compiler backend that gets fed a control flow graph and then does instruction selection, register allocation and code generation. The code generation generates \n\n\n### Execution/Runtime\nI'm not sure if V8 follows this exactly but I've heard and read that when the engine comes \nacross a function declaration it only parses and verifies the syntax and saves a ref\nto the function name. The statements inside the function are not checked at this stage\nonly the syntax of the function declaration (parenthesis, arguments, brackets etc). \n\n\n### Function methods\nThe declaration of Function can be found in `include/v8.h` (just noting this as I've looked for it several times)\n\n### Symbol\nThe declarations for the Symbol class can be found in `v8.h` and the internal\nimplementation in `src/api/api.cc`.\n\nThe well known Symbols are generated using macros so you won't find the just\nby searching using the static function names like 'GetToPrimitive`.\n```c++\n#define WELL_KNOWN_SYMBOLS(V)                 \\\n  V(AsyncIterator, async_iterator)            \\\n  V(HasInstance, has_instance)                \\\n  V(IsConcatSpreadable, is_concat_spreadable) \\\n  V(Iterator, iterator)                       \\\n  V(Match, match)                             \\\n  V(Replace, replace)                         \\\n  V(Search, search)                           \\\n  V(Split, split)                             \\\n  V(ToPrimitive, to_primitive)                \\\n  V(ToStringTag, to_string_tag)               \\\n  V(Unscopables, unscopables)\n\n#define SYMBOL_GETTER(Name, name)                                   \\\n  Local\u003cSymbol\u003e v8::Symbol::Get##Name(Isolate* isolate) {           \\\n    i::Isolate* i_isolate = reinterpret_cast\u003ci::Isolate*\u003e(isolate); \\\n    return Utils::ToLocal(i_isolate-\u003efactory()-\u003ename##_symbol());   \\\n  }\n```\nSo GetToPrimitive would become:\n```c++\nLocal\u003cSymbol\u003e v8::Symbol::GeToPrimitive(Isolate* isolate) {\n  i::Isolate* i_isolate = reinterpret_cast\u003ci::Isolate*\u003e(isolate);\n  return Utils::ToLocal(i_isolate-\u003efactory()-\u003eto_primitive_symbol());\n}\n\n```\n\n\n\nThere is an example in [symbol-test.cc](./test/symbol-test.cc).\n\n## Builtins\nAre JavaScript functions/objects that are provided by V8. These are built using a\nC++ DSL and are passed through:\n\n    CodeStubAssembler -\u003e CodeAssembler -\u003e RawMachineAssembler.\n\nBuiltins need to have bytecode generated for them so that they can be run in TurboFan.\n\n`src/code-stub-assembler.h`\n\nAll the builtins are declared in `src/builtins/builtins-definitions.h` by the\n`BUILTIN_LIST_BASE` macro. \nThere are different type of builtins (TF = Turbo Fan):\n* TFJ \nJavaScript linkage which means it is callable as a JavaScript function  \n* TFS\nCodeStub linkage. A builtin with stub linkage can be used to extract common code into a separate code object which can\nthen be used by multiple callers. These is useful because builtins are generated at compile time and\nincluded in the V8 snapshot. This means that they are part of every isolate that is created. Being \nable to share common code for multiple builtins will save space.\n\n* TFC \nCodeStub linkage with custom descriptor\n\nTo see how this works in action we first need to disable snapshots. If we don't, we won't be able to\nset breakpoints as the the heap will be serialized at compile time and deserialized upon startup of v8.\n\nTo find the option to disable snapshots use:\n\n    $ gn args --list out.gn/learning --short | more\n    ...\n    v8_use_snapshot=true\n    $ gn args out.gn/learning\n    v8_use_snapshot=false\n    $ gn -C out.gn/learning\n\nAfter building we should be able to set a break point in bootstrapper.cc and its function \n`Genesis::InitializeGlobal`:\n\n    (lldb) br s -f bootstrapper.cc -l 2684\n\nLets take a look at how the `JSON` object is setup:\n\n    Handle\u003cString\u003e name = factory-\u003eInternalizeUtf8String(\"JSON\");\n    Handle\u003cJSObject\u003e json_object = factory-\u003eNewJSObject(isolate-\u003eobject_function(), TENURED);\n\n`TENURED` means that this object should be allocated directly in the old generation.\n\n    JSObject::AddProperty(global, name, json_object, DONT_ENUM);\n\n`DONT_ENUM` is checked by some builtin functions and if set this object will be ignored by those\nfunctions.\n\n    SimpleInstallFunction(json_object, \"parse\", Builtins::kJsonParse, 2, false);\n\nHere we can see that we are installing a function named `parse`, which takes 2 parameters. You can\nfind the definition in src/builtins/builtins-json.cc.\nWhat does the `SimpleInstallFunction` do?\n\nLets take `console` as an example which was created using:\n\n    Handle\u003cJSObject\u003e console = factory-\u003eNewJSObject(cons, TENURED);\n    JSObject::AddProperty(global, name, console, DONT_ENUM);\n    SimpleInstallFunction(console, \"debug\", Builtins::kConsoleDebug, 1, false,\n                          NONE);\n\n    V8_NOINLINE Handle\u003cJSFunction\u003e SimpleInstallFunction(\n      Handle\u003cJSObject\u003e base, \n      const char* name, \n      Builtins::Name call, \n      int len,\n      bool adapt, \n      PropertyAttributes attrs = DONT_ENUM,\n      BuiltinFunctionId id = kInvalidBuiltinFunctionId) {\n\nSo we can see that base is our Handle to a JSObject, and name is \"debug\".\nBuiltins::Name is Builtins:kConsoleDebug. Where is this defined?  \nYou can find a macro named `CPP` in `src/builtins/builtins-definitions.h`:\n\n   CPP(ConsoleDebug)\n\nWhat does this macro expand to?  \nIt is part of the `BUILTIN_LIST_BASE` macro in builtin-definitions.h\nWe have to look at where BUILTIN_LIST is used which we can find in builtins.cc.\nIn `builtins.cc` we have an array of `BuiltinMetadata` which is declared as:\n\n    const BuiltinMetadata builtin_metadata[] = {\n      BUILTIN_LIST(DECL_CPP, DECL_API, DECL_TFJ, DECL_TFC, DECL_TFS, DECL_TFH, DECL_ASM)\n    };\n\n    #define DECL_CPP(Name, ...) { #Name, Builtins::CPP, \\\n                                { FUNCTION_ADDR(Builtin_##Name) }},\n\nWhich will expand to the creation of a BuiltinMetadata struct entry in the array. The\nBuildintMetadata struct looks like this which might help understand what is going on:\n\n    struct BuiltinMetadata {\n      const char* name;\n      Builtins::Kind kind;\n      union {\n        Address cpp_entry;       // For CPP and API builtins.\n        int8_t parameter_count;  // For TFJ builtins.\n      } kind_specific_data;\n    };\n\nSo the `CPP(ConsoleDebug)` will expand to an entry in the array which would look something like\nthis:\n\n    { ConsoleDebug, \n      Builtins::CPP, \n      {\n        reinterpret_cast\u003cv8::internal::Address\u003e(reinterpret_cast\u003cintptr_t\u003e(Builtin_ConsoleDebug))\n      }\n    },\n\nThe third paramter is the creation on the union which might not be obvious.\n\nBack to the question I'm trying to answer which is:  \n\"Buildtins::Name is is Builtins:kConsoleDebug. Where is this defined?\"  \nFor this we have to look at `builtins.h` and the enum Name:\n\n    enum Name : int32_t {\n    #define DEF_ENUM(Name, ...) k##Name,\n        BUILTIN_LIST_ALL(DEF_ENUM)\n    #undef DEF_ENUM\n        builtin_count\n     };\n\nThis will expand to the complete list of builtins in builtin-definitions.h using the DEF_ENUM\nmacro. So the expansion for ConsoleDebug will look like:\n\n    enum Name: int32_t {\n      ...\n      kDebugConsole,\n      ...\n    };\n\nSo backing up to looking at the arguments to SimpleInstallFunction which are:\n\n    SimpleInstallFunction(console, \"debug\", Builtins::kConsoleDebug, 1, false,\n                          NONE);\n\n    V8_NOINLINE Handle\u003cJSFunction\u003e SimpleInstallFunction(\n      Handle\u003cJSObject\u003e base, \n      const char* name, \n      Builtins::Name call, \n      int len,\n      bool adapt, \n      PropertyAttributes attrs = DONT_ENUM,\n      BuiltinFunctionId id = kInvalidBuiltinFunctionId) {\n\nWe know about `Builtins::Name`, so lets look at len which is one, what is this?  \nSimpleInstallFunction will call:\n\n    Handle\u003cJSFunction\u003e fun =\n      SimpleCreateFunction(base-\u003eGetIsolate(), function_name, call, len, adapt);\n\n`len` would be used if adapt was true but it is false in our case. This is what it would \nbe used for if adapt was true:\n\n    fun-\u003eshared()-\u003eset_internal_formal_parameter_count(len);\n\nI'm not exactly sure what adapt is referring to here.\n\nPropertyAttributes is not specified so it will get the default value of `DONT_ENUM`.\nThe last parameter which is of type BuiltinFunctionId is not specified either so the\ndefault value of `kInvalidBuiltinFunctionId` will be used. This is an enum defined in \n`src/objects/objects.h`.\n\n\nThis [blog](https://v8project.blogspot.se/2017/11/csa.html) provides an example of adding\na function to the String object. \n\n    $ out.gn/learning/mksnapshot --print-code \u003e output\n\nYou can then see the generated code from this. This will produce a code stub that can \nbe called through C++. Lets update this to have it be called from JavaScript:\n\nUpdate builtins/builtins-string-get.cc :\n\n    TF_BUILTIN(GetStringLength, StringBuiltinsAssembler) {\n      Node* const str = Parameter(Descriptor::kReceiver);\n      Return(LoadStringLength(str));\n    }\n\nWe also have to update builtins/builtins-definitions.h:\n\n    TFJ(GetStringLength, 0)\n\nAnd bootstrapper.cc:\n\n    SimpleInstallFunction(prototype, \"len\", Builtins::kGetStringLength, 0, true);\n\nIf you now build using 'ninja -C out.gn/learning_v8' you should be able to run d8 and try this out:\n\n    d8\u003e const s = 'testing'\n    undefined\n    d8\u003e s.len()\n    7\n\nNow lets take a closer look at the code that is generated for this:\n\n    $ out.gn/learning/mksnapshot --print-code \u003e output\n\nLooking at the output generated I was surprised to see two entries for GetStringLength (I changed the name\njust to make sure there was not something else generating the second one). Why two?\n\nThe following uses Intel Assembly syntax which means that no register/immediate prefixes and the first operand is the \ndestination and the second operand the source.\n```\n--- Code ---\nkind = BUILTIN\nname = BeveStringLength\ncompiler = turbofan\nInstructions (size = 136)\n0x1fafde09b3a0     0  55             push rbp\n0x1fafde09b3a1     1  4889e5         REX.W movq rbp,rsp                  // movq rsp into rbp\n\n0x1fafde09b3a4     4  56             push rsi                            // push the value of rsi (first parameter) onto the stack \n0x1fafde09b3a5     5  57             push rdi                            // push the value of rdi (second parameter) onto the stack\n0x1fafde09b3a6     6  50             push rax                            // push the value of rax (accumulator) onto the stack\n\n0x1fafde09b3a7     7  4883ec08       REX.W subq rsp,0x8                  // make room for a 8 byte value on the stack\n0x1fafde09b3ab     b  488b4510       REX.W movq rax,[rbp+0x10]           // move the value rpm + 10 to rax\n0x1fafde09b3af     f  488b58ff       REX.W movq rbx,[rax-0x1]\n0x1fafde09b3b3    13  807b0b80       cmpb [rbx+0xb],0x80                // IsString(object). compare byte to zero\n0x1fafde09b3b7    17  0f8350000000   jnc 0x1fafde09b40d  \u003c+0x6d\u003e        // jump it carry flag was not set\n\n0x1fafde09b3bd    1d  488b400f       REX.W movq rax,[rax+0xf]\n0x1fafde09b3c1    21  4989e2         REX.W movq r10,rsp\n0x1fafde09b3c4    24  4883ec08       REX.W subq rsp,0x8\n0x1fafde09b3c8    28  4883e4f0       REX.W andq rsp,0xf0\n0x1fafde09b3cc    2c  4c891424       REX.W movq [rsp],r10\n0x1fafde09b3d0    30  488945e0       REX.W movq [rbp-0x20],rax\n0x1fafde09b3d4    34  48be0000000001000000 REX.W movq rsi,0x100000000\n0x1fafde09b3de    3e  48bad9c228dfa8090000 REX.W movq rdx,0x9a8df28c2d9    ;; object: 0x9a8df28c2d9 \u003cString[101]: CAST(LoadObjectField(object, offset, MachineTypeOf\u003cT\u003e::value)) at ../../src/code-stub-assembler.h:432\u003e\n0x1fafde09b3e8    48  488bf8         REX.W movq rdi,rax\n0x1fafde09b3eb    4b  48b830726d0a01000000 REX.W movq rax,0x10a6d7230    ;; external reference (check_object_type)\n0x1fafde09b3f5    55  40f6c40f       testb rsp,0xf\n0x1fafde09b3f9    59  7401           jz 0x1fafde09b3fc  \u003c+0x5c\u003e\n0x1fafde09b3fb    5b  cc             int3l\n0x1fafde09b3fc    5c  ffd0           call rax\n0x1fafde09b3fe    5e  488b2424       REX.W movq rsp,[rsp]\n0x1fafde09b402    62  488b45e0       REX.W movq rax,[rbp-0x20]\n0x1fafde09b406    66  488be5         REX.W movq rsp,rbp\n0x1fafde09b409    69  5d             pop rbp\n0x1fafde09b40a    6a  c20800         ret 0x8\n\n// this is where we jump to if IsString failed\n0x1fafde09b40d    6d  48ba71c228dfa8090000 REX.W movq rdx,0x9a8df28c271    ;; object: 0x9a8df28c271 \u003cString[76]\\: CSA_ASSERT failed: IsString(object) [../../src/code-stub-assembler.cc:1498]\\n\u003e\n0x1fafde09b417    77  e8e4d1feff     call 0x1fafde088600     ;; code: BUILTIN\n0x1fafde09b41c    7c  cc             int3l\n0x1fafde09b41d    7d  cc             int3l\n0x1fafde09b41e    7e  90             nop\n0x1fafde09b41f    7f  90             nop\n\n\nSafepoints (size = 8)\n\nRelocInfo (size = 7)\n0x1fafde09b3e0  embedded object  (0x9a8df28c2d9 \u003cString[101]: CAST(LoadObjectField(object, offset, MachineTypeOf\u003cT\u003e::value)) at ../../src/code-stub-assembler.h:432\u003e)\n0x1fafde09b3ed  external reference (check_object_type)  (0x10a6d7230)\n0x1fafde09b40f  embedded object  (0x9a8df28c271 \u003cString[76]\\: CSA_ASSERT failed: IsString(object) [../../src/code-stub-assembler.cc:1498]\\n\u003e)\n0x1fafde09b418  code target (BUILTIN)  (0x1fafde088600)\n\n--- End code --- \n```\n\n\n### TF_BUILTIN macro\nIs a macro to defining Turbofan (TF) builtins and can be found in `builtins/builtins-utils-gen.h`\n\nIf we take a look at the file src/builtins/builtins-bigint-gen.cc and the following\nfunction:\n```c++\nTF_BUILTIN(BigIntToI64, CodeStubAssembler) {                                       \n  if (!Is64()) {                                                                   \n    Unreachable();                                                                 \n    return;                                                                        \n  }                                                                                \n                                                                                   \n  TNode\u003cObject\u003e value = CAST(Parameter(Descriptor::kArgument));                    \n  TNode\u003cContext\u003e context = CAST(Parameter(Descriptor::kContext));                  \n  TNode\u003cBigInt\u003e n = ToBigInt(context, value);                                      \n                                                                                   \n  TVARIABLE(UintPtrT, var_low);                                                    \n  TVARIABLE(UintPtrT, var_high);                                                   \n                                                                                   \n  BigIntToRawBytes(n, \u0026var_low, \u0026var_high);                                        \n  Return(var_low.value());                                                         \n}\n```\nLet's take our GetStringLength example from above and see what this will be expanded to after\nprocessing this macro:\n```console\n$ clang++ --sysroot=build/linux/debian_sid_amd64-sysroot -isystem=./buildtools/third_party/libc++/trunk/include -isystem=buildtools/third_party/libc++/trunk/include -I. -E src/builtins/builtins-bigint-gen.cc \u003e builtins-bigint-gen.cc.pp\n```\n```c++\nstatic void Generate_BigIntToI64(compiler::CodeAssemblerState* state);\n\nclass BigIntToI64Assembler : public CodeStubAssembler { \n public:\n  using Descriptor = Builtin_BigIntToI64_InterfaceDescriptor; \n  explicit BigIntToI64Assembler(compiler::CodeAssemblerState* state) : CodeStubAssembler(state) {} \n  void GenerateBigIntToI64Impl(); \n  Node* Parameter(Descriptor::ParameterIndices index) {\n    return CodeAssembler::Parameter(static_cast\u003cint\u003e(index));\n  }\n}; \n\nvoid Builtins::Generate_BigIntToI64(compiler::CodeAssemblerState* state) {\n  BigIntToI64Assembler assembler(state);\n  state-\u003eSetInitialDebugInformation(\"BigIntToI64\", \"src/builtins/builtins-bigint-gen.cc\", 14);\n  if (Builtins::KindOf(Builtins::kBigIntToI64) == Builtins::TFJ) {\n    assembler.PerformStackCheck(assembler.GetJSContextParameter());\n  }\n  assembler.GenerateBigIntToI64Impl();\n} \nvoid BigIntToI64Assembler::GenerateBigIntToI64Impl() {\n if (!Is64()) {                                                                \n   Unreachable();                                                              \n   return;                                                                     \n }                                                                             \n                                                                                \n TNode\u003cObject\u003e value = Cast(Parameter(Descriptor::kArgument));                 \n TNode\u003cContext\u003e context = Cast(Parameter(Descriptor::kContext));                \n TNode\u003cBigInt\u003e n = ToBigInt(context, value);                                   \n                                                                               \n TVariable\u003cUintPtrT\u003e var_low(this);                                            \n TVariable\u003cUintPtrT\u003e var_high(this);                                           \n                                                                                \n BigIntToRawBytes(n, \u0026var_low, \u0026var_high);                                     \n Return(var_low.value());                                                      \n} \n```\n\nFrom the resulting class you can see how `Parameter` can be used from within `TF_BUILTIN` macro.\n\n## Building V8\nYou'll need to have checked out the Google V8 sources to you local file system\nand build it by following the instructions found [here](https://v8.dev/docs/build).\n\n### Configure v8 build for learning-v8\nThere is a make target that can generate a build configuration for V8 that is\nspecific to this project. It can be run using the following command:\n```\n$ make configure_v8\n```\nThen to compile this configuration:\n``` console\n$ make compile_v8\n```\n\n\n### [gclient](https://www.chromium.org/developers/how-tos/depottools) sync\n```console\n$ gclient sync\n```\n\n#### Troubleshooting build:\n```console\n/v8_src/v8/out/x64.release/obj/libv8_monolith.a(eh-frame.o):eh-frame.cc:function v8::internal::EhFrameWriter::WriteEmptyEhFrame(std::__1::basic_ostream\u003cchar, std::__1::char_traits\u003cchar\u003e \u003e\u0026): error: undefined reference to 'std::__1::basic_ostream\u003cchar, std::__1::char_traits\u003cchar\u003e \u003e::write(char const*, long)'\nclang: error: linker command failed with exit code 1 (use -v to see invocation)\n```\n`-stdlib=libc++` is llvm's C++ runtime. This runtime has a `__1` namespace.\nI looks like the static library above was compiled with clangs/llvm's `libc++`\nas we are seeing the `__1` namespace.\n\n-stdlib=libstdc++ is GNU's C++ runtime\n\nSo we can see that the namespace `std::__1` is used which we now\nknow is the namespace that libc++ which is clangs libc++ library.\nI guess we could go about this in two ways, either we can change v8 build of\nto use glibc++ when compiling so that the symbols are correct when we want to\nlink against it, or we can update our linker (ld) to use libc++.\n\nWe need to include the correct libraries to link with during linking, \nwhich means specifying:\n```\n-stdlib=libc++ -Wl,-L$(v8_build_dir)\n```\nIf we look in $(v8_build_dir) we find `libc++.so`. We also need to this library\nto be found at runtime by the dynamic linker using `LD_LIBRARY_PATH`:\n```console\n$ LD_LIBRARY_PATH=../v8_src/v8/out/x64.release/ ./hello-world\n```\nNotice that this is using `ld` from our path. We can tell clang to use a different\nsearch path with the `-B` option:\n```console\n$ clang++ --help | grep -- '-B'\n  -B \u003cdir\u003e                Add \u003cdir\u003e to search path for binaries and object files used implicitly\n```\n\n`libgcc_s` is GCC low level runtime library. I've been confusing this with\nglibc++ libraries for some reason but they are not the same.\n\nRunning cctest:\n```console\n$ out.gn/learning/cctest test-heap-profiler/HeapSnapshotRetainedObjectInfo\n```\nTo get a list of the available tests:\n```console\n$ out.gn/learning/cctest --list\n```\n\nChecking formating/linting:\n```\n$ git cl format\n```\nYou can then `git diff` and see the changes.\n\nRunning pre-submit checks:\n```console\n$ git cl presubmit\n```\n\nThen upload using:\n```console\n$ git cl upload\n```\n\n#### Build details\nSo when we run gn it will generate Ninja build file. GN itself is written in \nC++ but has a python wrapper around it. \n\nA group in gn is just a collection of other targets which enables them to have\na name.\n\nSo when we run gn there will be a number of .ninja files generated. If we look\nin the root of the output directory we find two .ninja files:\n```console\nbuild.ninja  toolchain.ninja\n```\nBy default ninja will look for `build.ninja` and when we run ninja we usually\nspecify the `-C out/dir`. If no targets are specified on the command line ninja\nwill execute all outputs unless there is one specified as default. V8 has the \nfollowing default target:\n```\ndefault all\n\nbuild all: phony $\n    ./bytecode_builtins_list_generator $                                        \n    ./d8 $                                                                      \n    obj/fuzzer_support.stamp $                                                  \n    ./gen-regexp-special-case $                                                 \n    obj/generate_bytecode_builtins_list.stamp $                                 \n    obj/gn_all.stamp $                                                          \n    obj/json_fuzzer.stamp $                                                     \n    obj/lib_wasm_fuzzer_common.stamp $                                          \n    ./mksnapshot $                                                              \n    obj/multi_return_fuzzer.stamp $                                             \n    obj/parser_fuzzer.stamp $                                                   \n    obj/postmortem-metadata.stamp $                                             \n    obj/regexp_builtins_fuzzer.stamp $                                          \n    obj/regexp_fuzzer.stamp $                                                   \n    obj/run_gen-regexp-special-case.stamp $                                     \n    obj/run_mksnapshot_default.stamp $                                          \n    obj/run_torque.stamp $                                                      \n    ./torque $                                                                  \n    ./torque-language-server $                                                  \n    obj/torque_base.stamp $                                                     \n    obj/torque_generated_definitions.stamp $                                    \n    obj/torque_generated_initializers.stamp $                                   \n    obj/torque_ls_base.stamp $                                                  \n    ./libv8.so.TOC $                                                            \n    obj/v8_archive.stamp $\n    ...\n```\nA `phony` rule can be used to create an alias for other targets. \nThe `$` in ninja is an escape character so in the case of the all target it\nescapes the new line, like using \\ in a shell script.\n\nLets take a look at `bytecode_builtins_list_generator`: \n```\nbuild $:bytecode_builtins_list_generator: phony ./bytecode_builtins_list_generator\n```\nThe format of the ninja build statement is:\n```\nbuild outputs: rulename inputs\n```\nWe are again seeing the `$` ninja escape character but this time it is escaping\nthe colon which would otherwise be interpreted as separating file names. The output\nin this case is bytecode_builtins_list_generator. And I'm guessing, as I can't\nfind a connection between `./bytecode_builtins_list_generator` and \n\nThe default `target_out_dir` in this case is //out/x64.release_gcc/obj.\nThe executable in BUILD.gn which generates this does not specify any output\ndirectory so I'm assuming that it the generated .ninja file is place in the \ntarget_out_dir in this case where we can find `bytecode_builtins_list_generator.ninja` \nThis file has a label named:\n```\nlabel_name = bytecode_builtins_list_generator                                   \n```\nHmm, notice that in build.ninja there is the following command:\n```\nsubninja toolchain.ninja\n```\nAnd in `toolchain.ninja` we have:\n```\nsubninja obj/bytecode_builtins_list_generator.ninja\n```\nThis is what is making `./bytecode_builtins_list_generator` available.\n\n```console\n$ ninja -C out/x64.release_gcc/ -t targets all  | grep bytecode_builtins_list_generator\n$ rm out/x64.release_gcc/bytecode_builtins_list_generator \n$ ninja -C out/x64.release_gcc/ bytecode_builtins_list_generator\nninja: Entering directory `out/x64.release_gcc/'\n[1/1] LINK ./bytecode_builtins_list_generator\n```\n\nAlright, so I'd like to understand when in the process torque is run to\ngenerate classes like TorqueGeneratedStruct:\n```c++\nclass Struct : public TorqueGeneratedStruct\u003cStruct, HeapObject\u003e {\n```\n```\n./torque $                                                                  \n./torque-language-server $                                                  \nobj/torque_base.stamp $                                                     \nobj/torque_generated_definitions.stamp $                                    \nobj/torque_generated_initializers.stamp $                                   \nobj/torque_ls_base.stamp $  \n```\nLike before we can find that obj/torque.ninja in included by the subninja command\nin toolchain.ninja:\n```\nsubninja obj/torque.ninja\n```\nSo this is building the executable `torque`, but it has not been run yet.\n```console\n$ gn ls out/x64.release_gcc/ --type=action\n//:generate_bytecode_builtins_list\n//:postmortem-metadata\n//:run_gen-regexp-special-case\n//:run_mksnapshot_default\n//:run_torque\n//:v8_dump_build_config\n//src/inspector:protocol_compatibility\n//src/inspector:protocol_generated_sources\n//tools/debug_helper:gen_heap_constants\n//tools/debug_helper:run_mkgrokdump\n```\nNotice the `run_torque` target\n```console\n$ gn desc out/x64.release_gcc/ //:run_torque\n```\nIf we look in toolchain.ninja we have a rule named `___run_torque___build_toolchain_linux_x64__rule`\n```console\ncommand = python ../../tools/run.py ./torque -o gen/torque-generated -v8-root ../.. \n  src/builtins/array-copywithin.tq\n  src/builtins/array-every.tq\n  src/builtins/array-filter.tq\n  src/builtins/array-find.tq\n  ...\n```\nAnd there is a build that specifies the .h and cc files in gen/torque-generated\nwhich has this rule in it if they change.\n\n\n## Building chromium\nWhen making changes to V8 you might need to verify that your changes have not broken anything in Chromium. \n\nGenerate Your Project (gpy) :\nYou'll have to run this once before building:\n\n    $ gclient sync\n    $ gclient runhooks\n\n#### Update the code base\n\n    $ git fetch origin master\n    $ git co master\n    $ git merge origin/master\n\n### Building using GN\n\n    $ gn args out.gn/learning\n\n### Building using Ninja\n\n    $ ninja -C out.gn/learning \n\nBuilding the tests:\n\n    $ ninja -C out.gn/learning chrome/test:unit_tests\n\nAn error I got when building the first time:\n\n    traceback (most recent call last):\n    File \"./gyp-mac-tool\", line 713, in \u003cmodule\u003e\n      sys.exit(main(sys.argv[1:]))\n    File \"./gyp-mac-tool\", line 29, in main\n      exit_code = executor.Dispatch(args)\n    File \"./gyp-mac-tool\", line 44, in Dispatch\n      return getattr(self, method)(*args[1:])\n    File \"./gyp-mac-tool\", line 68, in ExecCopyBundleResource\n      self._CopyStringsFile(source, dest)\n    File \"./gyp-mac-tool\", line 134, in _CopyStringsFile\n      import CoreFoundation\n    ImportError: No module named CoreFoundation\n    [6642/20987] CXX obj/base/debug/base.task_annotator.o\n    [6644/20987] ACTION base_nacl: build newlib plib_9b4f41e4158ebb93a5d28e6734a13e85\n    ninja: build stopped: subcommand failed.\n\nI was able to get around this by:\n\n    $ pip install -U pyobjc\n\n#### Using a specific version of V8\nThe instructions below work but it is also possible to create a soft link from chromium/src/v8\nto local v8 repository and the build/test. \n\nSo, we want to include our updated version of V8 so that we can verify that it builds correctly with our change to V8.\nWhile I'm not sure this is the proper way to do it, I was able to update DEPS in src (chromium) and set\nthe v8 entry to git@github.com:danbev/v8.git@064718a8921608eaf9b5eadbb7d734ec04068a87:\n\n    \"git@github.com:danbev/v8.git@064718a8921608eaf9b5eadbb7d734ec04068a87\"\n\nYou'll have to run `gclient sync` after this. \n\nAnother way is to not updated the `DEPS` file, which is a version controlled file, but instead update\n`.gclientrc` and add a `custom_deps` entry:\n\n    solutions = [{u'managed': False, u'name': u'src', u'url': u'https://chromium.googlesource.com/chromium/src.git', \n    u'custom_deps': {\n      \"src/v8\": \"git@github.com:danbev/v8.git@27a666f9be7ca3959c7372bdeeee14aef2a4b7ba\"\n    }, u'deps_file': u'.DEPS.git', u'safesync_url': u''}]\n\n## Buiding pdfium\nYou may have to compile this project (in addition to chromium to verify that changes in v8 are not breaking\ncode in pdfium.\n\n### Create/clone the project\n\n     $ mkdir pdfuim_reop\n     $ gclient config --unmanaged https://pdfium.googlesource.com/pdfium.git\n     $ gclient sync\n     $ cd pdfium\n\n### Building\n\n    $ ninja -C out/Default\n\n#### Using a branch of v8\nYou should be able to update the .gclient file adding a custom_deps entry:\n\n    solutions = [\n    {\n      \"name\"        : \"pdfium\",\n      \"url\"         : \"https://pdfium.googlesource.com/pdfium.git\",\n      \"deps_file\"   : \"DEPS\",\n      \"managed\"     : False,\n      \"custom_deps\" : {\n        \"v8\": \"git@github.com:danbev/v8.git@064718a8921608eaf9b5eadbb7d734ec04068a87\"\n      },\n    },\n   ]\n   cache_dir = None\nYou'll have to run `gclient sync` after this too.\n\n\n\n\n## Code in this repo\n\n#### hello-world\n[hello-world](./hello-world.cc) is heavily commented and show the usage of a static int being exposed and\naccessed from JavaScript.\n\n#### instances\n[instances](./instances.cc) shows the usage of creating new instances of a C++ class from JavaScript.\n\n#### run-script\n[run-script](./run-script.cc) is basically the same as instance but reads an external file, [script.js](./script.js)\nand run the script.\n\n#### tests\nThe test directory contains unit tests for individual classes/concepts in V8 to help understand them.\n\n## Building this projects code\n\n    $ make\n\n## Running\n\n    $ ./hello-world\n\n## Cleaning\n\n    $ make clean\n\n## Contributing a change to V8\n1) Create a working branch using `git new-branch name`\n2) git cl upload  \n\nSee Googles [contributing-code](https://www.chromium.org/developers/contributing-code) for more details.\n\n### Find the current issue number\n\n    $ git cl issue\n\n## Debugging\n\n    $ lldb hello-world\n    (lldb) br s -f hello-world.cc -l 27\n\nThere are a number of useful functions in `src/objects-printer.cc` which can also be used in lldb.\n\n#### Print value of a Local object\n\n    (lldb) print _v8_internal_Print_Object(*(v8::internal::Object**)(*init_fn))\n\n#### Print stacktrace\n\n    (lldb) p _v8_internal_Print_StackTrace()\n\n#### Creating command aliases in lldb\nCreate a file named [.lldbinit](./.lldbinit) (in your project director or home directory). This file can now be found in v8's tools directory.\n\n\n\n### Using d8\nThis is the source used for the following examples:\n\n    $ cat class.js\n    function Person(name, age) {\n      this.name = name;\n      this.age = age;\n    }\n\n    print(\"before\");\n    const p = new Person(\"Daniel\", 41);\n    print(p.name);\n    print(p.age);\n    print(\"after\"); \n\n\n### V8_shell startup\nWhat happens when the v8_shell is run?   \n\n    $ lldb -- out/x64.debug/d8 --enable-inspector class.js\n    (lldb) breakpoint set --file d8.cc --line 2662\n    Breakpoint 1: where = d8`v8::Shell::Main(int, char**) + 96 at d8.cc:2662, address = 0x0000000100015150\n\nFirst v8::base::debug::EnableInProcessStackDumping() is called followed by some windows specific code guarded\nby macros. Next is all the options are set using `v8::Shell::SetOptions`\n\nSetOptions will call `v8::V8::SetFlagsFromCommandLine` which is found in src/api.cc:\n\n    i::FlagList::SetFlagsFromCommandLine(argc, argv, remove_flags);\n\nThis function can be found in src/flags.cc. The flags themselves are defined in src/flag-definitions.h\n\nNext a new SourceGroup array is create:\n    \n    options.isolate_sources = new SourceGroup[options.num_isolates];\n    SourceGroup* current = options.isolate_sources;\n    current-\u003eBegin(argv, 1);\n    for (int i = 1; i \u003c argc; i++) {\n      const char* str = argv[i];\n\n    (lldb) p str\n    (const char *) $6 = 0x00007fff5fbfed4d \"manual.js\"\n\nThere are then checks performed to see if the args is `--isolate` or `--module`, or `-e` and if not (like in our case)\n\n    } else if (strncmp(str, \"-\", 1) != 0) {\n      // Not a flag, so it must be a script to execute.\n      options.script_executed = true;\n\nTODO: I'm not exactly sure what SourceGroups are about but just noting this and will revisit later.\n\nThis will take us back `int Shell::Main` in src/d8.cc\n\n    ::V8::InitializeICUDefaultLocation(argv[0], options.icu_data_file);\n\n    (lldb) p argv[0]\n    (char *) $8 = 0x00007fff5fbfed48 \"./d8\"\n\nSee [ICU](international-component-for-unicode) a little more details.\n\nNext the default V8 platform is initialized:\n\n    g_platform = i::FLAG_verify_predictable ? new PredictablePlatform() : v8::platform::CreateDefaultPlatform();\n\nv8::platform::CreateDefaultPlatform() will be called in our case.\n\nWe are then back in Main and have the following lines:\n\n    2685 v8::V8::InitializePlatform(g_platform);\n    2686 v8::V8::Initialize();\n\nThis is very similar to what I've seen in the [Node.js startup process](https://github.com/danbev/learning-nodejs#startint-argc-char-argv).\n\nWe did not specify any natives_blob or snapshot_blob as an option on the command line so the defaults \nwill be used:\n\n    v8::V8::InitializeExternalStartupData(argv[0]);\n\nback in src/d8.cc line 2918:\n\n    Isolate* isolate = Isolate::New(create_params);\n\nthis call will bring us into api.cc line 8185:\n\n     i::Isolate* isolate = new i::Isolate(false);\nSo, we are invoking the Isolate constructor (in src/isolate.cc).\n\n    isolate-\u003eset_snapshot_blob(i::Snapshot::DefaultSnapshotBlob());\n\napi.cc:\n\n    isolate-\u003eInit(NULL);\n    \n    compilation_cache_ = new CompilationCache(this);\n    context_slot_cache_ = new ContextSlotCache();\n    descriptor_lookup_cache_ = new DescriptorLookupCache();\n    unicode_cache_ = new UnicodeCache();\n    inner_pointer_to_code_cache_ = new InnerPointerToCodeCache(this);\n    global_handles_ = new GlobalHandles(this);\n    eternal_handles_ = new EternalHandles();\n    bootstrapper_ = new Bootstrapper(this);\n    handle_scope_implementer_ = new HandleScopeImplementer(this);\n    load_stub_cache_ = new StubCache(this, Code::LOAD_IC);\n    store_stub_cache_ = new StubCache(this, Code::STORE_IC);\n    materialized_object_store_ = new MaterializedObjectStore(this);\n    regexp_stack_ = new RegExpStack();\n    regexp_stack_-\u003eisolate_ = this;\n    date_cache_ = new DateCache();\n    call_descriptor_data_ =\n      new CallInterfaceDescriptorData[CallDescriptors::NUMBER_OF_DESCRIPTORS];\n    access_compiler_data_ = new AccessCompilerData();\n    cpu_profiler_ = new CpuProfiler(this);\n    heap_profiler_ = new HeapProfiler(heap());\n    interpreter_ = new interpreter::Interpreter(this);\n    compiler_dispatcher_ =\n      new CompilerDispatcher(this, V8::GetCurrentPlatform(), FLAG_stack_size);\n\n\nsrc/builtins/builtins.cc, this is where the builtins are defined.\nTODO: sort out what these macros do.\n\nIn src/v8.cc we have a couple of checks for if the options passed are for a stress_run but since we \ndid not pass in any such flags this code path will be followed which will call RunMain:\n\n    result = RunMain(isolate, argc, argv, last_run);\n\nthis will end up calling:\n\n    options.isolate_sources[0].Execute(isolate);\n\nWhich will call SourceGroup::Execute(Isolate* isolate)\n\n    // Use all other arguments as names of files to load and run.\n    HandleScope handle_scope(isolate);\n    Local\u003cString\u003e file_name = String::NewFromUtf8(isolate, arg, NewStringType::kNormal).ToLocalChecked();\n    Local\u003cString\u003e source = ReadFile(isolate, arg);\n    if (source.IsEmpty()) {\n      printf(\"Error reading '%s'\\n\", arg);\n      Shell::Exit(1);\n    }\n    Shell::options.script_executed = true;\n    if (!Shell::ExecuteString(isolate, source, file_name, false, true)) {\n      exception_was_thrown = true;\n      break;\n    }\n\n    ScriptOrigin origin(name);\n    if (compile_options == ScriptCompiler::kNoCompileOptions) {\n      ScriptCompiler::Source script_source(source, origin);\n      return ScriptCompiler::Compile(context, \u0026script_source, compile_options);\n    }\n\nWhich will delegate to ScriptCompiler(Local\u003cContext\u003e, Source* source, CompileOptions options):\n\n    auto maybe = CompileUnboundInternal(isolate, source, options);\n\nCompileUnboundInternal\n\n    result = i::Compiler::GetSharedFunctionInfoForScript(\n        str, name_obj, line_offset, column_offset, source-\u003eresource_options,\n        source_map_url, isolate-\u003enative_context(), NULL, \u0026script_data, options,\n        i::NOT_NATIVES_CODE);\n\nsrc/compiler.cc\n\n    // Compile the function and add it to the cache.\n    ParseInfo parse_info(script);\n    Zone compile_zone(isolate-\u003eallocator(), ZONE_NAME);\n    CompilationInfo info(\u0026compile_zone, \u0026parse_info, Handle\u003cJSFunction\u003e::null());\n\n\nBack in src/compiler.cc-info.cc:\n\n    result = CompileToplevel(\u0026info);\n\n    (lldb) job *result\n    0x17df0df309f1: [SharedFunctionInfo]\n     - name = 0x1a7f12d82471 \u003cString[0]: \u003e\n     - formal_parameter_count = 0\n     - expected_nof_properties = 10\n     - ast_node_count = 23\n     - instance class name = #Object\n\n     - code = 0x1d8484d3661 \u003cCode: BUILTIN\u003e\n     - source code = function bajja(a, b, c) {\n      var d = c - 100;\n      return a + d * b;\n    }\n\n    var result = bajja(2, 2, 150);\n    print(result);\n\n     - anonymous expression\n     - function token position = -1\n     - start position = 0\n     - end position = 114\n     - no debug info\n     - length = 0\n     - optimized_code_map = 0x1a7f12d82241 \u003cFixedArray[0]\u003e\n     - feedback_metadata = 0x17df0df30d09: [FeedbackMetadata]\n     - length: 3\n     - slot_count: 11\n     Slot #0 LOAD_GLOBAL_NOT_INSIDE_TYPEOF_IC\n     Slot #2 kCreateClosure\n     Slot #3 LOAD_GLOBAL_NOT_INSIDE_TYPEOF_IC\n     Slot #5 CALL_IC\n     Slot #7 CALL_IC\n     Slot #9 LOAD_GLOBAL_NOT_INSIDE_TYPEOF_IC\n\n     - bytecode_array = 0x17df0df30c61\n\n\nBack in d8.cc:\n\n    maybe_result = script-\u003eRun(realm);\n\n\nsrc/api.cc\n\n    auto fun = i::Handle\u003ci::JSFunction\u003e::cast(Utils::OpenHandle(this));\n\n    (lldb) job *fun\n    0x17df0df30e01: [Function]\n     - map = 0x19cfe0003859 [FastProperties]\n     - prototype = 0x17df0df043b1\n     - elements = 0x1a7f12d82241 \u003cFixedArray[0]\u003e [FAST_HOLEY_ELEMENTS]\n     - initial_map =\n     - shared_info = 0x17df0df309f1 \u003cSharedFunctionInfo\u003e\n     - name = 0x1a7f12d82471 \u003cString[0]: \u003e\n     - formal_parameter_count = 0\n     - context = 0x17df0df03bf9 \u003cFixedArray[245]\u003e\n     - feedback vector cell = 0x17df0df30ed1 Cell for 0x17df0df30e49 \u003cFixedArray[13]\u003e\n     - code = 0x1d8484d3661 \u003cCode: BUILTIN\u003e\n     - properties = 0x1a7f12d82241 \u003cFixedArray[0]\u003e {\n        #length: 0x2c35a5718089 \u003cAccessorInfo\u003e (const accessor descriptor)\n        #name: 0x2c35a57180f9 \u003cAccessorInfo\u003e (const accessor descriptor)\n        #arguments: 0x2c35a5718169 \u003cAccessorInfo\u003e (const accessor descriptor)\n        #caller: 0x2c35a57181d9 \u003cAccessorInfo\u003e (const accessor descriptor)\n        #prototype: 0x2c35a5718249 \u003cAccessorInfo\u003e (const accessor descriptor)\n\n      }\n\n    i::Handle\u003ci::Object\u003e receiver = isolate-\u003eglobal_proxy();\n    Local\u003cValue\u003e result;\n    has_pending_exception = !ToLocal\u003cValue\u003e(i::Execution::Call(isolate, fun, receiver, 0, nullptr), \u0026result);\n\nsrc/execution.cc\n\n### Zone\nTaken directly from src/zone/zone.h:\n```\n// The Zone supports very fast allocation of small chunks of\n// memory. The chunks cannot be deallocated individually, but instead\n// the Zone supports deallocating all chunks in one fast\n// operation. The Zone is used to hold temporary data structures like\n// the abstract syntax tree, which is deallocated after compilation.\n```\n\n\n\n### V8 flags\n\n    $ ./d8 --help\n\n### d8\n\n    (lldb) br s -f d8.cc -l 2935\n\n    return v8::Shell::Main(argc, argv);\n\n    api.cc:6112\n    i::ReadNatives();\n    natives-external.cc\n\n### v8::String::NewFromOneByte\nSo I was a little confused when I first read this function name and thought it\nhad something to do with the length of the string. But the byte is the type\nof the chars that make up the string.\nFor example, a one byte char would be reinterpreted as uint8_t:\n\n    const char* data\n\n    reinterpret_cast\u003cconst uint8_t*\u003e(data)\n\n\n#### Tasks\n* gdbinit has been updated. Check if there is something that should be ported to lldbinit\n\n\n### Invocation walkthrough \nThis section will go through calling a Script to understand what happens in V8.\n\nI'll be using [run-scripts.cc](./run-scripts.cc) as the example for this.\n\n    $ lldb -- ./run-scripts\n    (lldb) br s -n main\n\nI'll step through until the following call:\n\n    script-\u003eRun(context).ToLocalChecked();\n\nSo, Script::Run is defined in api.cc\nFirst things that happens in this function is a macro:\n\n    PREPARE_FOR_EXECUTION_WITH_CONTEXT_IN_RUNTIME_CALL_STATS_SCOPE(\n         \"v8\", \n         \"V8.Execute\", \n         context, \n         Script, \n         Run, \n         MaybeLocal\u003cValue\u003e(),\n         InternalEscapableScope, \n    true);\n    TRACE_EVENT_CALL_STATS_SCOPED(isolate, category, name);\n    PREPARE_FOR_EXECUTION_GENERIC(isolate, context, class_name, function_name, \\\n        bailout_value, HandleScopeClass, do_callback);\n\nSo, what does the preprocessor replace this with then:\n\n    auto isolate = context.IsEmpty() ? i::Isolate::Current()                               : reinterpret_cast\u003ci::Isolate*\u003e(context-\u003eGetIsolate());\n\nI'm skipping TRACE_EVENT_CALL_STATS_SCOPED for now.\n`PREPARE_FOR_EXECUTION_GENERIC` will be replaced with:\n\n    if (IsExecutionTerminatingCheck(isolate)) {                        \\\n      return bailout_value;                                            \\\n    }                                                                  \\\n    HandleScopeClass handle_scope(isolate);                            \\\n    CallDepthScope\u003cdo_callback\u003e call_depth_scope(isolate, context);    \\\n    LOG_API(isolate, class_name, function_name);                       \\\n    ENTER_V8_DO_NOT_USE(isolate);                                      \\\n    bool has_pending_exception = false\n\n \n\n\n    auto fun = i::Handle\u003ci::JSFunction\u003e::cast(Utils::OpenHandle(this));\n\n    (lldb) job *fun\n    0x33826912c021: [Function]\n     - map = 0x1d0656c03599 [FastProperties]\n     - prototype = 0x338269102e69\n     - elements = 0x35190d902241 \u003cFixedArray[0]\u003e [FAST_HOLEY_ELEMENTS]\n     - initial_map =\n     - shared_info = 0x33826912bc11 \u003cSharedFunctionInfo\u003e\n     - name = 0x35190d902471 \u003cString[0]: \u003e\n     - formal_parameter_count = 0\n     - context = 0x338269102611 \u003cFixedArray[265]\u003e\n     - feedback vector cell = 0x33826912c139 \u003cCell value= 0x33826912c069 \u003cFixedArray[24]\u003e\u003e\n     - code = 0x1319e25fcf21 \u003cCode BUILTIN\u003e\n     - properties = 0x35190d902241 \u003cFixedArray[0]\u003e {\n        #length: 0x2e9d97ce68b1 \u003cAccessorInfo\u003e (const accessor descriptor)\n        #name: 0x2e9d97ce6921 \u003cAccessorInfo\u003e (const accessor descriptor)\n        #arguments: 0x2e9d97ce6991 \u003cAccessorInfo\u003e (const accessor descriptor)\n        #caller: 0x2e9d97ce6a01 \u003cAccessorInfo\u003e (const accessor descriptor)\n        #prototype: 0x2e9d97ce6a71 \u003cAccessorInfo\u003e (const accessor descriptor)\n     }\n\nThe code for i::JSFunction is generated in src/api.h. Lets take a closer look at this.\n\n    #define DECLARE_OPEN_HANDLE(From, To) \\\n      static inline v8::internal::Handle\u003cv8::internal::To\u003e \\\n      OpenHandle(const From* that, bool allow_empty_handle = false);\n\n    OPEN_HANDLE_LIST(DECLARE_OPEN_HANDLE)\n\nOPEN_HANDLE_LIST looks like this:\n\n    #define OPEN_HANDLE_LIST(V)                    \\\n    ....\n    V(Script, JSFunction)                        \\ \n\nSo lets expand this for JSFunction and it should become:\n\n      static inline v8::internal::Handle\u003cv8::internal::JSFunction\u003e \\\n        OpenHandle(const Script* that, bool allow_empty_handle = false);\n\nSo there will be an function named OpenHandle that will take a const pointer to Script.\n\nA little further down in src/api.h there is another macro which looks like this:\n\n    OPEN_HANDLE_LIST(MAKE_OPEN_HANDLE)\n\nMAKE_OPEN_HANDLE:\n```c++\n    #define MAKE_OPEN_HANDLE(From, To)\n      v8::internal::Handle\u003cv8::internal::To\u003e Utils::OpenHandle( \n      const v8::From* that, bool allow_empty_handle) {         \n      return v8::internal::Handle\u003cv8::internal::To\u003e(                         \n        reinterpret_cast\u003cv8::internal::Address*\u003e(const_cast\u003cv8::From*\u003e(that))); \n      }\n```\nAnd remember that JSFunction is included in the `OPEN_HANDLE_LIST` so there will\nbe the following in the source after the preprocessor has processed this header:\nA concrete example would look like this:\n```c++\nv8::internal::Handle\u003cv8::internal::JSFunction\u003e Utils::OpenHandle(\n    const v8::Script* that, bool allow_empty_handle) {\n  return v8::internal::Handle\u003cv8::internal::JSFunction\u003e(\n      reinterpret_cast\u003cv8::internal::Address*\u003e(const_cast\u003cv8::Script*\u003e(that))); }\n```\n\nYou can inspect the output of the preprocessor using:\n```console\n$ clang++ -I./out/x64.release/gen -I. -I./include -E src/api/api-inl.h \u003e api-inl.output\n```\n\n\nSo where is JSFunction declared? \nIt is defined in objects.h\n\n\n\n\n\n## Ignition interpreter\nUser JavaScript also needs to have bytecode generated for them and they also use\nthe C++ DLS and use the CodeStubAssembler -\u003e CodeAssembler -\u003e RawMachineAssembler\njust like builtins.\n\n## C++ Domain Specific Language (DLS)\n\n\n#### Build failure\nAfter rebasing I've seen the following issue:\n\n    $ ninja -C out/Debug chrome\n    ninja: Entering directory `out/Debug'\n    ninja: error: '../../chrome/renderer/resources/plugins/plugin_delay.html', needed by 'gen/chrome/grit/renderer_resources.h', missing and no known rule to make it\n\nThe \"solution\" was to remove the out directory and rebuild.\n\n### Tasks\nTo find suitable task you can use `label:HelpWanted` at [bugs.chromium.org](https://bugs.chromium.org/p/v8/issues/list?can=2\u0026q=label%3AHelpWanted+\u0026colspec=ID+Type+Status+Priority+Owner+Summary+HW+OS+Component+Stars\u0026x=priority\u0026y=owner\u0026cells=ids).\n\n\n### OpenHandle\nWhat does this call do: \n\n    Utils::OpenHandle(*(source-\u003esource_string));\n\n    OPEN_HANDLE_LIST(MAKE_OPEN_HANDLE)\n\nWhich is a macro defined in src/api.h:\n\n    #define MAKE_OPEN_HANDLE(From, To)                                             \\\n      v8::internal::Handle\u003cv8::internal::To\u003e Utils::OpenHandle(                    \\\n          const v8::From* that, bool allow_empty_handle) {                         \\\n      DCHECK(allow_empty_handle || that != NULL);                                \\\n      DCHECK(that == NULL ||                                                     \\\n           (*reinterpret_cast\u003cv8::internal::Object* const*\u003e(that))-\u003eIs##To()); \\\n      return v8::internal::Handle\u003cv8::internal::To\u003e(                             \\\n          reinterpret_cast\u003cv8::internal::To**\u003e(const_cast\u003cv8::From*\u003e(that)));    \\\n    }\n\n    OPEN_HANDLE_LIST(MAKE_OPEN_HANDLE)\n\nIf we take a closer look at the macro is should expand to something like this in our case:\n\n     v8::internal::Handle\u003cv8::internal::To\u003e Utils::OpenHandle(const v8:String* that, false) {\n       DCHECK(allow_empty_handle || that != NULL);                                \\\n       DCHECK(that == NULL ||                                                     \\\n           (*reinterpret_cast\u003cv8::internal::Object* const*\u003e(that))-\u003eIsString()); \\\n       return v8::internal::Handle\u003cv8::internal::String\u003e(                             \\\n          reinterpret_cast\u003cv8::internal::String**\u003e(const_cast\u003cv8::String*\u003e(that)));    \\\n     }\n\nSo this is returning a new v8::internal::Handle, the constructor is defined in src/handles.h:95.\n     \nsrc/objects.cc\nHandle\u003cWeakFixedArray\u003e WeakFixedArray::Add(Handle\u003cObject\u003e maybe_array,\n10167                                            Handle\u003cHeapObject\u003e value,\n10168                                            int* assigned_index) {\nNotice the name of the first parameter `maybe_array` but it is not of type maybe?\n\n### Context\nJavaScript provides a set of builtin functions and objects. These functions and\nobjects can be changed by user code. Each context is separate collection of\nthese objects and functions.\n\nAnd internal::Context is declared in `deps/v8/src/contexts.h` and extends FixedArray\n```console\nclass Context: public FixedArray {\n```\n\nA Context can be create by calling:\n```console\nconst v8::HandleScope handle_scope(isolate_);\nHandle\u003cContext\u003e context = Context::New(isolate_,\n                                       nullptr,\n                                       v8::Local\u003cv8::ObjectTemplate\u003e());\n```\n`Context::New` can be found in `src/api.cc:6405`:\n```c++\nLocal\u003cContext\u003e v8::Context::New(\n    v8::Isolate* external_isolate, v8::ExtensionConfiguration* extensions,\n    v8::MaybeLocal\u003cObjectTemplate\u003e global_template,\n    v8::MaybeLocal\u003cValue\u003e global_object,\n    DeserializeInternalFieldsCallback internal_fields_deserializer) {\n  return NewContext(external_isolate, extensions, global_template,\n                    global_object, 0, internal_fields_deserializer);\n}\n```\nThe declaration of this function can be found in `include/v8.h`:\n```c++\nstatic Local\u003cContext\u003e New(\n      Isolate* isolate, ExtensionConfiguration* extensions = NULL,\n      MaybeLocal\u003cObjectTemplate\u003e global_template = MaybeLocal\u003cObjectTemplate\u003e(),\n      MaybeLocal\u003cValue\u003e global_object = MaybeLocal\u003cValue\u003e(),\n      DeserializeInternalFieldsCallback internal_fields_deserializer =\n          DeserializeInternalFieldsCallback());\n```\nSo we can see the reason why we did not have to specify `internal_fields_deserialize`.\nWhat is `ExtensionConfiguration`?  \nThis class can be found in `include/v8.h` and only has two members, a count of the extension names \nand an array with the names.\n\nIf specified these will be installed by `Boostrapper::InstallExtensions` which will delegate to \n`Genesis::InstallExtensions`, both can be found in `src/boostrapper.cc`.\nWhere are extensions registered?   \nThis is done once per process and called from `V8::Initialize()`:\n```c++\nvoid Bootstrapper::InitializeOncePerProcess() {\n  free_buffer_extension_ = new FreeBufferExtension;\n  v8::RegisterExtension(free_buffer_extension_);\n  gc_extension_ = new GCExtension(GCFunctionName());\n  v8::RegisterExtension(gc_extension_);\n  externalize_string_extension_ = new ExternalizeStringExtension;\n  v8::RegisterExtension(externalize_string_extension_);\n  statistics_extension_ = new StatisticsExtension;\n  v8::RegisterExtension(statistics_extension_);\n  trigger_failure_extension_ = new TriggerFailureExtension;\n  v8::RegisterExtension(trigger_failure_extension_);\n  ignition_statistics_extension_ = new IgnitionStatisticsExtension;\n  v8::RegisterExtension(ignition_statistics_extension_);\n}\n```\nThe extensions can be found in `src/extensions`. You register your own extensions and an example of this\ncan be found in [test/context_test.cc](./test/context_test.cc).\n\n\n```console\n(lldb) br s -f node.cc -l 4439\n(lldb) expr context-\u003elength()\n(int) $522 = 281\n```\nThis output was taken\n\nCreating a new Context is done by `v8::CreateEnvironment`\n```console\n(lldb) br s -f api.cc -l 6565\n```\n```c++\nInvokeBootstrapper\u003cObjectType\u003e invoke;\n   6635    result =\n-\u003e 6636        invoke.Invoke(isolate, maybe_proxy, proxy_template, extensions,\n   6637                      context_snapshot_index, embedder_fields_deserializer);\n```\nThis will later end up in `Snapshot::NewContextFromSnapshot`:\n```c++\nVector\u003cconst byte\u003e context_data =\n      ExtractContextData(blob, static_cast\u003cuint32_t\u003e(context_index));\n  SnapshotData snapshot_data(context_data);\n\n  MaybeHandle\u003cContext\u003e maybe_result = PartialDeserializer::DeserializeContext(\n      isolate, \u0026snapshot_data, can_rehash, global_proxy,\n      embedder_fields_deserializer);\n```\nSo we can see here that the Context is deserialized from the snapshot. What does the Context contain at this stage:\n```console\n(lldb) expr result-\u003elength()\n(int) $650 = 281\n(lldb) expr result-\u003ePrint()\n// not inlcuding the complete output\n```\nLets take a look at an entry:\n```console\n(lldb) expr result-\u003eget(0)-\u003ePrint()\n0xc201584331: [Function] in OldSpace\n - map = 0xc24c002251 [FastProperties]\n - prototype = 0xc201584371\n - elements = 0xc2b2882251 \u003cFixedArray[0]\u003e [HOLEY_ELEMENTS]\n - initial_map =\n - shared_info = 0xc2b2887521 \u003cSharedFunctionInfo\u003e\n - name = 0xc2b2882441 \u003cString[0]: \u003e\n - formal_parameter_count = -1\n - kind = [ NormalFunction ]\n - context = 0xc201583a59 \u003cFixedArray[281]\u003e\n - code = 0x2df1f9865a61 \u003cCode BUILTIN\u003e\n - source code = () {}\n - properties = 0xc2b2882251 \u003cFixedArray[0]\u003e {\n    #length: 0xc2cca83729 \u003cAccessorInfo\u003e (const accessor descriptor)\n    #name: 0xc2cca83799 \u003cAccessorInfo\u003e (const accessor descriptor)\n    #arguments: 0xc201587fd1 \u003cAccessorPair\u003e (const accessor descriptor)\n    #caller: 0xc201587fd1 \u003cAccessorPair\u003e (const accessor descriptor)\n    #constructor: 0xc201584c29 \u003cJSFunction Function (sfi = 0xc2b28a6fb1)\u003e (const data descriptor)\n    #apply: 0xc201588079 \u003cJSFunction apply (sfi = 0xc2b28a7051)\u003e (const data descriptor)\n    #bind: 0xc2015880b9 \u003cJSFunction bind (sfi = 0xc2b28a70f1)\u003e (const data descriptor)\n    #call: 0xc2015880f9 \u003cJSFunction call (sfi = 0xc2b28a7191)\u003e (const data descriptor)\n    #toString: 0xc201588139 \u003cJSFunction toString (sfi = 0xc2b28a7231)\u003e (const data descriptor)\n    0xc2b28bc669 \u003cSymbol: Symbol.hasInstance\u003e: 0xc201588179 \u003cJSFunction [Symbol.hasInstance] (sfi = 0xc2b28a72d1)\u003e (const data descriptor)\n }\n\n - feedback vector: not available\n```\nSo we can see that this is of type `[Function]` which we can cast using:\n```\n(lldb) expr JSFunction::cast(result-\u003eget(0))-\u003ecode()-\u003ePrint()\n0x2df1f9865a61: [Code]\nkind = BUILTIN\nname = EmptyFunction\n```\n\n```console\n(lldb) expr JSFunction::cast(result-\u003eclosure())-\u003ePrint()\n0xc201584331: [Function] in OldSpace\n - map = 0xc24c002251 [FastProperties]\n - prototype = 0xc201584371\n - elements = 0xc2b2882251 \u003cFixedArray[0]\u003e [HOLEY_ELEMENTS]\n - initial_map =\n - shared_info = 0xc2b2887521 \u003cSharedFunctionInfo\u003e\n - name = 0xc2b2882441 \u003cString[0]: \u003e\n - formal_parameter_count = -1\n - kind = [ NormalFunction ]\n - context = 0xc201583a59 \u003cFixedArray[281]\u003e\n - code = 0x2df1f9865a61 \u003cCode BUILTIN\u003e\n - source code = () {}\n - properties = 0xc2b2882251 \u003cFixedArray[0]\u003e {\n    #length: 0xc2cca83729 \u003cAccessorInfo\u003e (const accessor descriptor)\n    #name: 0xc2cca83799 \u003cAccessorInfo\u003e (const accessor descriptor)\n    #arguments: 0xc201587fd1 \u003cAccessorPair\u003e (const accessor descriptor)\n    #caller: 0xc201587fd1 \u003cAccessorPair\u003e (const accessor descriptor)\n    #constructor: 0xc201584c29 \u003cJSFunction Function (sfi = 0xc2b28a6fb1)\u003e (const data descriptor)\n    #apply: 0xc201588079 \u003cJSFunction apply (sfi = 0xc2b28a7051)\u003e (const data descriptor)\n    #bind: 0xc2015880b9 \u003cJSFunction bind (sfi = 0xc2b28a70f1)\u003e (const data descriptor)\n    #call: 0xc2015880f9 \u003cJSFunction call (sfi = 0xc2b28a7191)\u003e (const data descriptor)\n    #toString: 0xc201588139 \u003cJSFunction toString (sfi = 0xc2b28a7231)\u003e (const data descriptor)\n    0xc2b28bc669 \u003cSymbol: Symbol.hasInstance\u003e: 0xc201588179 \u003cJSFunction [Symbol.hasInstance] (sfi = 0xc2b28a72d1)\u003e (const data descriptor)\n }\n\n - feedback vector: not available\n```\nSo this is the JSFunction associated with the deserialized context. Not sure what this is about as looking at the source code it looks like\nan empty function. A function can also be set on the context so I'm guessing that this give access to the function of a context once set.\nWhere is function set, well it is probably deserialized but we can see it be used in `deps/v8/src/bootstrapper.cc`:\n```c++\n{\n  Handle\u003cJSFunction\u003e function = SimpleCreateFunction(isolate, factory-\u003eempty_string(), Builtins::kAsyncFunctionAwaitCaught, 2, false);\n  native_context-\u003eset_async_function_await_caught(*function);\n}\n​```console\n(lldb) expr isolate()-\u003ebuiltins()-\u003ebuiltin_handle(Builtins::Name::kAsyncFunctionAwaitCaught)-\u003ePrint()\n```\n\n`Context::Scope` is a RAII class used to Enter/Exit a context. Lets take a closer look at `Enter`:\n```c++\nvoid Context::Enter() {\n  i::Handle\u003ci::Context\u003e env = Utils::OpenHandle(this);\n  i::Isolate* isolate = env-\u003eGetIsolate();\n  ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);\n  i::HandleScopeImplementer* impl = isolate-\u003ehandle_scope_implementer();\n  impl-\u003eEnterContext(env);\n  impl-\u003eSaveContext(isolate-\u003econtext());\n  isolate-\u003eset_context(*env);\n}\n```\nSo the current context is saved and then the this context `env` is set as the current on the isolate.\n`EnterContext` will push the passed-in context (deps/v8/src/api.cc):\n```c++\nvoid HandleScopeImplementer::EnterContext(Handle\u003cContext\u003e context) {\n  entered_contexts_.push_back(*context);\n}\n...\nDetachableVector\u003cContext*\u003e entered_contexts_;\n```\n```c++\nDetachableVector is a delegate/adaptor with some additonaly features on a std::vector.\nHandle\u003cContext\u003e context1 = NewContext(isolate);\nHandle\u003cContext\u003e context2 = NewContext(isolate);\nContext::Scope context_scope1(context1);        // entered_contexts_ [context1], saved_contexts_[isolateContext]\nContext::Scope context_scope2(context2);        // entered_contexts_ [context1, context2], saved_contexts[isolateContext, context1]\n```\n\nNow, `SaveContext` is using the current context, not `this` context (`env`) and pushing that to the end of the saved_contexts_ vector.\nWe can look at this as we entered context_scope2 from context_scope1:\n\n\nAnd `Exit` looks like:\n```c++\nvoid Context::Exit() {\n  i::Handle\u003ci::Context\u003e env = Utils::OpenHandle(this);\n  i::Isolate* isolate = env-\u003eGetIsolate();\n  ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);\n  i::HandleScopeImplementer* impl = isolate-\u003ehandle_scope_implementer();\n  if (!Utils::ApiCheck(impl-\u003eLastEnteredContextWas(env),\n                       \"v8::Context::Exit()\",\n                       \"Cannot exit non-entered context\")) {\n    return;\n  }\n  impl-\u003eLeaveContext();\n  isolate-\u003eset_context(impl-\u003eRestoreContext());\n}\n```\n\n\n#### EmbedderData\nA context can have embedder data set on it. Like decsribed above a Context is\ninternally A FixedArray. `SetEmbedderData` in Context is implemented in `src/api.cc`:\n```c++\nconst char* location = \"v8::Context::SetEmbedderData()\";\ni::Handle\u003ci::FixedArray\u003e data = EmbedderDataFor(this, index, true, location);\ni::Handle\u003ci::FixedArray\u003e data(env-\u003eembedder_data());\n```\n`location` is only used for logging and we can ignore it for now.\n`EmbedderDataFor`:\n```c++\ni::Handle\u003ci::Context\u003e env = Utils::OpenHandle(context);\n...\ni::Handle\u003ci::FixedArray\u003e data(env-\u003eembedder_data());\n```\nWe can find `embedder_data` in `src/contexts-inl.h`\n\n```c++\n#define NATIVE_CONTEXT_FIELD_ACCESSORS(index, type, name) \\\n  inline void set_##name(type* value);                    \\\n  inline bool is_##name(type* value) const;               \\\n  inline type* name() const;\n  NATIVE_CONTEXT_FIELDS(NATIVE_CONTEXT_FIELD_ACCESSORS)\n```\nAnd `NATIVE_CONTEXT_FIELDS` in context.h:\n```c++\n#define NATIVE_CONTEXT_FIELDS(V)                                               \\\n  V(GLOBAL_PROXY_INDEX, JSObject, global_proxy_object)                         \\\n  V(EMBEDDER_DATA_INDEX, FixedArray, embedder_data)                            \\\n...\n\n#define NATIVE_CONTEXT_FIELD_ACCESSORS(index, type, name) \\\n  void Context::set_##name(type* value) {                 \\\n    DCHECK(IsNativeContext());                            \\\n    set(index, value);                                    \\\n  }                                                       \\\n  bool Context::is_##name(type* value) const {            \\\n    DCHECK(IsNativeContext());                            \\\n    return type::cast(get(index)) == value;               \\\n  }                                                       \\\n  type* Context::name() const {                           \\\n    DCHECK(IsNativeContext());                            \\\n    return type::cast(get(index));                        \\\n  }\nNATIVE_CONTEXT_FIELDS(NATIVE_CONTEXT_FIELD_ACCESSORS)\n#undef NATIVE_CONTEXT_FIELD_ACCESSORS\n```\nSo the preprocessor would expand this to:\n```c++\nFixedArray embedder_data() const;\n\nvoid Context::set_embedder_data(FixedArray value) {\n  DCHECK(IsNativeContext());\n  set(EMBEDDER_DATA_INDEX, value);\n}\n\nbool Context::is_embedder_data(FixedArray value) const {\n  DCHECK(IsNativeContext());\n  return FixedArray::cast(get(EMBEDDER_DATA_INDEX)) == value;\n}\n\nFixedArray Context::embedder_data() const {\n  DCHECK(IsNativeContext());\n  return FixedArray::cast(get(EMBEDDER_DATA_INDEX));\n}\n```\nWe can take a look at the initial data:\n```console\nlldb) expr data-\u003ePrint()\n0x2fac3e896439: [FixedArray] in OldSpace\n - map = 0x2fac9de82341 \u003cMap(HOLEY_ELEMENTS)\u003e\n - length: 3\n         0-2: 0x2fac1cb822e1 \u003cundefined\u003e\n(lldb) expr data-\u003elength()\n(int) $5 = 3\n```\nAnd after setting:\n```console\n(lldb) expr data-\u003ePrint()\n0x2fac3e896439: [FixedArray] in OldSpace\n - map = 0x2fac9de82341 \u003cMap(HOLEY_ELEMENTS)\u003e\n - length: 3\n           0: 0x2fac20c866e1 \u003cString[7]: embdata\u003e\n         1-2: 0x2fac1cb822e1 \u003cundefined\u003e\n\n(lldb) expr v8::internal::String::cast(data-\u003eget(0))-\u003ePrint()\n\"embdata\"\n```\nThis was taken while debugging [ContextTest::EmbedderData](./test/context_test.cc).\n\n### ENTER_V8_FOR_NEW_CONTEXT\nThis macro is used in `CreateEnvironment` (src/api.cc) and the call in this function looks like this:\n```c++\nENTER_V8_FOR_NEW_CONTEXT(isolate);\n```\n\n\n### Factory::NewMap\nThis section will take a look at the following call:\n```c++\ni::Handle\u003ci::Map\u003e map = factory-\u003eNewMap(i::JS_OBJECT_TYPE, 24);\n```\n\nLets take a closer look at this function which can be found in `src/factory.cc`:\n```\nHandle\u003cMap\u003e Factory::NewMap(InstanceType type, int instance_size,\n                            ElementsKind elements_kind,\n                            int inobject_properties) {\n  CALL_HEAP_FUNCTION(\n      isolate(),\n      isolate()-\u003eheap()-\u003eAllocateMap(type, instance_size, elements_kind,\n                                     inobject_properties),\n      Map);\n}\n\n```\nIf we take a look at factory.h we can see the default values for elements_kind and inobject_properties:\n```c++\nHandle\u003cMap\u003e NewMap(InstanceType type, int instance_size,\n                     ElementsKind elements_kind = TERMINAL_FAST_ELEMENTS_KIND,\n                     int inobject_properties = 0);\n```\nIf we expand the CALL_HEAP_FUNCTION macro we will get:\n```c++\n    AllocationResult __allocation__ = isolate()-\u003eheap()-\u003eAllocateMap(type,\n                                                                     instance_size,\n                                                                     elements_kind,\n                                                                     inobject_properties),\n    Object* __object__ = nullptr;\n    RETURN_OBJECT_UNLESS_RETRY(isolate(), Map)\n    /* Two GCs before panicking.  In newspace will almost always succeed. */\n    for (int __i__ = 0; __i__ \u003c 2; __i__++) {\n      (isolate())-\u003eheap()-\u003eCollectGarbage(\n          __allocation__.RetrySpace(),\n          GarbageCollectionReason::kAllocationFailure);\n      __allocation__ = FUNCTION_CALL;\n      RETURN_OBJECT_UNLESS_RETRY(isolate, Map)\n    }\n    (isolate())-\u003ecounters()-\u003egc_last_resort_from_handles()-\u003eIncrement();\n    (isolate())-\u003eheap()-\u003eCollectAllAvailableGarbage(\n        GarbageCollectionReason::kLastResort);\n    {\n      AlwaysAllocateScope __scope__(isolate());\n    t __allocation__ = isolate()-\u003eheap()-\u003eAllocateMap(type,\n                                                      instance_size,\n                                                      elements_kind,\n                                                      inobject_properties),\n    }\n    RETURN_OBJECT_UNLESS_RETRY(isolate, Map)\n    /* TODO(1181417): Fix this. */\n    v8::internal::Heap::FatalProcessOutOfMemory(\"CALL_AND_RETRY_LAST\", true);\n    return Handle\u003cMap\u003e();\n```\nSo, lets take a look at `isolate()-\u003eheap()-\u003eAllocateMap` in 'src/heap/heap.cc':\n```c++\n  HeapObject* result = nullptr;\n  AllocationResult allocation = AllocateRaw(Map::kSize, MAP_SPACE);\n```\n`AllocateRaw` can be found in src/heap/heap-inl.h:\n```c++\n  bool large_object = size_in_bytes \u003e kMaxRegularHeapObjectSize;\n  HeapObject* object = nullptr;\n  AllocationResult allocation;\n  if (NEW_SPACE == space) {\n    if (large_object) {\n      space = LO_SPACE;\n    } else {\n      allocation = new_space_-\u003eAllocateRaw(size_in_bytes, alignment);\n      if (allocation.To(\u0026object)) {\n        OnAllocationEvent(object, size_in_bytes);\n      }\n      return allocation;\n    }\n  }\n } else if (MAP_SPACE == space) {\n    allocation = map_space_-\u003eAllocateRawUnaligned(size_in_bytes);\n }\n\n```\n```console\n(lldb) expr large_object\n(bool) $3 = false\n(lldb) expr size_in_bytes\n(int) $5 = 80\n(lldb) expr map_space_\n(v8::internal::MapSpace *) $6 = 0x0000000104700f60\n```\n`AllocateRawUnaligned` can be found in `src/heap/spaces-inl.h`\n```c++\n  HeapObject* object = AllocateLinearly(size_in_bytes);\n```\n\n### v8::internal::Object\nIs an abstract super class for all classes in the object hierarch and both Smi and HeapObject\nare subclasses of Object so there are no data members in object only functions.\nFor example:\n```\n  bool IsObject() const { return true; }\n  INLINE(bool IsSmi() const\n  INLINE(bool IsLayoutDescriptor() const\n  INLINE(bool IsHeapObject() const\n  INLINE(bool IsPrimitive() const\n  INLINE(bool IsNumber() const\n  INLINE(bool IsNumeric() const\n  INLINE(bool IsAbstractCode() const\n  INLINE(bool IsAccessCheckNeeded() const\n  INLINE(bool IsArrayList() const\n  INLINE(bool IsBigInt() const\n  INLINE(bool IsUndefined() const\n  INLINE(bool IsNull() const\n  INLINE(bool IsTheHole() const\n  INLINE(bool IsException() const\n  INLINE(bool IsUninitialized() const\n  INLINE(bool IsTrue() const\n  INLINE(bool IsFalse() const\n  ...\n```\n\n### v8::internal::Smi\nExtends v8::internal::Object and are not allocated on the heap. There are no members as the\npointer itself is used to store the information.\n\n\n\n\nIn our case the calling v8::Isolate::New which is done by the test fixture: \n```c++\nvirtual void SetUp() {\n  isolate_ = v8::Isolate::New(create_params_);\n}\n```\nThis will call: \n```c++\nIsolate* Isolate::New(const Isolate::CreateParams\u0026 params) {\n  Isolate* isolate = Allocate();\n  Initialize(isolate, params);\n  return isolate;\n}\n```\nIn `Isolate::Initialize` we'll call `i::Snapshot::Initialize(i_isolate)`:\n```c++\nif (params.entry_hook || !i::Snapshot::Initialize(i_isolate)) {\n  ...\n```\nWhich will call:\n```c++\nbool success = isolate-\u003eInit(\u0026deserializer);\n```\nBefore this call all the roots are uninitialized. Reading this [blog](https://v8project.blogspot.com/) it says that\nthe Isolate class contains a roots table. It looks to me that the Heap contains this data structure but perhaps that\nis what they meant. \n```console\n(lldb) bt 3\n* thread #1, queue = 'com.apple.main-thread', stop reason = step over\n  * frame #0: 0x0000000101584f43 libv8.dylib`v8::internal::StartupDeserializer::DeserializeInto(this=0x00007ffeefbfe200, isolate=0x000000010481cc00) at startup-deserializer.cc:39\n    frame #1: 0x0000000101028bb6 libv8.dylib`v8::internal::Isolate::Init(this=0x000000010481cc00, des=0x00007ffeefbfe200) at isolate.cc:3036\n    frame #2: 0x000000010157c682 libv8.dylib`v8::internal::Snapshot::Initialize(isolate=0x000000010481cc00) at snapshot-common.cc:54\n```\nIn `startup-deserializer.cc` we can find `StartupDeserializer::DeserializeInto`:\n```c++\n  DisallowHeapAllocation no_gc;\n  isolate-\u003eheap()-\u003eIterateSmiRoots(this);\n  isolate-\u003eheap()-\u003eIterateStrongRoots(this, VISIT_ONLY_STRONG);\n```\nAfter \nIf we take a look in `src/roots.h` we can find the read-only roots in Heap. If we take the 10 value, which is:\n```c++\nV(String, empty_string, empty_string)                                        \\\n```\nwe can then inspect this value:\n```console\n(lldb) expr roots_[9]\n(v8::internal::Object *) $32 = 0x0000152d30b82851\n(lldb) expr roots_[9]-\u003eIsString()\n(bool) $30 = true\n(lldb) expr roots_[9]-\u003ePrint()\n#\n```\nSo this entry is a pointer to objects on the managed heap which have been deserialized from the snapshot.\n\nThe heap class has a lot of members that are initialized during construction by the body of the constructor looks like this:\n```c++\n{\n  // Ensure old_generation_size_ is a multiple of kPageSize.\n  DCHECK_EQ(0, max_old_generation_size_ \u0026 (Page::kPageSize - 1));\n\n  memset(roots_, 0, sizeof(roots_[0]) * kRootListLength);\n  set_native_contexts_list(nullptr);\n  set_allocation_sites_list(Smi::kZero);\n  set_encountered_weak_collections(Smi::kZero);\n  // Put a dummy entry in the remembered pages so we can find the list the\n  // minidump even if there are no real unmapped pages.\n  RememberUnmappedPage(nullptr, false);\n}\n```\nWe can see that roots_ is filled with 0 values. We can inspect `roots_` using:\n```console\n(lldb) expr roots_\n(lldb) expr RootListIndex::kRootListLength\n(int) $16 = 509\n```\nNow they are all 0 at this stage, so when will this array get populated?  \nThese will happen in `Isolate::Init`:\n```c++\n  heap_.SetUp()\n  if (!create_heap_objects) des-\u003eDeserializeInto(this);\n\nvoid StartupDeserializer::DeserializeInto(Isolate* isolate) {\n-\u003e 17    Initialize(isolate);\nstartup-deserializer.cc:37\n\nisolate-\u003eheap()-\u003eIterateSmiRoots(this);\n```\n\nThis will delegate to `ConfigureHeapDefaults()` which will call Heap::ConfigureHeap:\n```c++\nenum RootListIndex {\n  kFreeSpaceMapRootIndex,\n  kOnePointerFillerMapRootIndex,\n  ...\n}\n```\n\n```console\n(lldb) expr heap-\u003eRootListIndex::kFreeSpaceMapRootIndex\n(int) $3 = 0\n(lldb) expr heap-\u003eRootListIndex::kOnePointerFillerMapRootIndex\n(int) $4 = 1\n```\n\n### MemoryChunk\nFound in `src/heap/spaces.h` an instace of a MemoryChunk represents a region in memory that is \nowned by a specific space.\n\n\n### Embedded builtins\nIn the [blog post](https://v8project.blogspot.com/) explains how the builtins are embedded into the executable\nin to the .TEXT section which is readonly and therefore can be shared amoung multiple processes. We know that\nbuiltins are compiled and stored in the snapshot but now it seems that the are instead placed in to `out.gn/learning/gen/embedded.cc` and the combined with the object files from the compile to produce the libv8.dylib.\nV8 has a configuration option named `v8_enable_embedded_builtins` which which case `embedded.cc` will be added \nto the list of sources. This is done in `BUILD.gn` and the `v8_snapshot` target. If `v8_enable_embedded_builtins` is false then `src/snapshot/embedded-empty.cc` will be included instead. Both of these files have the following functions:\n```c++\nconst uint8_t* DefaultEmbeddedBlob()\nuint32_t DefaultEmbeddedBlobSize()\n\n#ifdef V8_MULTI_SNAPSHOTS\nconst uint8_t* TrustedEmbeddedBlob()\nuint32_t TrustedEmbeddedBlobSize()\n#endif\n```\nThese functions are used by `isolate.cc` and declared `extern`:\n```c++\nextern const uint8_t* DefaultEmbeddedBlob();\nextern uint32_t DefaultEmbeddedBlobSize();\n```\nAnd the usage of `DefaultEmbeddedBlob` can be see in Isolate::Isolate where is sets the embedded blob:\n```c++\nSetEmbeddedBlob(DefaultEmbeddedBlob(), DefaultEmbeddedBlobSize());\n```\nLets set a break point there and see if this is empty of not.\n```console\n(lldb) expr v8_embedded_blob_size_\n(uint32_t) $0 = 4021088\n```\nSo we can see that we are not using the empty one. Isolate::SetEmbeddedBlob\n\nWe can see in `src/snapshot/deserializer.cc` (line 552) we have a check for the embedded_blob():\n```c++\n  CHECK_NOT_NULL(isolate-\u003eembedded_blob());\n  EmbeddedData d = EmbeddedData::FromBlob();\n  Address address = d.InstructionStartOfBuiltin(builtin_index);\n```\n`EmbeddedData can be found in `src/snapshot/snapshot.h` and the implementation can be found in snapshot-common.cc.\n```c++\nAddress EmbeddedData::InstructionStartOfBuiltin(int i) const {\n  const struct Metadata* metadata = Metadata();\n  const uint8_t* result = RawData() + metadata[i].instructions_offset;\n  return reinterpret_cast\u003cAddress\u003e(result);\n}\n```\n```console\n(lldb) expr *metadata\n(const v8::internal::EmbeddedData::Metadata) $7 = (instructions_offset = 0, instructions_length = 1464)\n```\n```c++\n  struct Metadata {\n    // Blob layout information.\n    uint32_t instructions_offset;\n    uint32_t instructions_length;\n  };\n```\n```console\n(lldb) expr *this\n(v8::internal::EmbeddedData) $10 = (data_ = \"\\xffffffdc\\xffffffc0\\xffffff88'\"y[\\xffffffd6\", size_ = 4021088)\n(lldb) expr metadata[i]\n(const v8::internal::EmbeddedData::Metadata) $8 = (instructions_offset = 0, instructions_length = 1464)\n```\nSo, is it possible for us to verify that this information is in the .text section?\n```console\n(lldb) expr result\n(const uint8_t *) $13 = 0x0000000101b14ee0 \"UH\\x89�jH\\x83�(H\\x89U�H�\\x16H\\x89}�H�u�H�E�H\\x89U�H\\x83�\n(lldb) image lookup --address 0x0000000101b14ee0 --verbose\n      Address: libv8.dylib[0x00000000019cdee0] (libv8.dylib.__TEXT.__text + 27054464)\n      Summary: libv8.dylib`v8_Default_embedded_blob_ + 7072\n       Module: file = \"/Users/danielbevenius/work/google/javascript/v8/out.gn/learning/libv8.dylib\", arch = \"x86_64\"\n       Symbol: id = {0x0004b596}, range = [0x0000000101b13340-0x0000000101ee8ea0), name=\"v8_Default_embedded_blob_\"\n```\nSo what we have is a pointer to the .text segment which is returned:\n```console\n(lldb) memory read -f x -s 1 -c 13 0x0000000101b14ee0\n0x101b14ee0: 0x55 0x48 0x89 0xe5 0x6a 0x18 0x48 0x83\n0x101b14ee8: 0xec 0x28 0x48 0x89 0x55\n```\nAnd we can compare this with `out.gn/learning/gen/embedded.cc`:\n```c++\nV8_EMBEDDED_TEXT_HEADER(v8_Default_embedded_blob_)\n__asm__(\n  ...\n  \".byte 0x55,0x48,0x89,0xe5,0x6a,0x18,0x48,0x83,0xec,0x28,0x48,0x89,0x55\\n\"\n  ...\n);\n```\nThe macro `V8_EMBEDDED_TEXT_HEADER` can be found `src/snapshot/macros.h`:\n```c++\n#define V8_EMBEDDED_TEXT_HEADER(LABEL)         \\\n  __asm__(V8_ASM_DECLARE(#LABEL)               \\\n          \".csect \" #LABEL \"[DS]\\n\"            \\\n          #LABEL \":\\n\"                         \\\n          \".llong .\" #LABEL \", TOC[tc0], 0\\n\"  \\\n          V8_ASM_TEXT_SECTION                  \\\n          \".\" #LABEL \":\\n\");\n\ndefine V8_ASM_DECLARE(NAME) \".private_extern \" V8_ASM_MANGLE_LABEL NAME \"\\n\"\n#define V8_ASM_MANGLE_LABEL \"_\"\n#define V8_ASM_TEXT_SECTION \".csect .text[PR]\\n\"\n```\nAnd would be expanded by the preprocessor into:\n```c++\n  __asm__(\".private_extern \" _ v8_Default_embedded_blob_ \"\\n\"\n          \".csect \" v8_Default_embedded_blob_ \"[DS]\\n\"\n          v8_Default_embedded_blob_ \":\\n\"\n          \".llong .\" v8_Default_embedded_blob_ \", TOC[tc0], 0\\n\"\n          \".csect .text[PR]\\n\"\n          \".\" v8_Default_embedded_blob_ \":\\n\");\n  __asm__(\n    ...\n    \".byte 0x55,0x48,0x89,0xe5,0x6a,0x18,0x48,0x83,0xec,0x28,0x48,0x89,0x55\\n\"\n    ...\n  );\n\n```\n\nBack in `src/snapshot/deserialzer.cc` we are on this line:\n```c++\n  Address address = d.InstructionStartOfBuiltin(builtin_index);\n  CHECK_NE(kNullAddress, address);\n  if (RelocInfo::OffHeapTargetIsCodedSpecially()) {\n    // is false in our case so skipping the code here\n  } else {\n    MaybeObject* o = reinterpret_cast\u003cMaybeObject*\u003e(address);\n    UnalignedCopy(current, \u0026o);\n    current++;\n  }\n  break;\n```\n\n### print-code\n```console\n$ ./d8 -print-bytecode  -print-code sample.js \n[generated bytecode for function:  (0x2a180824ffbd \u003cSharedFunctionInfo\u003e)]\nParameter count 1\nRegister count 5\nFrame size 40\n         0x2a1808250066 @    0 : 12 00             LdaConstant [0]\n         0x2a1808250068 @    2 : 26 f9             Star r2\n         0x2a180825006a @    4 : 27 fe f8          Mov \u003cclosure\u003e, r3\n         0x2a180825006d @    7 : 61 32 01 f9 02    CallRuntime [DeclareGlobals], r2-r3\n         0x2a1808250072 @   12 : 0b                LdaZero \n         0x2a1808250073 @   13 : 26 fa             Star r1\n         0x2a1808250075 @   15 : 0d                LdaUndefined \n         0x2a1808250076 @   16 : 26 fb             Star r0\n         0x2a1808250078 @   18 : 00 0c 10 27       LdaSmi.Wide [10000]\n         0x2a180825007c @   22 : 69 fa 00          TestLessThan r1, [0]\n         0x2a180825007f @   25 : 9a 1c             JumpIfFalse [28] (0x2a180825009b @ 53)\n         0x2a1808250081 @   27 : a7                StackCheck \n         0x2a1808250082 @   28 : 13 01 01          LdaGlobal [1], [1]\n         0x2a1808250085 @   31 : 26 f9             Star r2\n         0x2a1808250087 @   33 : 0c 02             LdaSmi [2]\n         0x2a1808250089 @   35 : 26 f7             Star r4\n         0x2a180825008b @   37 : 5e f9 fa f7 03    CallUndefinedReceiver2 r2, r1, r4, [3]\n         0x2a1808250090 @   42 : 26 fb             Star r0\n         0x2a1808250092 @   44 : 25 fa             Ldar r1\n         0x2a1808250094 @   46 : 4c 05             Inc [5]\n         0x2a1808250096 @   48 : 26 fa             Star r1\n         0x2a1808250098 @   50 : 8a 20 00          JumpLoop [32], [0] (0x2a1808250078 @ 18)\n         0x2a180825009b @   53 : 25 fb             Ldar r0\n         0x2a180825009d @   55 : ab                Return \nConstant pool (size = 2)\n0x2a1808250035: [FixedArray] in OldSpace\n - map: 0x2a18080404b1 \u003cMap\u003e\n - length: 2\n           0: 0x2a180824ffe5 \u003cFixedArray[2]\u003e\n           1: 0x2a180824ff61 \u003cString[#9]: something\u003e\nHandler Table (size = 0)\nSource Position Table (size = 0)\n[generated bytecode for function: something (0x2a180824fff5 \u003cSharedFunctionInfo something\u003e)]\nParameter count 3\nRegister count 0\nFrame size 0\n         0x2a18082501ba @    0 : 25 02             Ldar a1\n         0x2a18082501bc @    2 : 34 03 00          Add a0, [0]\n         0x2a18082501bf @    5 : ab                Return \nConstant pool (size = 0)\nHandler Table (size = 0)\nSource Position Table (size = 0)\n--- Raw source ---\nfunction something(x, y) {\n  return x + y\n}\nfor (let i = 0; i \u003c 10000; i++) {\n  something(i, 2);\n}\n\n\n--- Optimized code ---\noptimization_id = 0\nsource_position = 0\nkind = OPTIMIZED_FUNCTION\nstack_slots = 14\ncompiler = turbofan\naddress = 0x108400082ae1\n\nInstructions (size = 536)\n0x108400082b20     0  488d1df9ffffff REX.W leaq rbx,[rip+0xfffffff9]\n0x108400082b27     7  483bd9         REX.W cmpq rbx,rcx\n0x108400082b2a     a  7418           jz 0x108400082b44  \u003c+0x24\u003e\n0x108400082b2c     c  48ba6800000000000000 REX.W movq rdx,0x68\n0x108400082b36    16  49bae0938c724b560000 REX.W movq r10,0x564b728c93e0  (Abort)    ;; off heap target\n0x108400082b40    20  41ffd2         call r10\n0x108400082b43    23  cc             int3l\n0x108400082b44    24  8b59d0         movl rbx,[rcx-0x30]\n0x108400082b47    27  4903dd         REX.W addq rbx,r13\n0x108400082b4a    2a  f6430701       testb [rbx+0x7],0x1\n0x108400082b4e    2e  740d           jz 0x108400082b5d  \u003c+0x3d\u003e\n0x108400082b50    30  49bae0f781724b560000 REX.W movq r10,0x564b7281f7e0  (CompileLazyDeoptimizedCode)    ;; off heap target\n0x108400082b5a    3a  41ffe2         jmp r10\n0x108400082b5d    3d  55             push rbp\n0x108400082b5e    3e  4889e5         REX.W movq rbp,rsp\n0x108400082b61    41  56             push rsi\n0x108400082b62    42  57             push rdi\n0x108400082b63    43  48ba4200000000000000 REX.W movq rdx,0x42\n0x108400082b6d    4d  4c8b15c4ffffff REX.W movq r10,[rip+0xffffffc4]\n0x108400082b74    54  41ffd2         call r10\n0x108400082b77    57  cc             int3l\n0x108400082b78    58  4883ec18       REX.W subq rsp,0x18\n0x108400082b7c    5c  488975a0       REX.W movq [rbp-0x60],rsi\n0x108400082b80    60  488b4dd0       REX.W movq rcx,[rbp-0x30]\n0x108400082b84    64  f6c101         testb rcx,0x1\n0x108400082b87    67  0f8557010000   jnz 0x108400082ce4  \u003c+0x1c4\u003e\n0x108400082b8d    6d  81f9204e0000   cmpl rcx,0x4e20\n0x108400082b93    73  0f8c0b000000   jl 0x108400082ba4  \u003c+0x84\u003e\n0x108400082b99    79  488b45d8       REX.W movq rax,[rbp-0x28]\n0x108400082b9d    7d  488be5         REX.W movq rsp,rbp\n0x108400082ba0    80  5d             pop rbp\n0x108400082ba1    81  c20800         ret 0x8\n0x108400082ba4    84  493b6560       REX.W cmpq rsp,[r13+0x60] (external value (StackGuard::address_of_jslimit()))\n0x108400082ba8    88  0f8669000000   jna 0x108400082c17  \u003c+0xf7\u003e\n0x108400082bae    8e  488bf9         REX.W movq rdi,rcx\n0x108400082bb1    91  d1ff           sarl rdi, 1\n0x108400082bb3    93  4c8bc7         REX.W movq r8,rdi\n0x108400082bb6    96  4183c002       addl r8,0x2\n0x108400082bba    9a  0f8030010000   jo 0x108400082cf0  \u003c+0x1d0\u003e\n0x108400082bc0    a0  83c701         addl rdi,0x1\n0x108400082bc3    a3  0f8033010000   jo 0x108400082cfc  \u003c+0x1dc\u003e\n0x108400082bc9    a9  e921000000     jmp 0x108400082bef  \u003c+0xcf\u003e\n0x108400082bce    ae  6690           nop\n0x108400082bd0    b0  488bcf         REX.W movq rcx,rdi\n0x108400082bd3    b3  83c102         addl rcx,0x2\n0x108400082bd6    b6  0f802c010000   jo 0x108400082d08  \u003c+0x1e8\u003e\n0x108400082bdc    bc  4c8bc7         REX.W movq r8,rdi\n0x108400082bdf    bf  4183c001       addl r8,0x1\n0x108400082be3    c3  0f802b010000   jo 0x108400082d14  \u003c+0x1f4\u003e\n0x108400082be9    c9  498bf8         REX.W movq rdi,r8\n0x108400082bec    cc  4c8bc1         REX.W movq r8,rcx\n0x108400082bef    cf  81ff10270000   cmpl rdi,0x2710\n0x108400082bf5    d5  0f8d0b000000   jge 0x108400082c06  \u003c+0xe6\u003e\n0x108400082bfb    db  493b6560       REX.W cmpq rsp,[r13+0x60] (external value (StackGuard::address_of_jslimit()))\n0x108400082bff    df  77cf           ja 0x108400082bd0  \u003c+0xb0\u003e\n0x108400082c01    e1  e943000000     jmp 0x108400082c49  \u003c+0x129\u003e\n0x108400082c06    e6  498bc8         REX.W movq rcx,r8\n0x108400082c09    e9  4103c8         addl rcx,r8\n0x108400082c0c    ec  0f8061000000   jo 0x108400082c73  \u003c+0x153\u003e\n0x108400082c12    f2  488bc1         REX.W movq rax,rcx\n0x108400082c15    f5  eb86           jmp 0x108400082b9d  \u003c+0x7d\u003e\n0x108400082c17    f7  33c0           xorl rax,rax\n0x108400082c19    f9  48bef50c240884100000 REX.W movq rsi,0x108408240cf5    ;; object: 0x108408240cf5 \u003cNativeContext[261]\u003e\n0x108400082c23   103  48bb101206724b560000 REX.W movq rbx,0x564b72061210    ;; external reference (Runtime::StackGuard)\n0x108400082c2d   10d  488bf8         REX.W movq rdi,rax\n0x108400082c30   110  4c8bc6         REX.W movq r8,rsi\n0x108400082c33   113  49ba2089a3724b560000 REX.W movq r10,0x564b72a38920  (CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit)    ;; off heap target\n0x108400082c3d   11d  41ffd2         call r10\n0x108400082c40   120  488b4dd0       REX.W movq rcx,[rbp-0x30]\n0x108400082c44   124  e965ffffff     jmp 0x108400082bae  \u003c+0x8e\u003e\n0x108400082c49   129  48897da8       REX.W movq [rbp-0x58],rdi\n0x108400082c4d   12d  488b1dd1ffffff REX.W movq rbx,[rip+0xffffffd1]\n0x108400082c54   134  33c0           xorl rax,rax\n0x108400082c56   136  48bef50c240884100000 REX.W movq rsi,0x108408240cf5    ;; object: 0x108408240cf5 \u003cNativeContext[261]\u003e\n0x108400082c60   140  4c8b15ceffffff REX.W movq r10,[rip+0xffffffce]\n0x108400082c67   147  41ffd2         call r10\n0x108400082c6a   14a  488b7da8       REX.W movq rdi,[rbp-0x58]\n0x108400082c6e   14e  e95dffffff     jmp 0x108400082bd0  \u003c+0xb0\u003e\n0x108400082c73   153  48b968ea2f744b560000 REX.W movq rcx,0x564b742fea68    ;; external reference (Heap::NewSpaceAllocationTopAddress())\n0x108400082c7d   15d  488b39         REX.W movq rdi,[rcx]\n0x108400082c80   160  4c8d4f0c       REX.W leaq r9,[rdi+0xc]\n0x108400082c84   164  4c8945b0       REX.W movq [rbp-0x50],r8\n0x108400082c88   168  49bb70ea2f744b560000 REX.W movq r11,0x564b742fea70    ;; external reference (Heap::NewSpaceAllocationLimitAddress())\n0x108400082c92   172  4d390b         REX.W cmpq [r11],r9\n0x108400082c95   175  0f8721000000   ja 0x108400082cbc  \u003c+0x19c\u003e\n0x108400082c9b   17b  ba0c000000     movl rdx,0xc\n0x108400082ca0   180  49ba200282724b560000 REX.W movq r10,0x564b72820220  (AllocateRegularInYoungGeneration)    ;; off heap target\n0x108400082caa   18a  41ffd2         call r10\n0x108400082cad   18d  488d78ff       REX.W leaq rdi,[rax-0x1]\n0x108400082cb1   191  488b0dbdffffff REX.W movq rcx,[rip+0xffffffbd]\n0x108400082cb8   198  4c8b45b0       REX.W movq r8,[rbp-0x50]\n0x108400082cbc   19c  4c8d4f0c       REX.W leaq r9,[rdi+0xc]\n0x108400082cc0   1a0  4c8909         REX.W movq [rcx],r9\n0x108400082cc3   1a3  488d4f01       REX.W leaq rcx,[rdi+0x1]\n0x108400082cc7   1a7  498bbd40010000 REX.W movq rdi,[r13+0x140] (root (heap_number_map))\n0x108400082cce   1ae  8979ff         movl [rcx-0x1],rdi\n0x108400082cd1   1b1  c4c1032ac0     vcvtlsi2sd xmm0,xmm15,r8\n0x108400082cd6   1b6  c5fb114103     vmovsd [rcx+0x3],xmm0\n0x108400082cdb   1bb  488bc1         REX.W movq rax,rcx\n0x108400082cde   1be  e9bafeffff     jmp 0x108400082b9d  \u003c+0x7d\u003e\n0x108400082ce3   1c3  90             nop\n0x108400082ce4   1c4  49c7c500000000 REX.W movq r13,0x0\n0x108400082ceb   1cb  e850f30300     call 0x1084000c2040     ;; eager deoptimization bailout\n0x108400082cf0   1d0  49c7c501000000 REX.W movq r13,0x1\n0x108400082cf7   1d7  e844f30300     call 0x1084000c2040     ;; eager deoptimization bailout\n0x108400082cfc   1dc  49c7c502000000 REX.W movq r13,0x2\n0x108400082d03   1e3  e838f30300     call 0x1084000c2040     ;; eager deoptimization bailout\n0x108400082d08   1e8  49c7c503000000 REX.W movq r13,0x3\n0x108400082d0f   1ef  e82cf30300     call 0x1084000c2040     ;; eager deoptimization bailout\n0x108400082d14   1f4  49c7c504000000 REX.W movq r13,0x4\n0x108400082d1b   1fb  e820f30300     call 0x1084000c2040     ;; eager deoptimization bailout\n0x108400082d20   200  49c7c505000000 REX.W movq r13,0x5\n0x108400082d27   207  e814f30700     call 0x108400102040     ;; lazy deoptimization bailout\n0x108400082d2c   20c  49c7c506000000 REX.W movq r13,0x6\n0x108400082d33   213  e808f30700     call 0x108400102040     ;; lazy deoptimization bailout\n\nSource positions:\n pc offset  position\n        f7         0\n\nInlined functions (count = 1)\n 0x10840824fff5 \u003cSharedFunctionInfo something\u003e\n\nDeoptimization Input Data (deopt points = 7)\n index  bytecode-offset    pc\n     0               22    NA \n     1                2    NA \n     2               46    NA \n     3                2    NA \n     4               46    NA \n     5               27   120 \n     6               27   14a \n\nSafepoints (size = 50)\n0x108400082c40     120   200  10000010000000 (sp -\u003e fp)       5\n0x108400082c6a     14a   20c  10000000000000 (sp -\u003e fp)       6\n0x108400082cad     18d    NA  00000000000000 (sp -\u003e fp)  \u003cnone\u003e\n\nRelocInfo (size = 34)\n0x108400082b38  off heap target\n0x108400082b52  off heap target\n0x108400082c1b  full embedded object  (0x108408240cf5 \u003cNativeContext[261]\u003e)\n0x108400082c25  external reference (Runtime::StackGuard)  (0x564b72061210)\n0x108400082c35  off heap target\n0x108400082c58  full embedded object  (0x108408240cf5 \u003cNativeContext[261]\u003e)\n0x108400082c75  external reference (Heap::NewSpaceAllocationTopAddress())  (0x564b742fea68)\n0x108400082c8a  external reference (Heap::NewSpaceAllocationLimitAddress())  (0x564b742fea70)\n0x108400082ca2  off heap target\n0x108400082cec  runtime entry  (eager deoptimization bailout)\n0x108400082cf8  runtime entry  (eager deoptimization bailout)\n0x108400082d04  runtime entry  (eager deoptimization bailout)\n0x108400082d10  runtime entry  (eager deoptimization bailout)\n0x108400082d1c  runtime entry  (eager deoptimization bailout)\n0x108400082d28  runtime entry  (lazy deoptimization bailout)\n0x108400082d34  runtime entry  (lazy deoptimization bailout)\n\n--- End code ---\n$ \n\n```\n\n### Building Google Test\n```console\n$ mkdir lib\n$ mkdir deps ; cd deps\n$ git clone git@github.com:google/googletest.git\n$ cd googletest/googletest\n$ /usr/bin/clang++ --std=c++14 -Iinclude -I. -pthread -c src/gtest-all.cc\n$ ar -rv libgtest-linux.a gtest-all.o \n$ cp libgtest-linux.a ../../../../lib/gtest\n```\n\nLinking issue:\n```console\n./lib/gtest/libgtest-linux.a(gtest-all.o):gtest-all.cc:function testing::internal::BoolFromGTestEnv(char const*, bool): error: undefined reference to 'std::__cxx11::basic_string\u003cchar, std::char_traits\u003cchar\u003e, std::allocator\u003cchar\u003e \u003e::c_str() const'\n```\n```\n$ nm lib/gtest/libgtest-linux.a | grep basic_string | c++filt \n....\n```\nThere are a lot of symbols listed above but the point is that in the object\nfile of `libgtest-linux.a` these symbols were compiled in. Now, when we compile\nv8 and the tests we are using `-std=c++14` and we have to use the same when compiling\ngtest. Lets try that. Just adding that does not help in this case. We need to\ncheck which c++ headers are being used:\n```console\n$ /usr/bin/clang++ -print-search-dirs\nprograms: =/usr/bin:/usr/bin/../lib/gcc/x86_64-redhat-linux/9/../../../../x86_64-redhat-linux/bin\nlibraries: =/usr/lib64/clang/9.0.0:\n            /usr/bin/../lib/gcc/x86_64-redhat-linux/9:\n            /usr/bin/../lib/gcc/x86_64-redhat-linux/9/../../../../lib64:\n            /usr/bin/../lib64:\n            /lib/../lib64:\n            /usr/lib/../lib64:\n            /usr/bin/../lib/gcc/x86_64-redhat-linux/9/../../..:\n            /usr/bin/../lib:\n            /lib:/usr/lib\n$ \n```\nLets search for the `string` header and inspect the namespace in that header:\n```console\n$ find /usr/ -name string\n/usr/include/c++/9/debug/string\n/usr/include/c++/9/experimental/string\n/usr/include/c++/9/string\n/usr/src/debug/gcc-9.2.1-1.fc31.x86_64/obj-x86_64-redhat-linux/x86_64-redhat-linux/libstdc++-v3/include/string\n```\n```console\n$ vi /usr/include/c++/9/string\n```\nSo this looks alright and thinking about this a little more I've been bitten\nby the linking with different libc++ symbols issue (again). When we compile using Make we\nare using the c++ headers that are shipped with v8 (clang libc++). Take the\nstring header for example in v8/buildtools/third_party/libc++/trunk/include/string\nwhich is from clang's c++ library which does not use namespaces (__11 or __14 etc).\n\nBut when I compiled gtest did not specify the istystem include path and the\ndefault would be used adding symbols with __11 into them. When the linker tries\nto find these symbols it fails as it does not have any such symbols in the libraries\nthat it searches.\n\nCreate a simple test linking with the standard build of gtest to see if that\ncompiles and runs:\n```console\n$ /usr/bin/clang++ -std=c++14 -I./deps/googletest/googletest/include  -L$PWD/lib -g -O0 -o test/simple_test test/main.cc test/simple.cc lib/libgtest.a -lpthread\n```\nThat worked and does not segfault. \n\nBut when I run the version that is built using the makefile I get:\n```console\nlldb) target create \"./test/persistent-object_test\"\nCurrent executable set to './test/persistent-object_test' (x86_64).\n(lldb) r\nProcess 1024232 launched: '/home/danielbevenius/work/google/learning-v8/test/persistent-object_test' (x86_64)\nwarning: (x86_64) /lib64/libgcc_s.so.1 unsupported DW_FORM values: 0x1f20 0x1f21\n\n[ FATAL ] Process 1024232 stopped\n* thread #1, name = 'persistent-obje', stop reason = signal SIGSEGV: invalid address (fault address: 0x33363658)\n    frame #0: 0x00007ffff7c0a7b0 libc.so.6`__GI___libc_free + 32\nlibc.so.6`__GI___libc_free:\n-\u003e  0x7ffff7c0a7b0 \u003c+32\u003e: mov    rax, qword ptr [rdi - 0x8]\n    0x7ffff7c0a7b4 \u003c+36\u003e: lea    rsi, [rdi - 0x10]\n    0x7ffff7c0a7b8 \u003c+40\u003e: test   al, 0x2\n    0x7ffff7c0a7ba \u003c+42\u003e: jne    0x7ffff7c0a7f0            ; \u003c+96\u003e\n(lldb) bt\n* thread #1, name = 'persistent-obje', stop reason = signal SIGSEGV: invalid address (fault address: 0x33363658)\n  * frame #0: 0x00007ffff7c0a7b0 libc.so.6`__GI___libc_free + 32\n    frame #1: 0x000000000042bb58 persistent-object_test`std::__1::basic_stringbuf\u003cchar, std::__1::char_traits\u003cchar\u003e, std::__1::allocator\u003cchar\u003e \u003e::~basic_stringbuf(this=0x000000000046e908) at iosfwd:130:32\n    frame #2: 0x000000000042ba4f persistent-object_test`std::__1::basic_stringstream\u003cchar, std::__1::char_traits\u003cchar\u003e, std::__1::allocator\u003cchar\u003e \u003e::~basic_stringstream(this=0x000000000046e8f0, vtt=0x000000000044db28) at iosfwd:139:32\n    frame #3: 0x0000000000420176 persistent-object_test`std::__1::basic_stringstream\u003cchar, std::__1::char_traits\u003cchar\u003e, std::__1::allocator\u003cchar\u003e \u003e::~basic_stringstream(this=0x000000000046e8f0) at iosfwd:139:32\n    frame #4: 0x000000000042bacc persistent-object_test`std::__1::basic_stringstream\u003cchar, std::__1::char_traits\u003cchar\u003e, std::__1::allocator\u003cchar\u003e \u003e::~basic_stringstream(this=0x000000000046e8f0) at iosfwd:139:32\n    frame #5: 0x0000000000427f4e persistent-object_test`testing::internal::scoped_ptr\u003cstd::__1::basic_stringstream\u003cchar, std::__1::char_traits\u003cchar\u003e, std::__1::allocator\u003cchar\u003e \u003e \u003e::reset(this=0x00007fffffffcee8, p=0x0000000000000000) at gtest-port.h:1216:9\n    frame #6: 0x0000000000427ee9 persistent-object_test`testing::internal::scoped_ptr\u003cstd::__1::basic_stringstream\u003cchar, std::__1::char_traits\u003cchar\u003e, std::__1::allocator\u003cchar\u003e \u003e \u003e::~scoped_ptr(this=0x00007fffffffcee8) at gtest-port.h:1201:19\n    frame #7: 0x000000000041f265 persistent-object_test`testing::Message::~Message(this=0x00007fffffffcee8) at gtest-message.h:89:18\n    frame #8: 0x00000000004235ec persistent-object_test`std::__1::basic_string\u003cchar, std::__1::char_traits\u003cchar\u003e, std::__1::allocator\u003cchar\u003e \u003e testing::internal::StreamableToString\u003cint\u003e(streamable=0x00007fffffffcf9c) at gtest-message.h:247:3\n    frame #9: 0x000000000040d2bd persistent-object_test`testing::internal::FormatFileLocation(file=\"/home/danielbevenius/work/google/learning-v8/deps/googletest/googletest/src/gtest-internal-inl.h\", line=663) at gtest-port.cc:946:28\n    frame #10: 0x000000000041b7e2 persistent-object_test`testing::internal::GTestLog::GTestLog(this=0x00007fffffffd060, severity=GTEST_FATAL, file=\"/home/danielbevenius/work/google/learning-v8/deps/googletest/googletest/src/gtest-internal-inl.h\", line=663) at gtest-port.cc:972:18\n    frame #11: 0x000000000042242c persistent-object_test`testing::internal::UnitTestImpl::AddTestInfo(this=0x000000000046e480, set_up_tc=(persistent-object_test`testing::Test::SetUpTestCase() at gtest.h:427), tear_down_tc=(persistent-object_test`testing::Test::TearDownTestCase() at gtest.h:435), test_info=0x000000000046e320)(), void (*)(), testing::TestInfo*) at gtest-internal-inl.h:663:7\n    frame #12: 0x000000000040d04f persistent-object_test`testing::internal::MakeAndRegisterTestInfo(test_case_name=\"Persistent\", name=\"object\", type_param=0x0000000000000000, value_param=0x0000000000000000, code_location=\u003cunavailable\u003e, fixture_class_id=0x000000000046d748, set_up_tc=(persistent-object_test`testing::Test::SetUpTestCase() at gtest.h:427), tear_down_tc=(persistent-object_test`testing::Test::TearDownTestCase() at gtest.h:435), factory=0x000000000046e300)(), void (*)(), testing::internal::TestFactoryBase*) at gtest.cc:2599:22\n    frame #13: 0x00000000004048b8 persistent-object_test`::__cxx_global_var_init() at persistent-object_test.cc:5:1\n    frame #14: 0x00000000004048e9 persistent-object_test`_GLOBAL__sub_I_persistent_object_test.cc at persistent-object_test.cc:0\n    frame #15: 0x00000000004497a5 persistent-object_test`__libc_csu_init + 69\n    frame #16: 0x00007ffff7ba512e libc.so.6`__libc_start_main + 126\n    frame #17: 0x0000000000404eba persistent-object_test`_start + 42\n```\n\n### Google test (gtest) linking issue\nThis issue came up when linking a unit test with gtest:\n```console\n/usr/bin/ld: ./lib/gtest/libgtest-linux.a(gtest-all.o): in function `testing::internal::BoolFromGTestEnv(char const*, bool)':\n/home/danielbevenius/work/google/learning-v8/deps/googletest/googletest/src/gtest-port.cc:1259: undefined reference to `std::__1::basic_string\u003cchar, std::__1::char_traits\u003cchar\u003e, std::__1::allocator\u003cchar\u003e \u003e::~basic_string()'\n```\nSo this indicated that the object files in `libgtest-linux.a` where infact using\nheaders from libc++ and not libstc++. This was a really stupig mistake on my\npart, I'd not specified the output file explicitly (-o) so this was getting\nadded into the current working directory, but the file included in the archive\nwas taken from within deps/googltest/googletest/ directory which was old and\ncompiled using libc++.\n\n### Peristent cast-function-type\nThis issue was seen in Node.js when compiling with GCC. It can also been see\nif building V8 using GCC and also enabling `-Wcast-function-type` in BUILD.gn:\n```\n      \"-Wcast-function-type\",\n```\nThere are unit tests in V8 that also produce this warning, for example\n`test/cctest/test-global-handles.cc`:\nOriginal:\n```console\ng++ -MMD -MF obj/test/cctest/cctest_sources/test-global-handles.o.d -DV8_INTL_SUPPORT -DUSE_UDEV -DUSE_AURA=1 -DUSE_GLIB=1 -DUSE_NSS_CERTS=1 -DUSE_X11=1 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -DCR_SYSROOT_HASH=9c905c99558f10e19cc878b5dca1d4bd58c607ae -D_DEBUG -DDYNAMIC_ANNOTATIONS_ENABLED=1 -DENABLE_DISASSEMBLER -DV8_TYPED_ARRAY_MAX_SIZE_IN_HEAP=64 -DENABLE_GDB_JIT_INTERFACE -DENABLE_MINOR_MC -DOBJECT_PRINT -DV8_TRACE_MAPS -DV8_ENABLE_ALLOCATION_TIMEOUT -DV8_ENABLE_FORCE_SLOW_PATH -DV8_ENABLE_DOUBLE_CONST_STORE_CHECK -DV8_INTL_SUPPORT -DENABLE_HANDLE_ZAPPING -DV8_SNAPSHOT_NATIVE_CODE_COUNTERS -DV8_CONCURRENT_MARKING -DV8_ENABLE_LAZY_SOURCE_POSITIONS -DV8_CHECK_MICROTASKS_SCOPES_CONSISTENCY -DV8_EMBEDDED_BUILTINS -DV8_WIN64_UNWINDING_INFO -DV8_ENABLE_REGEXP_INTERPRETER_THREADED_DISPATCH -DV8_SNAPSHOT_COMPRESSION -DV8_ENABLE_CHECKS -DV8_COMPRESS_POINTERS -DV8_31BIT_SMIS_ON_64BIT_ARCH -DV8_DEPRECATION_WARNINGS -DV8_IMMINENT_DEPRECATION_WARNINGS -DV8_TARGET_ARCH_X64 -DV8_HAVE_TARGET_OS -DV8_TARGET_OS_LINUX -DDEBUG -DDISABLE_UNTRUSTED_CODE_MITIGATIONS -DV8_ENABLE_CHECKS -DV8_COMPRESS_POINTERS -DV8_31BIT_SMIS_ON_64BIT_ARCH -DV8_DEPRECATION_WARNINGS -DV8_IMMINENT_DEPRECATION_WARNINGS -DU_USING_ICU_NAMESPACE=0 -DU_ENABLE_DYLOAD=0 -DUSE_CHROMIUM_ICU=1 -DU_STATIC_IMPLEMENTATION -DICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_FILE -DUCHAR_TYPE=uint16_t -I../.. -Igen -I../../include -Igen/include -I../.. -Igen -I../../third_party/icu/source/common -I../../third_party/icu/source/i18n -I../../include -I../../tools/debug_helper -fno-strict-aliasing --param=ssp-buffer-size=4 -fstack-protector -funwind-tables -fPIC -pipe -B../../third_party/binutils/Linux_x64/Release/bin -pthread -m64 -march=x86-64 -Wno-builtin-macro-redefined -D__DATE__= -D__TIME__= -D__TIMESTAMP__= -Wall -Wno-unused-local-typedefs -Wno-maybe-uninitialized -Wno-deprecated-declarations -Wno-comments -Wno-packed-not-aligned -Wno-missing-field-initializers -Wno-unused-parameter -fno-omit-frame-pointer -g2 -Wno-strict-overflow -Wno-return-type -Wcast-function-type -O3 -fno-ident -fdata-sections -ffunction-sections -fvisibility=default -std=gnu++14 -Wno-narrowing -Wno-class-memaccess -fno-exceptions -fno-rtti --sysroot=../../build/linux/debian_sid_amd64-sysroot -c ../../test/cctest/test-global-handles.cc -o obj/test/cctest/cctest_sources/test-global-handles.o\nIn file included from ../../include/v8-inspector.h:14,\n                 from ../../src/execution/isolate.h:15,\n                 from ../../src/api/api.h:10,\n                 from ../../src/api/api-inl.h:8,\n                 from ../../test/cctest/test-global-handles.cc:28:\n../../include/v8.h: In instantiation of ‘void v8::PersistentBase\u003cT\u003e::SetWeak(P*, typename v8::WeakCallbackInfo\u003cP\u003e::Callback, v8::WeakCallbackType) [with P = v8::Global\u003cv8::Object\u003e; T = v8::Object; typename v8::WeakCallbackInfo\u003cP\u003e::Callback = void (*)(const v8::WeakCallbackInfo\u003cv8::Global\u003cv8::Object\u003e \u003e\u0026)]’:\n../../test/cctest/test-global-handles.cc:292:47:   required from here\n../../include/v8.h:10750:16: warning: cast between incompatible function types from ‘v8::WeakCallbackInfo\u003cv8::Global\u003cv8::Object\u003e \u003e::Callback’ {aka ‘void (*)(const v8::WeakCallbackInfo\u003cv8::Global\u003cv8::Object\u003e \u003e\u0026)’} to ‘Callback’ {aka ‘void (*)(const v8::WeakCallbackInfo\u003cvoid\u003e\u0026)’} [-Wcast-function-type]\n10750 |                reinterpret_cast\u003cCallback\u003e(callback), type);\n      |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n../../include/v8.h: In instantiation of ‘void v8::PersistentBase\u003cT\u003e::SetWeak(P*, typename v8::WeakCallbackInfo\u003cP\u003e::Callback, v8::WeakCallbackType) [with P = v8::internal::{anonymous}::FlagAndGlobal; T = v8::Object; typename v8::WeakCallbackInfo\u003cP\u003e::Callback = void (*)(const v8::WeakCallbackInfo\u003cv8::internal::{anonymous}::FlagAndGlobal\u003e\u0026)]’:\n../../test/cctest/test-global-handles.cc:493:53:   required from here\n../../include/v8.h:10750:16: warning: cast between incompatible function types from ‘v8::WeakCallbackInfo\u003cv8::internal::{anonymous}::FlagAndGlobal\u003e::Callback’ {aka ‘void (*)(const v8::WeakCallbackInfo\u003cv8::internal::{anonymous}::FlagAndGlobal\u003e\u0026)’} to ‘Callback’ {aka ‘void (*)(const v8::WeakCallbackInfo\u003cvoid\u003e\u0026)’} [-Wcast-function-type]\n```\nFormatted for git commit message:\n```console\ng++ -MMD -MF obj/test/cctest/cctest_sources/test-global-handles.o.d \n...\nIn file included from ../../include/v8-inspector.h:14,\n                 from ../../src/execution/isolate.h:15,\n                 from ../../src/api/api.h:10,\n                 from ../../src/api/api-inl.h:8,\n                 from ../../test/cctest/test-global-handles.cc:28:\n../../include/v8.h:\nIn instantiation of ‘void v8::PersistentBase\u003cT\u003e::SetWeak(\n    P*,\n    typename v8::WeakCallbackInfo\u003cP\u003e::Callback,\n    v8::WeakCallbackType)\n[with \n  P = v8::Global\u003cv8::Object\u003e; \n  T = v8::Object;\n  typename v8::WeakCallbackInfo\u003cP\u003e::Callback =\n  void (*)(const v8::WeakCallbackInfo\u003cv8::Global\u003cv8::Object\u003e \u003e\u0026)\n]’:\n../../test/cctest/test-global-handles.cc:292:47:   required from here\n../../include/v8.h:10750:16: warning:\ncast between incompatible function types from\n‘v8::WeakCallbackInfo\u003cv8::Global\u003cv8::Object\u003e \u003e::Callback’ {aka\n‘void (*)(const v8::WeakCallbackInfo\u003cv8::Global\u003cv8::Object\u003e \u003e\u0026)’} to \n‘Callback’ {aka ‘void (*)(const v8::WeakCallbackInfo\u003cvoid\u003e\u0026)’}\n[-Wcast-function-type]\n10750 |                reinterpret_cast\u003cCallback\u003e(callback), type);\n      |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n```\nThis commit suggests adding a pragma specifically for GCC to suppress\nthis warning. The motivation for this is that there were quite a few\nof these warnings in the Node.js build, but these have been suppressed\nby adding a similar pragma but around the include of v8.h [1].\n\n[1] https://github.com/nodejs/node/blob/331d63624007be4bf49d6d161bdef2b5e540affa/src/node.h#L63-L70\n\n```console\n$ \nIn file included from persistent-obj.cc:8:\n/home/danielbevenius/work/google/v8_src/v8/include/v8.h: In instantiation of ‘void v8::PersistentBase\u003cT\u003e::SetWeak(P*, typename v8::WeakCallbackInfo\u003cP\u003e::Callback, v8::WeakCallbackType) [with P = Something; T = v8::Object; typename v8::WeakCallbackInfo\u003cP\u003e::Callback = void (*)(const v8::WeakCallbackInfo\u003cSomething\u003e\u0026)]’:\n\npersistent-obj.cc:57:38:   required from here\n/home/danielbevenius/work/google/v8_src/v8/include/v8.h:10750:16: warning: cast between incompatible function types from ‘v8::WeakCallbackInfo\u003cSomething\u003e::Callback’ {aka ‘void (*)(const v8::WeakCallbackInfo\u003cSomething\u003e\u0026)’} to ‘Callback’ {aka ‘void (*)(const v8::WeakCallbackInfo\u003cvoid\u003e\u0026)’} [-Wcast-function-type]\n10750 |                reinterpret_cast\u003cCallback\u003e(callback), type);\n      |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n```\nCurrently, we have added a pragma to avoid this warning in node.js but we'd like\nto add this in v8 and closer to the actual code that is causing it. In node we\nhave to set the praga on the header.\n```c++\ntemplate \u003cclass T\u003e\ntemplate \u003ctypename P\u003e\nV8_INLINE void PersistentBase\u003cT\u003e::SetWeak(\n    P* parameter,\n    typename WeakCallbackInfo\u003cP\u003e::Callback callback,\n    WeakCallbackType type) {\n  typedef typename WeakCallbackInfo\u003cvoid\u003e::Callback Callback;\n  V8::MakeWeak(reinterpret_cast\u003cinternal::Address*\u003e(this-\u003eval_), parameter,\n               reinterpret_cast\u003cCallback\u003e(callback), type);\n}\n```\nNotice the second parameter is `typename WeakCallbackInfo\u003cP\u003e::Callback` which is\na typedef:\n```c++\n  typedef void (*Callback)(const WeakCallbackInfo\u003cT\u003e\u0026 data);\n```\nThis is a function declaration for `Callback` which is a function that takes\na reference to a const WeakCallbackInfo\u003cT\u003e and returns void. So we could define\nit like this:\n```c++\nvoid WeakCallback(const v8::WeakCallbackInfo\u003cSomething\u003e\u0026 data) {\n  Something* obj = data.GetParameter();\n  std::cout \u003c\u003c \"in make weak callback...\" \u003c\u003c '\\n';\n}\n```\nAnd the trying to cast it into:\n```c++\n  typedef typename v8::WeakCallbackInfo\u003cvoid\u003e::Callback Callback;\n  Callback cb = reinterpret_cast\u003cCallback\u003e(WeakCallback);\n```\nThis is done as V8::MakeWeak has the following signature:\n```c++\nvoid V8::MakeWeak(i::Address* location, void* parameter,\n                  WeakCallbackInfo\u003cvoid\u003e::Callback weak_callback,\n                  WeakCallbackType type) {\n  i::GlobalHandles::MakeWeak(location, parameter, weak_callback, type);\n}\n```\n\n\n### gdb warnings\n```console\nwarning: Could not find DWO CU obj/v8_compiler/common-node-cache.dwo(0x42b8adb87d74d56b) referenced by CU at offset 0x206f7 [in module /home/danielbevenius/work/google/learning-v8/hello-world]\n```\nThis can be worked around by specifying the `--cd` argument to gdb:\n```console\n$ gdb --cd=/home/danielbevenius/work/google/v8_src/v8/out/x64.release --args /home/danielbevenius/work/google/learning-v8/hello-world\n```\n\n### Building with g++\nUpdate args.gn to include:\n```\nis_clang = false\n```\nNext I got the following error when trying to compile:\n```console\n$ ninja -v -C out/x64.release/ obj/test/cctest/cctest_sources/test-global-handles.o\nux/debian_sid_amd64-sysroot -fexceptions -frtti -c ../../src/torque/instance-type-generator.cc -o obj/torque_base/instance-type-generator.o\nIn file included from /usr/include/c++/9/bits/stl_algobase.h:59,\n                 from /usr/include/c++/9/memory:62,\n                 from ../../src/torque/implementation-visitor.h:8,\n                 from ../../src/torque/instance-type-generator.cc:5:\n/usr/include/c++/9/x86_64-redhat-linux/bits/c++config.h:3:10: fatal error: bits/wordsize.h: No such file or directory\n    3 | #include \u003cbits/wordsize.h\u003e\n      |          ^~~~~~~~~~~~~~~~~\ncompilation terminated.\nninja: build stopped: subcommand failed.\n```\n```console\n$ export CPATH=/usr/include\n```\n\n```console\nthird_party/binutils/Linux_x64/Release/bin/ld.gold: error: cannot open /usr/lib64/libatomic.so.1.2.0: No such file or directory\n```\n```console\n$ sudo dnf install -y libatomic\n```\nI still got an error because of a warning but I'm trying to build using:\n```\ntreat_warnings_as_errors = false\n```\nLets see how that works out. I also had to use gnus linker by disableing \ngold:\n```console\nuse_gold = false\n```\n\n### CodeStubAssembler\nThis history of this is that JavaScript builtins used be written in assembly\nwhich gave very good performance but made porting V8 to different architectures\nmore difficult as these builtins had to have specific implementations for each\nsupported architecture, so it dit not scale very well. With the addition of features\nto the JavaScript specifications having to support new features meant having to\nimplement them for all platforms which made it difficult to keep up and deliver\nthese new features.\n\nThe goal is to have the perfomance of handcoded assembly but not have to write\nit for every platform. So a portable assembly language was build on top of\nTubofans backend. This is an API that generates Turbofan's machine-level IR.\nThis IR can be used by Turbofan to produce very good machine code on all platforms.\nSo one \"only\" has to implement one component/function/feature (not sure what to\ncall this) and then it can be made available to all platforms. They no longer\nhave to maintain all that handwritten assembly.\n\nJust to be clear CSA is a C++ API that is used to generate IR which is then\ncompiled in to machine code for the target instruction set architectur.\n\n### Torque\n[Torque](https://v8.dev/docs/torque) is a DLS language to avoid having to use\nthe CodeStubAssembler directly (it is still used behind the scene). This language\nis statically typed, garbage collected, and compatible with JavaScript.\n\nThe JavaScript standard library was implemented in V8 previously using hand\nwritten assembly. But as we mentioned in the previous section this did not scale.\n\nIt could have been written in JavaScript too, and I think this was done in the\npast but this has some issues as builtins would need warmup time to become\noptimized, there were also issues with monkey-patching and exposing VM internals\nunintentionally.\n\nIs torque run a build time, I'm thinking yes as it would have to generate the\nc++ code.\n\nThere is a main function in torque.cc which will be built into an executable\n```console\n$ ./out/x64.release_gcc/torque --help\nUnexpected command-line argument \"--help\", expected a .tq file.\n```\n\nThe files that are processed by torque are defined in BUILD.gc in the \n`torque_files` section. There is also a template named `run_torque`.\nI've noticed that this template and others in GN use the script  `tools/run.py`.\nThis is apperently because GN can only execute scripts at the moment and what this\nscript does is use python to create a subprocess with the passed in argument:\n```console\n$ gn help action\n```\nAnd a template is way to reuse code in GN.\n\n\nThere is a make target that shows what is generated by torque:\n```console\n$ make torque-example\n```\nThis will create a directory in the current directory named `gen/torque-generated`.\nNotice that this directory contains c++ headers and sources.\n\nIt take [torque-example.tq](./torque-example.tq) as input. For this file the\nfollowing header will be generated:\n```c++\n#ifndef V8_GEN_TORQUE_GENERATED_TORQUE_EXAMPLE_TQ_H_                            \n#define V8_GEN_TORQUE_GENERATED_TORQUE_EXAMPLE_TQ_H_                            \n                                                                                \n#include \"src/builtins/builtins-promise.h\"                                      \n#include \"src/compiler/code-assembler.h\"                                        \n#include \"src/codegen/code-stub-assembler.h\"                                    \n#include \"src/utils/utils.h\"                                                    \n#include \"torque-generated/field-offsets-tq.h\"                                  \n#include \"torque-generated/csa-types-tq.h\"                                      \n                                                                                \nnamespace v8 {                                                                  \nnamespace internal {                                                            \n                                                                                \nvoid HelloWorld_0(compiler::CodeAssemblerState* state_);                        \n\n}  // namespace internal                                                        \n}  // namespace v8                                                              \n                                                                                \n#endif  // V8_GEN_TORQUE_GENERATED_TORQUE_EXAMPLE_TQ_H_\n\n```\nThis is only to show the generated files and make it clear that torque will\ngenerate these file which will then be compiled during the v8 build. So, lets\ntry copying `example-torque.tq` to v8/src/builtins directory.\n```console\n$ cp torque-example.tq ../v8_src/v8/src/builtins/\n```\nThis is not enough to get it included in the build, we have to update BUILD.gn\nand add this file to the `torque_files` list. After running the build we can\nsee that there is a file named `src/builtins/torque-example-tq-csa.h` generated\nalong with a .cc.\n\nTo understand how this works I'm going to use https://v8.dev/docs/torque-builtins\nas a starting point:\n```\n  transitioning javascript builtin                                              \n  MathIs42(js-implicit context: NativeContext, receiver: JSAny)(x: JSAny): Boolean {\n    const number: Number = ToNumber_Inline(x);                                  \n    typeswitch (number) {                                                       \n      case (smi: Smi): {                                                        \n        return smi == 42 ? True : False;                                        \n      }                                                                         \n      case (heapNumber: HeapNumber): {                                          \n        return Convert\u003cfloat64\u003e(heapNumber) == 42 ? True : False;               \n      }                                                                         \n    }                                                                           \n  }                   \n```\nThis has been updated to work with the latest V8 version.\n\nNext, we need to update `src/init/bootstrappers.cc` to add/install this function\non the math object:\n```c++\n  SimpleInstallFunction(isolate_, math, \"is42\", Builtins::kMathIs42, 1, true);\n```\nAfter this we need to rebuild v8:\n```console\n$ env CPATH=/usr/include ninja -v -C out/x64.release_gcc\n```\n```console\n$ d8\nd8\u003e Math.is42(42)\ntrue\nd8\u003e Math.is42(2)\nfalse\n```\n\nIf we look at the generated code that Torque has produced in \n`out/x64.release_gcc/gen/torque-generated/src/builtins/math-tq-csa.cc` (we can\nrun it through the preprocessor using):\n```console\n$ clang++ --sysroot=build/linux/debian_sid_amd64-sysroot -isystem=./buildtools/third_party/libc++/trunk/include -isystem=buildtools/third_party/libc++/trunk/include -I. -E out/x64.release_gcc/gen/torque-generated/src/builtins/math-tq-csa.cc \u003e math.cc.pp\n```\nIf we open math.cc.pp and search for `Is42` we can find:\n```c++\nclass MathIs42Assembler : public CodeStubAssembler {                            \n public:                                                                        \n  using Descriptor = Builtin_MathIs42_InterfaceDescriptor;                      \n  explicit MathIs42Assembler(compiler::CodeAssemblerState* state) : CodeStubAssembler(state) {}\n  void GenerateMathIs42Impl();                                                  \n  Node* Parameter(Descriptor::ParameterIndices index) {                         \n    return CodeAssembler::Parameter(static_cast\u003cint\u003e(index));                   \n  }                                                                             \n};                                                                              \n                                                                                \nvoid Builtins::Generate_MathIs42(compiler::CodeAssemblerState* state) {         \n  MathIs42Assembler assembler(state);                                           \n  state-\u003eSetInitialDebugInformation(\"MathIs42\", \"out/x64.release_gcc/gen/torque-generated/src/builtins/math-tq-csa.cc\", 2121);\n  if (Builtins::KindOf(Builtins::kMathIs42) == Builtins::TFJ) {                 \n    assembler.PerformStackCheck(assembler.GetJSContextParameter());             \n  }                                                                             \n  assembler.GenerateMathIs42Impl();                                             \n}                                                                               \n                                                                                \nvoid MathIs42Assembler::GenerateMathIs42Impl() {     \n  ...\n```\nSo this is what gets generated by the Torque compiler and what we see\nabove is CodeStubAssemble class. \n\nIf we take a look in out/x64.release_gcc/gen/torque-generated/builtin-definitions-tq.h\nwe can find the following line that has been generated:\n```c++\nTFJ(MathIs42, 1, kReceiver, kX) \\                                               \n```\nNow, there is a section about the [TF_BUILTIN](#tf_builtin) macro, and it will\ncreate function declarations, and function and class definitions:\n\nNow, in src/builtins/builtins.h we have the following macros:\n```c++\nclass Builtins {\n public:\n\n  enum Name : int32_t {\n#define DEF_ENUM(Name, ...) k##Name,                                            \n    BUILTIN_LIST(DEF_ENUM, DEF_ENUM, DEF_ENUM, DEF_ENUM, DEF_ENUM, DEF_ENUM,    \n                 DEF_ENUM)                                                      \n#undef DEF_ENUM \n    ...\n  }\n\n#define DECLARE_TF(Name, ...) \\                                                 \n  static void Generate_##Name(compiler::CodeAssemblerState* state);             \n                                                                                \n  BUILTIN_LIST(IGNORE_BUILTIN, DECLARE_TF, DECLARE_TF, DECLARE_TF, DECLARE_TF,  \n               IGNORE_BUILTIN, DECLARE_ASM)\n```\nAnd `BUILTINS_LIST` is declared in src/builtins/builtins-definitions.h and this\nfile includes:\n```c++\n#include \"torque-generated/builtin-definitions-tq.h\"\n\n#define BUILTIN_LIST(CPP, TFJ, TFC, TFS, TFH, BCH, ASM)  \\                          \n  BUILTIN_LIST_BASE(CPP, TFJ, TFC, TFS, TFH, ASM)        \\                          \n  BUILTIN_LIST_FROM_TORQUE(CPP, TFJ, TFC, TFS, TFH, ASM) \\                          \n  BUILTIN_LIST_INTL(CPP, TFJ, TFS)                       \\                          \n  BUILTIN_LIST_BYTECODE_HANDLERS(BCH)     \n```\nNotice `BUILTIN_LIST_FROM_TORQUE`, this is how our MathIs42 gets included from\nbuiltin-definitions-tq.h. This is in turn included by builtins.h.\n\nIf we take a look at the this header after it has gone through the preprocessor\nwe can see what has been generated for MathIs42:\n```console\n$ clang++ --sysroot=build/linux/debian_sid_amd64-sysroot -isystem=./buildtools/third_party/libc++/trunk/include -isystem=buildtools/third_party/libc++/trunk/include -I. -I./out/x64.release_gcc/gen/ -E src/builtins/builtins.h \u003e builtins.h.pp\n```\nFirst MathIs42 will be come a member in the Name enum of the Builtins class:\n```c++\nclass Builtins {\n public:\n\n  enum Name : int32_t { \n    ...\n    kMathIs42,\n  };\n\n  static void Generate_MathIs42(compiler::CodeAssemblerState* state); \n```\nWe should also take a look in `src/builtins/builtins-descriptors.h` as the BUILTIN_LIST\nis used there two and specifically to our current example there is a\n`DEFINE_TFJ_INTERFACE_DESCRIPTOR` macro used:\n```c++\nBUILTIN_LIST(IGNORE_BUILTIN, DEFINE_TFJ_INTERFACE_DESCRIPTOR,\n             DEFINE_TFC_INTERFACE_DESCRIPTOR, DEFINE_TFS_INTERFACE_DESCRIPTOR,\n             DEFINE_TFH_INTERFACE_DESCRIPTOR, IGNORE_BUILTIN,\n             DEFINE_ASM_INTERFACE_DESCRIPTOR)\n\n#define DEFINE_TFJ_INTERFACE_DESCRIPTOR(Name, Argc, ...)                \\\n  struct Builtin_##Name##_InterfaceDescriptor {                         \\\n    enum ParameterIndices {                                             \\\n      kJSTarget = compiler::CodeAssembler::kTargetParameterIndex,       \\\n      ##__VA_ARGS__,                                                    \\\n      kJSNewTarget,                                                     \\\n      kJSActualArgumentsCount,                                          \\\n      kContext,                                                         \\\n      kParameterCount,                                                  \\\n    };                                                                  \\\n  }; \n```\nSo the above will generate the following code but this time for builtins.cc:\n```console\n$ clang++ --sysroot=build/linux/debian_sid_amd64-sysroot -isystem=./buildtools/third_party/libc++/trunk/include -isystem=buildtools/third_party/libc++/trunk/include -I. -I./out/x64.release_gcc/gen/ -E src/builtins/builtins.cc \u003e builtins.cc.pp\n```\n\n```c++\nstruct Builtin_MathIs42_InterfaceDescriptor { \n  enum ParameterIndices { \n    kJSTarget = compiler::CodeAssembler::kTargetParameterIndex,\n    kReceiver,\n    kX,\n    kJSNewTarget,\n    kJSActualArgumentsCount,\n    kContext,\n    kParameterCount,\n  };\n\nconst BuiltinMetadata builtin_metadata[] = {\n  ...\n  {\"MathIs42\", Builtins::TFJ, {1, 0}}\n  ...\n};\n```\nBuiltinMetadata is a struct defined in builtins.cc and in our case the name \nis passed, then the type, and the last struct is specifying the number of parameters\nand the last 0 is unused as far as I can tell and only there make it different\nfrom the constructor that takes an Address parameter.\n\nSo, where is `Generate_MathIs42` used:\n```c++\nvoid SetupIsolateDelegate::SetupBuiltinsInternal(Isolate* isolate) {\n  Code code;\n  ...\n  code = BuildWithCodeStubAssemblerJS(isolate, index, \u0026Builtins::Generate_MathIs42, 1, \"MathIs42\");\n  AddBuiltin(builtins, index++, code);\n  ...\n```\n`BuildWithCodeStubAssemblerJS` can be found in `src/builtins/setup-builtins-internal.cc`\n```c++\nCode BuildWithCodeStubAssemblerJS(Isolate* isolate, int32_t builtin_index,\n                                  CodeAssemblerGenerator generator, int argc,\n                                  const char* name) {\n  Zone zone(isolate-\u003eallocator(), ZONE_NAME);\n  const int argc_with_recv = (argc == kDontAdaptArgumentsSentinel) ? 0 : argc + 1;\n  compiler::CodeAssemblerState state(\n      isolate, \u0026zone, argc_with_recv, Code::BUILTIN, name,\n      PoisoningMitigationLevel::kDontPoison, builtin_index);\n  generator(\u0026state);\n  Handle\u003cCode\u003e code = compiler::CodeAssembler::GenerateCode(\n      \u0026state, BuiltinAssemblerOptions(isolate, builtin_index));\n  return *code;\n```\nLets add a conditional break point so that we can stop in this function when\n`MathIs42` is passed in:\n```console\n(gdb) br setup-builtins-internal.cc:161\n(gdb) cond 1 ((int)strcmp(name, \"MathIs42\")) == 0\n```\nWe can see that we first create a new `CodeAssemblerState`, which we say previously\nwas that type that the `Generate_MathIs42` function takes. TODO: look into this class\na litte more.\nAfter this `generator` will be called with the newly created state passed in:\n```console \n(gdb) p generator\n$8 = (v8::internal::(anonymous namespace)::CodeAssemblerGenerator) 0x5619fd61b66e \u003cv8::internal::Builtins::Generate_MathIs42(v8::internal::compiler::CodeAssemblerState*)\u003e\n```\nTODO: Take a closer look at generate and how that code works. \nAfter generate returns we will have the following call:\n```c++\n  generator(\u0026state);                                                               \n  Handle\u003cCode\u003e code = compiler::CodeAssembler::GenerateCode(                       \n      \u0026state, BuiltinAssemblerOptions(isolate, builtin_index));                    \n  return *code;\n```\nThen next thing that will happen is the code returned will be added to the builtins\nby calling `SetupIsolateDelegate::AddBuiltin`:\n```c++\nvoid SetupIsolateDelegate::AddBuiltin(Builtins* builtins, int index, Code code) {\n  builtins-\u003eset_builtin(index, code);                                           \n} \n```\n`set_builtins` can be found in src/builtins/builtins.cc` and looks like this:\n```c++\nvoid Builtins::set_builtin(int index, Code builtin) {                           \n  isolate_-\u003eheap()-\u003eset_builtin(index, builtin);                                \n}\n```\nAnd Heap::set_builtin does:\n```c++\n void Heap::set_builtin(int index, Code builtin) {\n  isolate()-\u003ebuiltins_table()[index] = builtin.ptr();\n}\n```\nSo this is how the builtins_table is populated.\n \nAnd when is `SetupBuiltinsInternal` called?  \nIt is called from `SetupIsolateDelegat::SetupBuiltins` which is called from Isolate::Init.\n\nJust to recap before I loose track of what is going on...We have math.tq, which\nis the torque source file. This is parsed by the torque compiler/parser and it\nwill generate c++ headers and source files, one of which will be a\nCodeStubAssembler class for our MathI42 function. It will also generate the \n\"torque-generated/builtin-definitions-tq.h. \nAfter this has happened the sources need to be compiled into object files. After\nthat if a snapshot is configured to be created, mksnapshot will create a new\nIsolate and in that process the MathIs42 builtin will get added. Then a context will\nbe created and saved. The snapshot can then be deserialized into an Isoalte as\nsome later point.\n\nAlright, so we have seen what gets generated for the function MathIs42 but how\ndoes this get \"hooked\" but to enable us to call `Math.is42(11)`?  \n\nIn bootstrapper.cc we can see a number of lines:\n```c++\n SimpleInstallFunction(isolate_, math, \"trunc\", Builtins::kMathTrunc, 1, true); \n```\nAnd we are going to add a line like the following:\n```c++\n SimpleInstallFunction(isolate_, math, \"is42\", Builtins::kMathIs42, 1, true);\n```\nThe signature for `SimpleInstallFunction` looks like this\n```c++\nV8_NOINLINE Handle\u003cJSFunction\u003e SimpleInstallFunction(\n    Isolate* isolate, Handle\u003cJSObject\u003e base, const char* name,\n    Builtins::Name call, int len, bool adapt,\n    PropertyAttributes attrs = DONT_ENUM) {\n  Handle\u003cString\u003e internalized_name = isolate-\u003efactory()-\u003eInternalizeUtf8String(name);\n  Handle\u003cJSFunction\u003e fun = SimpleCreateFunction(isolate, internalized_name, call, len, adapt);       \n  JSObject::AddProperty(isolate, base, internalized_name, fun, attrs);          \n  return fun;                                                                   \n} \n```\nSo we see that the function is added as a property to the Math object.\nNotice that we also have to add `kMathIs42` to the Builtins class which is now\npart of the builtins_table_ array which we went through above.\n\n#### Transitioning/Transient\nIn torgue source files we can sometimes see types declared as `transient`, and\nfunctions that have a `transitioning` specifier. In V8 HeapObjects can change\nat runtime (I think an example of this would be deleting an element in an array\nwhich would transition it to a different type of array HoleyElementArray or\nsomething like that. TODO: verify and explain this). And a function that calls\nJavaScript which cause such a transition is marked with transitioning.\n\n\n#### Callables\nAre like functions is js/c++ but have some additional capabilities and there\nare several different types of callables:\n\n##### macro callables\nThese correspond to generated CodeStubAssebler C++ that will be inlined at\nthe callsite.\n\n##### builtin callables\nThese will become V8 builtins with info added to builtin-definitions.h (via\nthe include of torque-generated/builtin-definitions-tq.h). There is only one\ncopy of this and this will be a call instead of being inlined as is the case\nwith macros.\n\n##### runtime callables\n\n##### intrinsic callables\n\n#### Explicit parameters\nmacros and builtins can have parameters. For example:\n```\n@export\nmacro HelloWorld1(msg: JSAny) {\n  Print(msg);\n}\n```\nAnd we can call this from another macro like this:\n```\n@export\nmacro HelloWorld() {\n  HelloWorld1('Hello World');\n}\n```\n\n#### Implicit parameters\nIn the previous section we showed explicit parameters but we can also have\nimplicit parameters:\n```\n@export\nmacro HelloWorld2(implicit msg: JSAny)() {\n  Print(msg);\n}\n@export\nmacro HelloWorld() {\n  const msg = 'Hello implicit';\n  HelloWorld2();\n}\n```\n\n### Troubleshooting\nCompilation error when including `src/objects/objects-inl.h:\n```console\n/home/danielbevenius/work/google/v8_src/v8/src/objects/object-macros.h:263:14: error: no declaration matches ‘bool v8::internal::HeapObject::IsJSCollator() const’\n```\nDoes this need i18n perhaps?\n```console\n$ gn args --list out/x64.release_gcc | grep i18n\nv8_enable_i18n_support\n```\n\n```console\nusr/bin/ld: /tmp/ccJOrUMl.o: in function `v8::internal::MaybeHandle\u003cv8::internal::Object\u003e::Check() const':\n/home/danielbevenius/work/google/v8_src/v8/src/handles/maybe-handles.h:44: undefined reference to `V8_Fatal(char const*, ...)'\ncollect2: error: ld returned 1 exit status\n```\nV8_Fatal is referenced but not defined in v8_monolith.a:\n```console\n$ nm libv8_monolith.a | grep V8_Fatal | c++filt \n...\nU V8_Fatal(char const*, int, char const*, ...)\n```\nAnd I thought it might be defined in libv8_libbase.a but it is the same there.\nActually, I was looking at the wrong symbol. This was not from the logging.o \nobject file. If we look at it we find:\n```console\nv8_libbase/logging.o:\n...\n0000000000000000 T V8_Fatal(char const*, int, char const*, ...)\n```\nIn out/x64.release/obj/logging.o we can find it defined:\n```console\n$ nm -C  libv8_libbase.a | grep -A 50 logging.o | grep V8_Fatal\n0000000000000000 T V8_Fatal(char const*, int, char const*, ...)\n```\n`T` means that the symbol is in the text section.\nSo if the linker is able to find libv8_libbase.a it should be able to resolve\nthis.\n\nSo we need to make sure the linker can find the directory where the libraries\nare located ('-Wl,-Ldir'), and also that it will include the library ('-Wl,-llibname')\n\nWith this in place I can see that the linker can open the archive:\n```console\nattempt to open /home/danielbevenius/work/google/v8_src/v8/out/x64.release_gcc/obj/libv8_libbase.so failed\nattempt to open /home/danielbevenius/work/google/v8_src/v8/out/x64.release_gcc/obj/libv8_libbase.a succeeded\n/home/danielbevenius/work/google/v8_src/v8/out/x64.release_gcc/obj/libv8_libbase.a\n```\nBut I'm still getting the same linking error. If we look closer at the error message\nwe can see that it is maybe-handles.h that is complaining. Could it be that the\norder is incorrect when linking. libv8_libbase.a needs to come after libv8_monolith\nSomething I noticed is that even though the library libv8_libbase.a is found it\ndoes not look like the linker actually reads the object files. I can see that it\ndoes this for libv8_monolith.a:\n```console\n(/home/danielbevenius/work/google/v8_src/v8/out/x64.release_gcc/obj/libv8_monolith.a)common-node-cache.o\n```\nHmm, actually looking at the signature of the function it is V8_Fatal(char const*, ...)\nand not char const*, int, char const*, ...)\n\nFor a debug build it will be:\n```\n    void V8_Fatal(const char* file, int line, const char* format, ...);\n```\nAnd else\n```\n    void V8_Fatal(const char* format, ...);\n```\nSo it looks like I need to set debug to false. With this the V8_Fatal symbol\nin logging.o is:\n```console\n$ nm -C out/x64.release_gcc/obj/v8_libbase/logging.o | grep V8_Fatal\n0000000000000000 T V8_Fatal(char const*, ...)\n```\n\n\n### V8 Build artifacts\nWhat is actually build when you specify \nv8_monolithic:\nWhen this type is chosen the build cannot be a component build, there is an\nassert for this. In this case a static library build:\n```\nif (v8_monolithic) {                                                            \n  # A component build is not monolithic.                                        \n  assert(!is_component_build)                                                   \n                                                                                \n  # Using external startup data would produce separate files.                   \n  assert(!v8_use_external_startup_data)                                         \n  v8_static_library(\"v8_monolith\") {                                            \n    deps = [                                                                    \n      \":v8\",                                                                    \n      \":v8_libbase\",                                                            \n      \":v8_libplatform\",                                                        \n      \":v8_libsampler\",                                                         \n      \"//build/win:default_exe_manifest\",                                       \n    ]                                                                           \n                                                                                \n    configs = [ \":internal_config\" ]                                            \n  }                                                                             \n}\n```\nNotice that the builtin function is called `static_library` so is a template\nthat can be found in `gni/v8.gni` \n\nv8_static_library:\nThis will use source_set instead of creating a static library when compiling.\nWhen set to false, the object files that would be included in the linker command.\nThe can speed up the build as the creation of the static libraries is skipped.\nBut this does not really help when linking to v8 externally as from this project.\n\nis_component_build:\nThis will compile targets declared as components as shared libraries.\nAll the v8_components in BUILD.gn will be built as .so files in the output\ndirector (not the obj directory which is the case for static libraries).\n\nSo the only two options are the v8_monolith or is_component_build where it\nmight be an advantage of being able to build a single component and not have\nto rebuild the whole monolith at times.\n\n### wee8\n`libwee8` can be produced which is a library which only supports WebAssembly\nand does not support JavaScript.\n```console\n$ ninja -C out/wee8 wee8\n```\n\n\n### V8 Internal Isolate\n`src/execution/isolate.h` is where you can find the v8::internal::Isolate.\n```c++\nclass V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {\n\n```\nAnd HiddenFactory is just to allow Isolate to inherit privately from Factory\nwhich can be found in src/heap/factory.h.\n\n### Startup Walk through\nThis section will walk through the start up on V8 by using the hello_world example\nin this project:\n```console\n$ LD_LIBRARY_PATH=../v8_src/v8/out/x64.release_gcc/ lldb ./hello-world\n(lldb) br s -n main\nBreakpoint 1: where = hello-world`main + 25 at hello-world.cc:41:38, address = 0x0000000000402821\n```\n```console\n    V8::InitializeExternalStartupData(argv[0]);\n```\nThis call will land in `api.cc` which will just delegate the call to and internal\n(internal namespace that is). If you try to step into this function you will\njust land on the next line in hello_world. This is because we compiled v8 without\nexternal start up data so this function will be empty:\n```console\n$ objdump -Cd out/x64.release_gcc/obj/v8_base_without_compiler/startup-data-util.o\nDisassembly of section .text._ZN2v88internal37InitializeExternalStartupDataFromFileEPKc:\n\n0000000000000000 \u003cv8::internal::InitializeExternalStartupDataFromFile(char const*)\u003e:\n   0:\tc3                   \tretq\n```\nNext, we have:\n```console\n    std::unique_ptr\u003cPlatform\u003e platform = platform::NewDefaultPlatform();\n```\nThis will land in `src/libplatform/default-platform.cc` which will create a new\nDefaultPlatform.\n\n```c++\nIsolate* isolate = Isolate::New(create_params);\n```\nThis will call Allocate: \n```c++\nIsolate* isolate = Allocate();\n```\n```c++\nIsolate* Isolate::Allocate() {\n  return reinterpret_cast\u003cIsolate*\u003e(i::Isolate::New());\n}\n```\n\nRemember that the internal Isolate can be found in `src/execution/isolate.h`.\nIn `src/execution/isolate.cc` we find `Isolate::New`\n```c++\nIsolate* Isolate::New(IsolateAllocationMode mode) {\n  std::unique_ptr\u003cIsolateAllocator\u003e isolate_allocator = std::make_unique\u003cIsolateAllocator\u003e(mode);\n  void* isolate_ptr = isolate_allocator-\u003eisolate_memory();\n  Isolate* isolate = new (isolate_ptr) Isolate(std::move(isolate_allocator));\n```\nSo we first create an IsolateAllocator instance which will allocate memory for\na single Isolate instance. This is then passed into the Isolate constructor,\nnotice the usage of `new` here, this is just a normal heap allocation. \n\nThe default new operator has been deleted and an override provided that takes\na void pointer, which is just returned: \n```c++\n  void* operator new(size_t, void* ptr) { return ptr; }\n  void* operator new(size_t) = delete;\n  void operator delete(void*) = delete;\n```\nIn this case it just returns the memory allocateed by isolate-memory().\nThe reason for doing this is that using the new operator not only invokes the\nnew operator but the compiler will also add a call the types constructor passing\nin the address of the allocated memory.\n```c++\nIsolate::Isolate(std::unique_ptr\u003ci::IsolateAllocator\u003e isolate_allocator)\n    : isolate_data_(this),\n      isolate_allocator_(std::move(isolate_allocator)),\n      id_(isolate_counter.fetch_add(1, std::memory_order_relaxed)),\n      allocator_(FLAG_trace_zone_stats\n                     ? new VerboseAccountingAllocator(\u0026heap_, 256 * KB)\n                     : new AccountingAllocator()),\n      builtins_(this),\n      rail_mode_(PERFORMANCE_ANIMATION),\n      code_event_dispatcher_(new CodeEventDispatcher()),\n      jitless_(FLAG_jitless),\n#if V8_SFI_HAS_UNIQUE_ID\n      next_unique_sfi_id_(0),\n#endif\n      cancelable_task_manager_(new CancelableTaskManager()) {\n```\nNotice that  `isolate_data_` will be populated by calling the constructor which\ntakes an pointer to an Isolate.\n```c++\nclass IsolateData final {\n public:\n  explicit IsolateData(Isolate* isolate) : stack_guard_(isolate) {}\n```\n\nBack in Isolate's constructor we have:\n```c++\n#define ISOLATE_INIT_LIST(V)                                                   \\\n  /* Assembler state. */                                                       \\\n  V(FatalErrorCallback, exception_behavior, nullptr)                           \\\n  ...\n\n#define ISOLATE_INIT_EXECUTE(type, name, initial_value) \\                           \n  name##_ = (initial_value);                                                        \n  ISOLATE_INIT_LIST(ISOLATE_INIT_EXECUTE)                                           \n#undef ISOLATE_INIT_EXECUTE\n```\nSo lets expand the first entry to understand what is going on:\n```c++\n   exception_behavior_ = (nullptr);\n   oom_behavior_ = (nullptr);\n   event_logger_ = (nullptr);\n   allow_code_gen_callback_ = (nullptr);\n   modify_code_gen_callback_ = (nullptr);\n   allow_wasm_code_gen_callback_ = (nullptr);\n   wasm_module_callback_ = (\u0026NoExtension);\n   wasm_instance_callback_ = (\u0026NoExtension);\n   wasm_streaming_callback_ = (nullptr);\n   wasm_threads_enabled_callback_ = (nullptr);\n   wasm_load_source_map_callback_ = (nullptr);\n   relocatable_top_ = (nullptr);\n   string_stream_debug_object_cache_ = (nullptr);\n   string_stream_current_security_token_ = (Object());\n   api_external_references_ = (nullptr);\n   external_reference_map_ = (nullptr);\n   root_index_map_ = (nullptr);\n   default_microtask_queue_ = (nullptr);\n   turbo_statistics_ = (nullptr);\n   code_tracer_ = (nullptr);\n   per_isolate_assert_data_ = (0xFFFFFFFFu);\n   promise_reject_callback_ = (nullptr);\n   snapshot_blob_ = (nullptr);\n   code_and_metadata_size_ = (0);\n   bytecode_and_metadata_size_ = (0);\n   external_script_source_size_ = (0);\n   is_profiling_ = (false);\n   num_cpu_profilers_ = (0);\n   formatting_stack_trace_ = (false);\n   debug_execution_mode_ = (DebugInfo::kBreakpoints);\n   code_coverage_mode_ = (debug::CoverageMode::kBestEffort);\n   type_profile_mode_ = (debug::TypeProfileMode::kNone);\n   last_stack_frame_info_id_ = (0);\n   last_console_context_id_ = (0);\n   inspector_ = (nullptr);\n   next_v8_call_is_safe_for_termination_ = (false);\n   only_terminate_in_safe_scope_ = (false);\n   detailed_source_positions_for_profiling_ = (FLAG_detailed_line_info);\n   embedder_wrapper_type_index_ = (-1);\n   embedder_wrapper_object_index_ = (-1);\n```\nSo all of the entries in this list will become private members of the\nIsolate class after the preprocessor is finished. There will also be public\nassessor to get and set these initial values values (which is the last entry\nin the ISOLATE_INIT_LIST above.\n\nBack in isolate.cc constructor we have:\n```c++\n#define ISOLATE_INIT_ARRAY_EXECUTE(type, name, length) \\\n  memset(name##_, 0, sizeof(type) * length);\n  ISOLATE_INIT_ARRAY_LIST(ISOLATE_INIT_ARRAY_EXECUTE)\n#undef ISOLATE_INIT_ARRAY_EXECUTE\n#define ISOLATE_INIT_ARRAY_LIST(V)                                             \\\n  /* SerializerDeserializer state. */                                          \\\n  V(int32_t, jsregexp_static_offsets_vector, kJSRegexpStaticOffsetsVectorSize) \\\n  ...\n\n  InitializeDefaultEmbeddedBlob();\n  MicrotaskQueue::SetUpDefaultMicrotaskQueue(this);\n```\nAfter that we have created a new Isolate, we were in this function call:\n```c++\n  Isolate* isolate = new (isolate_ptr) Isolate(std::move(isolate_allocator));\n```\nAfter this we will be back in `api.cc`:\n```c++\n  Initialize(isolate, params);\n```\n```c++\nvoid Isolate::Initialize(Isolate* isolate,\n                         const v8::Isolate::CreateParams\u0026 params) {\n```\nWe are not using any external snapshot data so the following will be false:\n```c++\n  if (params.snapshot_blob != nullptr) {\n    i_isolate-\u003eset_snapshot_blob(params.snapshot_blob);\n  } else {\n    i_isolate-\u003eset_snapshot_blob(i::Snapshot::DefaultSnapshotBlob());\n```\n```console\n(gdb) p snapshot_blob_\n$7 = (const v8::StartupData *) 0x0\n(gdb) n\n(gdb) p i_isolate-\u003esnapshot_blob_\n$8 = (const v8::StartupData *) 0x7ff92d7d6cf0 \u003cv8::internal::blob\u003e\n```\n`snapshot_blob_` is also one of the members that was set up with ISOLATE_INIT_LIST.\nSo we are setting up the Isolate instance for creation. \n\n```c++\nIsolate::Scope isolate_scope(isolate);                                        \nif (!i::Snapshot::Initialize(i_isolate)) { \n```\nIn `src/snapshot/snapshot-common.cc` we find \n```c++\nbool Snapshot::Initialize(Isolate* isolate) {\n  ...\n  const v8::StartupData* blob = isolate-\u003esnapshot_blob();\n  Vector\u003cconst byte\u003e startup_data = ExtractStartupData(blob);\n  Vector\u003cconst byte\u003e read_only_data = ExtractReadOnlyData(blob);\n  SnapshotData startup_snapshot_data(MaybeDecompress(startup_data));\n  SnapshotData read_only_snapshot_data(MaybeDecompress(read_only_data));\n  StartupDeserializer startup_deserializer(\u0026startup_snapshot_data);\n  ReadOnlyDeserializer read_only_deserializer(\u0026read_only_snapshot_data);\n  startup_deserializer.SetRehashability(ExtractRehashability(blob));\n  read_only_deserializer.SetRehashability(ExtractRehashability(blob));\n\n  bool success = isolate-\u003eInitWithSnapshot(\u0026read_only_deserializer, \u0026startup_deserializer);\n```\nSo we get the blob and create deserializers for it which are then passed to\n`isolate-\u003eInitWithSnapshot` which delegated to `Isolate::Init`. The blob will\nhave be create previously using `mksnapshot` (more on this can be found later).\n\nThis will use a `FOR_EACH_ISOLATE_ADDRESS_NAME` macro to assign to the\n`isolate_addresses_` field:\n```c++\nisolate_addresses_[IsolateAddressId::kHandlerAddress] = reinterpret_cast\u003cAddress\u003e(handler_address());\nisolate_addresses_[IsolateAddressId::kCEntryFPAddress] = reinterpret_cast\u003cAddress\u003e(c_entry_fp_address());\nisolate_addresses_[IsolateAddressId::kCFunctionAddress] = reinterpret_cast\u003cAddress\u003e(c_function_address());\nisolate_addresses_[IsolateAddressId::kContextAddress] = reinterpret_cast\u003cAddress\u003e(context_address());\nisolate_addresses_[IsolateAddressId::kPendingExceptionAddress] = reinterpret_cast\u003cAddress\u003e(pending_exception_address());\nisolate_addresses_[IsolateAddressId::kPendingHandlerContextAddress] = reinterpret_cast\u003cAddress\u003e(pending_handler_context_address());\n isolate_addresses_[IsolateAddressId::kPendingHandlerEntrypointAddress] = reinterpret_cast\u003cAddress\u003e(pending_handler_entrypoint_address());\n isolate_addresses_[IsolateAddressId::kPendingHandlerConstantPoolAddress] = reinterpret_cast\u003cAddress\u003e(pending_handler_constant_pool_address());\n isolate_addresses_[IsolateAddressId::kPendingHandlerFPAddress] = reinterpret_cast\u003cAddress\u003e(pending_handler_fp_address());\n isolate_addresses_[IsolateAddressId::kPendingHandlerSPAddress] = reinterpret_cast\u003cAddress\u003e(pending_handler_sp_address());\n isolate_addresses_[IsolateAddressId::kExternalCaughtExceptionAddress] = reinterpret_cast\u003cAddress\u003e(external_caught_exception_address());\n isolate_addresses_[IsolateAddressId::kJSEntrySPAddress] = reinterpret_cast\u003cAddress\u003e(js_entry_sp_address());\n```\nAfter this we have a number of members that are assigned to:\n```c++\n  compilation_cache_ = new CompilationCache(this);\n  descriptor_lookup_cache_ = new DescriptorLookupCache();\n  inner_pointer_to_code_cache_ = new InnerPointerToCodeCache(this);\n  global_handles_ = new GlobalHandles(this);\n  eternal_handles_ = new EternalHandles();\n  bootstrapper_ = new Bootstrapper(this);\n  handle_scope_implementer_ = new HandleScopeImplementer(this);\n  load_stub_cache_ = new StubCache(this);\n  store_stub_cache_ = new StubCache(this);\n  materialized_object_store_ = new MaterializedObjectStore(this);\n  regexp_stack_ = new RegExpStack();\n  regexp_stack_-\u003eisolate_ = this;\n  date_cache_ = new DateCache();\n  heap_profiler_ = new HeapProfiler(heap());\n  interpreter_ = new interpreter::Interpreter(this);\n  compiler_dispatcher_ =\n      new CompilerDispatcher(this, V8::GetCurrentPlatform(), FLAG_stack_size);\n```\nAfter this we have:\n```c++\nisolate_data_.external_reference_table()-\u003eInit(this);\n```\nThis will land in `src/codegen/external-reference-table.cc` where we have:\n```c++\nvoid ExternalReferenceTable::Init(Isolate* isolate) {                              \n  int index = 0;                                                                   \n  Add(kNullAddress, \u0026index);                                                       \n  AddReferences(isolate, \u0026index);                                                  \n  AddBuiltins(\u0026index);                                                             \n  AddRuntimeFunctions(\u0026index);                                                     \n  AddIsolateAddresses(isolate, \u0026index);                                            \n  AddAccessors(\u0026index);                                                            \n  AddStubCache(isolate, \u0026index);                                                   \n  AddNativeCodeStatsCounters(isolate, \u0026index);                                     \n  is_initialized_ = static_cast\u003cuint32_t\u003e(true);                                   \n                                                                                   \n  CHECK_EQ(kSize, index);                                                          \n}\n\nvoid ExternalReferenceTable::Add(Address address, int* index) {                 \nref_addr_[(*index)++] = address;                                                \n} \n\nAddress ref_addr_[kSize];\n```\n\nNow, lets take a look at `AddReferences`: \n```c++\nAdd(ExternalReference::abort_with_reason().address(), index); \n```\nWhat are ExternalReferences?   \nThey represent c++ addresses used in generated code.\n\nAfter that we have AddBuiltins:\n```c++\nstatic const Address c_builtins[] = {                                         \n      (reinterpret_cast\u003cv8::internal::Address\u003e(\u0026Builtin_HandleApiCall)), \n      ...\n\nAddress Builtin_HandleApiCall(int argc, Address* args, Isolate* isolate);\n```\nI can see that the function declaration is in external-reference.h but the\nimplementation is not there. Instead this is defined in `src/builtins/builtins-api.cc`:\n```c++\nBUILTIN(HandleApiCall) {                                                           \n(will expand to:)\n\nV8_WARN_UNUSED_RESULT static Object Builtin_Impl_HandleApiCall(\n      BuiltinArguments args, Isolate* isolate);\n\nV8_NOINLINE static Address Builtin_Impl_Stats_HandleApiCall(\n      int args_length, Address* args_object, Isolate* isolate) {\n    BuiltinArguments args(args_length, args_object);\n    RuntimeCallTimerScope timer(isolate,\n                                RuntimeCallCounterId::kBuiltin_HandleApiCall);\n    TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT(\"v8.runtime\"), \"V8.Builtin_HandleApiCall\");\n    return CONVERT\n}\nV8_WARN_UNUSED_RESULT Address Builtin_HandleApiCall(\n      int args_length, Address* args_object, Isolate* isolate) {\n    DCHECK(isolate-\u003econtext().is_null() || isolate-\u003econtext().IsContext());\n    if (V8_UNLIKELY(TracingFlags::is_runtime_stats_enabled())) {\n      return Builtin_Impl_Stats_HandleApiCall(args_length, args_object, isolate);\n    }\n    BuiltinArguments args(args_length, args_object);\n    return CONVERT_OBJECT(Builtin_Impl_HandleApiCall(args, isolate));\n  }\n\n  V8_WARN_UNUSED_RESULT static Object Builtin_Impl_HandleApiCall(\n      BuiltinArguments args, Isolate* isolate) {\n    HandleScope scope(isolate);                                                      \n    Handle\u003cJSFunction\u003e function = args.target();                                  \n    Handle\u003cObject\u003e receiver = args.receiver();                                    \n    Handle\u003cHeapObject\u003e new_target = args.new_target();                               \n    Handle\u003cFunctionTemplateInfo\u003e fun_data(function-\u003eshared().get_api_func_data(), \n                                        isolate);                                  \n    if (new_target-\u003eIsJSReceiver()) {                                                \n      RETURN_RESULT_OR_FAILURE(                                                   \n          isolate, HandleApiCallHelper\u003ctrue\u003e(isolate, function, new_target,          \n                                             fun_data, receiver, args));             \n    } else {                                                                         \n      RETURN_RESULT_OR_FAILURE(                                                      \n          isolate, HandleApiCallHelper\u003cfalse\u003e(isolate, function, new_target,         \n                                            fun_data, receiver, args));            \n    }\n  }\n``` \nThe `BUILTIN` macro can be found in `src/builtins/builtins-utils.h`:\n```c++\n#define BUILTIN(name)                                                       \\\n  V8_WARN_UNUSED_RESULT static Object Builtin_Impl_##name(                  \\\n      BuiltinArguments args, Isolate* isolate);\n```\n\n```c++\n  if (setup_delegate_ == nullptr) {                                                 \n    setup_delegate_ = new SetupIsolateDelegate(create_heap_objects);            \n  } \n\n  if (!setup_delegate_-\u003eSetupHeap(\u0026heap_)) {                                    \n    V8::FatalProcessOutOfMemory(this, \"heap object creation\");                  \n    return false;                                                               \n  }    \n```\nThis does nothing in the current code path and the code comment says that the\nheap will be deserialized from the snapshot and true will be returned.\n\n```c++\nInitializeThreadLocal();\nstartup_deserializer-\u003eDeserializeInto(this);\n```\n```c++\nDisallowHeapAllocation no_gc;                                               \nisolate-\u003eheap()-\u003eIterateSmiRoots(this);                                     \nisolate-\u003eheap()-\u003eIterateStrongRoots(this, VISIT_FOR_SERIALIZATION);         \nIterate(isolate, this);                                                     \nisolate-\u003eheap()-\u003eIterateWeakRoots(this, VISIT_FOR_SERIALIZATION);           \nDeserializeDeferredObjects();                                               \nRestoreExternalReferenceRedirectors(accessor_infos());                      \nRestoreExternalReferenceRedirectors(call_handler_infos());\n```\nIn `heap.cc` we find IterateSmiRoots` which takes a pointer to a `RootVistor`.\nRootVisitor is used for visiting and modifying (optionally) the pointers contains\nin roots. This is used in garbage collection and also in serializing and deserializing\nsnapshots.\n\n### Roots\nRootVistor:\n```c++\nclass RootVisitor {\n public:\n  virtual void VisitRootPointers(Root root, const char* description,\n                                 FullObjectSlot start, FullObjectSlot end) = 0;\n\n  virtual void VisitRootPointer(Root root, const char* description,\n                                FullObjectSlot p) {\n    VisitRootPointers(root, description, p, p + 1);\n  }\n \n  static const char* RootName(Root root);\n```\nRoot is an enum in `src/object/visitors.h`. This enum is generated by a macro\nand expands to:\n```c++\nenum class Root {                                                               \n  kStringTable,\n  kExternalStringsTable,\n  kReadOnlyRootList,\n  kStrongRootList,\n  kSmiRootList,\n  kBootstrapper,\n  kTop,\n  kRelocatable,\n  kDebug,\n  kCompilationCache,\n  kHandleScope,\n  kBuiltins,\n  kGlobalHandles,\n  kEternalHandles,\n  kThreadManager,\n  kStrongRoots,\n  kExtensions,\n  kCodeFlusher,\n  kPartialSnapshotCache,\n  kReadOnlyObjectCache,\n  kWeakCollections,\n  kWrapperTracing,\n  kUnknown,\n  kNumberOfRoots                                                            \n}; \n```\nThese can be displayed using:\n```console\n$ ./test/roots_test --gtest_filter=RootsTest.visitor_roots\n```\nJust to keep things clear for myself here, these visitor roots are only used\nfor GC and serialization/deserialization (at least I think so) and should not\nbe confused with the RootIndex enum in `src/roots/roots.h`.\n\nLets set a break point in `mksnapshot` and see if we can find where one of the\nabove Root enum elements is used to make it a little more clear what these are\nused for.\n```console\n$ lldb ../v8_src/v8/out/x64.debug/mksnapshot \n(lldb) target create \"../v8_src/v8/out/x64.debug/mksnapshot\"\nCurrent executable set to '../v8_src/v8/out/x64.debug/mksnapshot' (x86_64).\n(lldb) br s -n main\nBreakpoint 1: where = mksnapshot`main + 42, address = 0x00000000009303ca\n(lldb) r\n```\nWhat this does is that it creates an V8 environment (Platform, Isolate, Context)\n and then saves it to a file, either a binary file on disk but it can also save\nit to a .cc file that can be used in programs in which case the binary is a byte array.\nIt does this in much the same way as the hello-world example create a platform\nand then initializes it, and the creates and initalizes a new Isolate. \nAfter the Isolate a new Context will be create using the Isolate. If there was\nan embedded-src flag passed to mksnaphot it will be run.\n\nStartupSerializer will use the Root enum elements for example and the deserializer\nwill use the same enum elements.\n\nAdding a script to a snapshot:\n```\n$ gdb ../v8_src/v8/out/x64.release_gcc/mksnapshot --embedded-src=\"$PWD/embed.js\"\n```\n\nTODO: Look into CreateOffHeapTrampolines.\n\nSo the VisitRootPointers function takes one of these Root's and visits all those\nroots.  In our case the first Root to be visited is Heap::IterateSmiRoots:\n```c++\nvoid Heap::IterateSmiRoots(RootVisitor* v) {                                        \n  ExecutionAccess access(isolate());                                                \n  v-\u003eVisitRootPointers(Root::kSmiRootList, nullptr,                                 \n                       roots_table().smi_roots_begin(),                             \n                       roots_table().smi_roots_end());                              \n  v-\u003eSynchronize(VisitorSynchronization::kSmiRootList);                             \n}\n```\nAnd here we can see that it is using `Root::kSmiRootList`, and passing nullptr\nfor the description argument (I wonder what this is used for?). Next, comes\nthe start and end arguments. \n```console\n(lldb) p roots_table().smi_roots_begin()\n(v8::internal::FullObjectSlot) $5 = {\n  v8::internal::SlotBase\u003cv8::internal::FullObjectSlot, unsigned long, 8\u003e = (ptr_ = 50680614097760)\n}\n```\nWe can list all the values of roots_table using:\n```console\n(lldb) expr -A -- roots_table()\n```\nIn `src/snapshot/deserializer.cc` we can find VisitRootPointers:\n```c++\nvoid Deserializer::VisitRootPointers(Root root, const char* description,\n                                     FullObjectSlot start, FullObjectSlot end)\n  ReadData(FullMaybeObjectSlot(start), FullMaybeObjectSlot(end),\n           SnapshotSpace::kNew, kNullAddress);\n```\nNotice that description is never used. `ReadData`is in the same source file:\n\nThe class SnapshotByteSource has a `data` member that is initialized upon construction\nfrom a const char* or a Vector\u003cconst byte\u003e. Where is this done?  \nThis was done back in `Snapshot::Initialize`:\n```c++\n  const v8::StartupData* blob = isolate-\u003esnapshot_blob();                       \n  Vector\u003cconst byte\u003e startup_data = ExtractStartupData(blob);                   \n  Vector\u003cconst byte\u003e read_only_data = ExtractReadOnlyData(blob);                \n  SnapshotData startup_snapshot_data(MaybeDecompress(startup_data));            \n  SnapshotData read_only_snapshot_data(MaybeDecompress(read_only_data));        \n  StartupDeserializer startup_deserializer(\u0026startup_snapshot_data); \n```\n```console\n(lldb) expr *this\n(v8::internal::SnapshotByteSource) $30 = (data_ = \"`\\x04\", length_ = 125752, position_ = 1)\n```\n\nAll the roots in a heap are declared in src/roots/roots.h. You can access the\nroots using RootsTable via the Isolate using isolate_data-\u003eroots() or by using\nisolate-\u003eroots_table. The roots_ field is an array of Address elements:\n```c++\nclass RootsTable {                                                              \n public:\n  static constexpr size_t kEntriesCount = static_cast\u003csize_t\u003e(RootIndex::kRootListLength);\n  ...\n private:\n  Address roots_[kEntriesCount];                                                \n  static const char* root_names_[kEntriesCount]; \n```\nRootIndex is generated by a macro\n```c++\nenum class RootIndex : uint16_t {\n```\nThe complete enum can be displayed using:\n```console\n$ ./test/roots_test --gtest_filter=RootsTest.list_root_index\n```\n\nLets take a look at an entry:\n```console\n(lldb) p roots_[(uint16_t)RootIndex::kError_string]\n(v8::internal::Address) $1 = 42318447256121\n```\nNow, there are functions in factory which can be used to retrieve these addresses,\nlike factory-\u003eError_string():\n```console\n(lldb) expr *isolate-\u003efactory()-\u003eError_string()\n(v8::internal::String) $9 = {\n  v8::internal::TorqueGeneratedString\u003cv8::internal::String, v8::internal::Name\u003e = {\n    v8::internal::Name = {\n      v8::internal::TorqueGeneratedName\u003cv8::internal::Name, v8::internal::PrimitiveHeapObject\u003e = {\n        v8::internal::PrimitiveHeapObject = {\n          v8::internal::TorqueGeneratedPrimitiveHeapObject\u003cv8::internal::PrimitiveHeapObject, v8::internal::HeapObject\u003e = {\n            v8::internal::HeapObject = {\n              v8::internal::Object = {\n                v8::internal::TaggedImpl\u003cv8::internal::HeapObjectReferenceType::STRONG, unsigned long\u003e = (ptr_ = 42318447256121)\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n(lldb) expr $9.length()\n(int32_t) $10 = 5\n(lldb) expr $9.Print()\n#Error\n```\nThese accessor functions declarations are generated by the\n`ROOT_LIST(ROOT_ACCESSOR))` macros:\n```c++\n#define ROOT_ACCESSOR(Type, name, CamelName) inline Handle\u003cType\u003e name();           \n  ROOT_LIST(ROOT_ACCESSOR)                                                         \n#undef ROOT_ACCESSOR\n```\nAnd the definitions can be found in `src/heap/factory-inl.h` and look like this\nThe implementations then look like this:\n```c++\nString ReadOnlyRoots::Error_string() const { \n  return  String::unchecked_cast(Object(at(RootIndex::kError_string)));\n} \n\nHandle\u003cString\u003e ReadOnlyRoots::Error_string_handle() const {\n  return Handle\u003cString\u003e(\u0026at(RootIndex::kError_string)); \n}\n```\nThe unit test [roots_test](./test/roots_test.cc) shows and example of this.\n\nThis shows the usage of root entries but where are the roots added to this\narray. `roots_` is a member of `IsolateData` in `src/execution/isolate-data.h`:\n```\n  RootsTable roots_;\n```\nWe can inspect the roots_ content by using the interal Isolate:\n```\n(lldb) f\nframe #0: 0x00007ffff6261cdf libv8.so`v8::Isolate::Initialize(isolate=0x00000eb900000000, params=0x00007fffffffd0d0) at api.cc:8269:31\n   8266\tvoid Isolate::Initialize(Isolate* isolate,\n   8267\t                         const v8::Isolate::CreateParams\u0026 params) {\n\n(lldb) expr i_isolate-\u003eisolate_data_.roots_\n(v8::internal::RootsTable) $5 = {\n  roots_ = {\n    [0] = 0\n    [1] = 0\n    [2] = 0\n```\nSo we can see that the roots are intially zero:ed out. And the type of `roots_`\nis an array of `Address`'s.\n```console\n    frame #3: 0x00007ffff6c33d58 libv8.so`v8::internal::Deserializer::VisitRootPointers(this=0x00007fffffffcce0, root=kReadOnlyRootList, description=0x0000000000000000, start=FullObjectSlot @ 0x00007fffffffc530, end=FullObjectSlot @ 0x00007fffffffc528) at deserializer.cc:94:11\n    frame #4: 0x00007ffff6b6212f libv8.so`v8::internal::ReadOnlyRoots::Iterate(this=0x00007fffffffc5c8, visitor=0x00007fffffffcce0) at roots.cc:21:29\n    frame #5: 0x00007ffff6c46fee libv8.so`v8::internal::ReadOnlyDeserializer::DeserializeInto(this=0x00007fffffffcce0, isolate=0x00000f7500000000) at read-only-deserializer.cc:41:18\n    frame #6: 0x00007ffff66af631 libv8.so`v8::internal::ReadOnlyHeap::DeseralizeIntoIsolate(this=0x000000000049afb0, isolate=0x00000f7500000000, des=0x00007fffffffcce0) at read-only-heap.cc:85:23\n    frame #7: 0x00007ffff66af5de libv8.so`v8::internal::ReadOnlyHeap::SetUp(isolate=0x00000f7500000000, des=0x00007fffffffcce0) at read-only-heap.cc:78:53\n```\nThis will land us in `roots.cc` ReadOnlyRoots::Iterate(RootVisitor* visitor):\n```c++\nvoid ReadOnlyRoots::Iterate(RootVisitor* visitor) {                                \n  visitor-\u003eVisitRootPointers(Root::kReadOnlyRootList, nullptr,                     \n                             FullObjectSlot(read_only_roots_),                     \n                             FullObjectSlot(\u0026read_only_roots_[kEntriesCount])); \n  visitor-\u003eSynchronize(VisitorSynchronization::kReadOnlyRootList);                 \n} \n```\nDeserializer::VisitRootPointers calls `Deserializer::ReadData` and the roots_\narray is still zero:ed out when we enter this function.\n\n```c++\nvoid Deserializer::VisitRootPointers(Root root, const char* description,\n                                     FullObjectSlot start, FullObjectSlot end) {\n  ReadData(FullMaybeObjectSlot(start), FullMaybeObjectSlot(end),\n           SnapshotSpace::kNew, kNullAddress);\n```\nNotice that we called VisitRootPointer and pased in `Root:kReadOnlyRootList`, \nnullptr (the description), and start and end addresses as FullObjectSlots. The\nsignature of `VisitRootPointers` looks like this:\n```c++\nvirtual void VisitRootPointers(Root root, const char* description,            \n                                 FullObjectSlot start, FullObjectSlot end)\n```\nIn our case we are using the address of `read_only_roots_` from `src/roots/roots.h`\nand the end is found by using the static member of ReadOnlyRoots::kEntrysCount.\n\nThe switch statement in `ReadData` is generated by macros so lets take a look at\nan expanded snippet to understand what is going on:\n```c++\ntemplate \u003ctypename TSlot\u003e\nbool Deserializer::ReadData(TSlot current, TSlot limit,\n                            SnapshotSpace source_space,\n                            Address current_object_address) {\n  Isolate* const isolate = isolate_;\n  ...\n  while (current \u003c limit) {                                                     \n    byte data = source_.Get();                                                  \n```\nSo current is the start address of the read_only_list and limit the end. `source_`\nis a member of `ReadOnlyDeserializer` and is of type SnapshotByteSource.\n\n`source_` got populated back in Snapshot::Initialize(internal_isolate):\n```\nconst v8::StartupData* blob = isolate-\u003esnapshot_blob();\nVector\u003cconst byte\u003e read_only_data = ExtractReadOnlyData(blob);\nReadOnlyDeserializer read_only_deserializer(\u0026read_only_snapshot_data);\n```\nAnd `ReadOnlyDeserializer` extends `Deserialier` (src/snapshot/deserializer.h)\nwhich has a constructor that sets the source_ member to data-\u003ePayload().\nSo `source_` is will be pointer to an instance of `SnapshotByteSource` which\ncan be found in `src/snapshot-source-sink.h`:\n```c++\nclass SnapshotByteSource final {\n public:\n  SnapshotByteSource(const char* data, int length)\n      : data_(reinterpret_cast\u003cconst byte*\u003e(data)),\n        length_(length),\n        position_(0) {}\n\n  byte Get() {                                                                  \n    return data_[position_++];                                                  \n  }\n  ...\n private:\n  const byte* data_;\n  int length_;\n  int posistion_;\n```\nAlright, so we are calling source_.Get() which we can see returns the current\nentry from the byte array data_ and increment the position. So with that in\nmind lets take closer look at the switch statment:\n```c++\n  while (current \u003c limit) {                                                     \n    byte data = source_.Get();                                                  \n    switch (data) {                                                             \n      case kNewObject + static_cast\u003cint\u003e(SnapshotSpace::kNew):\n        current = ReadDataCase\u003cTSlot, kNewObject, SnapshotSpace::kNew\u003e(isolate, current, current_object_address, data, write_barrier_needed);\n        break;\n      case kNewObject + static_cast\u003cint\u003e(SnapshotSpace::kOld):\n        [[clang::fallthrough]];\n      case kNewObject + static_cast\u003cint\u003e(SnapshotSpace::kCode):\n        [[clang::fallthrough]];\n      case kNewObject + static_cast\u003cint\u003e(SnapshotSpace::kMap):\n        static_assert((static_cast\u003cint\u003e(SnapshotSpace::kMap) \u0026 ~kSpaceMask) == 0, \"(static_cast\u003cint\u003e(SnapshotSpace::kMap) \u0026 ~kSpaceMask) == 0\");\n        [[clang::fallthrough]];\n      ...\n```\nWe can see that switch statement will assign the passed-in `current` with a new\ninstance of `ReadDataCase`.\n```c++\n  current = ReadDataCase\u003cTSlot, kNewObject, SnapshotSpace::kNew\u003e(isolate,\n      current, current_object_address, data, write_barrier_needed);\n```\nNotice that kNewObject is the type of SerializerDeserliazer::Bytecode that is\nto be read (I think), this enum can be found in `src/snapshot/serializer-common.h`.\n`TSlot` I think stands for the \"Type of Slot\", which in our case is a FullMaybyObjectSlot.\n```c++\n  HeapObject heap_object;\n  if (bytecode == kNewObject) {                                                 \n    heap_object = ReadObject(space);   \n```\nReadObject is also in deserializer.cc :\n```c++\nAddress address = allocator()-\u003eAllocate(space, size);\nHeapObject obj = HeapObject::FromAddress(address);\nisolate_-\u003eheap()-\u003eOnAllocationEvent(obj, size);\n\nAlright, lets set a watch point on the roots_ array to see when the first entry\nis populated and try to figure this out that way:\n```console\n(lldb) watch set variable  isolate-\u003eisolate_data_.roots_.roots_[0]\nWatchpoint created: Watchpoint 5: addr = 0xf7500000080 size = 8 state = enabled type = w\n    declare @ '/home/danielbevenius/work/google/v8_src/v8/src/heap/read-only-heap.cc:28'\n    watchpoint spec = 'isolate-\u003eisolate_data_.roots_.roots_[0]'\n    new value: 0\n(lldb) r\n\nWatchpoint 5 hit:\nold value: 0\nnew value: 16995320070433\nProcess 1687448 stopped\n* thread #1, name = 'hello-world', stop reason = watchpoint 5\n    frame #0: 0x00007ffff664e5b1 libv8.so`v8::internal::FullMaybeObjectSlot::store(this=0x00007fffffffc3b0, value=MaybeObject @ 0x00007fffffffc370) const at slots-inl.h:74:1\n   71  \t\n   72  \tvoid FullMaybeObjectSlot::store(MaybeObject value) const {\n   73  \t  *location() = value.ptr();\n-\u003e 74  \t}\n   75 \n```\nWe can verify that location actually contains the address of `roots_[0]`:\n```console\n(lldb) expr -f hex -- this-\u003eptr_\n(v8::internal::Address) $164 = 0x00000f7500000080\n(lldb) expr -f hex -- \u0026this-\u003eisolate_-\u003eisolate_data_.roots_.roots_[0]\n(v8::internal::Address *) $171 = 0x00000f7500000080\n\n(lldb) expr -f hex -- value.ptr()\n(unsigned long) $184 = 0x00000f7508040121\n(lldb) expr -f hex -- isolate_-\u003eisolate_data_.roots_.roots_[0]\n(v8::internal::Address) $183 = 0x00000f7508040121\n```\nThe first entry is free_space_map.\n```console\n(lldb) expr v8::internal::Map::unchecked_cast(v8::internal::Object(value-\u003eptr()))\n(v8::internal::Map) $185 = {\n  v8::internal::HeapObject = {\n    v8::internal::Object = {\n      v8::internal::TaggedImpl\u003cv8::internal::HeapObjectReferenceType::STRONG, unsigned long\u003e = (ptr_ = 16995320070433)\n    }\n  }\n```\nNext, we will go through the while loop again:\n```console\n(lldb) expr -f hex -- isolate_-\u003eisolate_data_.roots_.roots_[1]\n(v8::internal::Address) $191 = 0x0000000000000000\n(lldb) expr -f hex -- \u0026isolate_-\u003eisolate_data_.roots_.roots_[1]\n(v8::internal::Address *) $192 = 0x00000f7500000088\n(lldb) expr -f hex -- location()\n(v8::internal::SlotBase\u003cv8::internal::FullMaybeObjectSlot, unsigned long, 8\u003e::TData *) $194 = 0x00000f7500000088\n```\nNotice that in Deserializer::Write we have:\n```c++\n  dest.store(value);\n  return dest + 1;\n```\nAnd it's current value is:\n```console\n(v8::internal::Address) $197 = 0x00000f7500000088\n```\nWhich is the same address as roots_[1] that we just wrote to.\n\nIf we know the type that an Address points to we can use the Type::cast(Object obj)\nto cast it into a pointer of that type. I think this works will all types.\n```console\n(lldb) expr -A -f hex  -- v8::internal::Oddball::cast(v8::internal::Object(isolate_-\u003eisolate_data_.roots_.roots_[4]))\n(v8::internal::Oddball) $258 = {\n  v8::internal::TorqueGeneratedOddball\u003cv8::internal::Oddball, v8::internal::PrimitiveHeapObject\u003e = {\n    v8::internal::PrimitiveHeapObject = {\n      v8::internal::TorqueGeneratedPrimitiveHeapObject\u003cv8::internal::PrimitiveHeapObject, v8::internal::HeapObject\u003e = {\n        v8::internal::HeapObject = {\n          v8::internal::Object = {\n            v8::internal::TaggedImpl\u003cv8::internal::HeapObjectReferenceType::STRONG, unsigned long\u003e = (ptr_ = 0x00000f750804030d)\n          }\n        }\n      }\n    }\n  }\n}\n```\nYou can also just cast it to an object and try printing it:\n```console\n(lldb) expr -A -f hex  -- v8::internal::Object(isolate_-\u003eisolate_data_.roots_.roots_[4]).Print()\n#undefined\n```\nThis is actually the Oddball UndefinedValue so it makes sense in this case I think.\nWith this value in the roots_ array we can use the function ReadOnlyRoots::undefined_value():\n```console\n(lldb) expr v8::internal::ReadOnlyRoots(\u0026isolate_-\u003eheap_).undefined_value()\n(v8::internal::Oddball) $265 = {\n  v8::internal::TorqueGeneratedOddball\u003cv8::internal::Oddball, v8::internal::PrimitiveHeapObject\u003e = {\n    v8::internal::PrimitiveHeapObject = {\n      v8::internal::TorqueGeneratedPrimitiveHeapObject\u003cv8::internal::PrimitiveHeapObject, v8::internal::HeapObject\u003e = {\n        v8::internal::HeapObject = {\n          v8::internal::Object = {\n            v8::internal::TaggedImpl\u003cv8::internal::HeapObjectReferenceType::STRONG, unsigned long\u003e = (ptr_ = 16995320070925)\n          }\n        }\n      }\n    }\n  }\n}\n```\nSo how are these roots used, take the above `undefined_value` for example?  \nWell most things (perhaps all) that are needed go via the Factory which the\ninternal Isolate is a type of. In factory we can find:\n```c++\nHandle\u003cOddball\u003e Factory::undefined_value() {\n  return Handle\u003cOddball\u003e(\u0026isolate()-\u003eroots_table()[RootIndex::kUndefinedValue]);\n}\n```\nNotice that this is basically what we did in the debugger before but here\nit is wrapped in Handle so that it can be tracked by the GC.\n\nThe unit test [isolate_test](./test/isolate_test.cc) explores the internal \nisolate and has example of usages of the above mentioned methods.\n\nInitwithSnapshot will call Isolate::Init:\n```c++\nbool Isolate::Init(ReadOnlyDeserializer* read_only_deserializer,\n                   StartupDeserializer* startup_deserializer) {\n\n#define ASSIGN_ELEMENT(CamelName, hacker_name)                  \\\n  isolate_addresses_[IsolateAddressId::k##CamelName##Address] = \\\n      reinterpret_cast\u003cAddress\u003e(hacker_name##_address());\n  FOR_EACH_ISOLATE_ADDRESS_NAME(ASSIGN_ELEMENT)\n#undef ASSIGN_ELEMENT\n```\n```c++\n  Address isolate_addresses_[kIsolateAddressCount + 1] = {};\n```\n```console\n(gdb) p isolate_addresses_\n$16 = {0 \u003crepeats 13 times\u003e}\n```\n\nLets take a look at the expanded code in Isolate::Init:\n```console\n$ clang++ -I./out/x64.release/gen -I. -I./include -E src/execution/isolate.cc \u003e output\n```\n```c++\nisolate_addresses_[IsolateAddressId::kHandlerAddress] = reinterpret_cast\u003cAddress\u003e(handler_address());\nisolate_addresses_[IsolateAddressId::kCEntryFPAddress] = reinterpret_cast\u003cAddress\u003e(c_entry_fp_address());\nisolate_addresses_[IsolateAddressId::kCFunctionAddress] = reinterpret_cast\u003cAddress\u003e(c_function_address());\nisolate_addresses_[IsolateAddressId::kContextAddress] = reinterpret_cast\u003cAddress\u003e(context_address());\nisolate_addresses_[IsolateAddressId::kPendingExceptionAddress] = reinterpret_cast\u003cAddress\u003e(pending_exception_address());\nisolate_addresses_[IsolateAddressId::kPendingHandlerContextAddress] = reinterpret_cast\u003cAddress\u003e(pending_handler_context_address());\nisolate_addresses_[IsolateAddressId::kPendingHandlerEntrypointAddress] = reinterpret_cast\u003cAddress\u003e(pending_handler_entrypoint_address());\nisolate_addresses_[IsolateAddressId::kPendingHandlerConstantPoolAddress] = reinterpret_cast\u003cAddress\u003e(pending_handler_constant_pool_address());\nisolate_addresses_[IsolateAddressId::kPendingHandlerFPAddress] = reinterpret_cast\u003cAddress\u003e(pending_handler_fp_address());\nisolate_addresses_[IsolateAddressId::kPendingHandlerSPAddress] = reinterpret_cast\u003cAddress\u003e(pending_handler_sp_address());\nisolate_addresses_[IsolateAddressId::kExternalCaughtExceptionAddress] = reinterpret_cast\u003cAddress\u003e(external_caught_exception_address());\nisolate_addresses_[IsolateAddressId::kJSEntrySPAddress] = reinterpret_cast\u003cAddress\u003e(js_entry_sp_address());\n```\nThen functions, like handler_address() are implemented as:\n```c++ \ninline Address* handler_address() { return \u0026thread_local_top()-\u003ehandler_; }   \n```\n```console\n(gdb) x/x isolate_addresses_[0]\n0x1a3500003240:\t0x00000000\n```\nAt this point in the program we have only set the entries to point contain\nthe addresses specified in ThreadLocalTop, At the time there are initialized\nthe will mostly be initialized to `kNullAddress`:\n```c++\nstatic const Address kNullAddress = 0;\n```\nAnd notice that the functions above return pointers so later these pointers can\nbe updated to point to something. What/when does this happen?  Lets continue and\nfind out...\n\nBack in Isolate::Init we have:\n```c++\n  compilation_cache_ = new CompilationCache(this);\n  descriptor_lookup_cache_ = new DescriptorLookupCache();\n  inner_pointer_to_code_cache_ = new InnerPointerToCodeCache(this);\n  global_handles_ = new GlobalHandles(this);\n  eternal_handles_ = new EternalHandles();\n  bootstrapper_ = new Bootstrapper(this);\n  handle_scope_implementer_ = new HandleScopeImplementer(this);\n  load_stub_cache_ = new StubCache(this);\n  store_stub_cache_ = new StubCache(this);\n  materialized_object_store_ = new MaterializedObjectStore(this);\n  regexp_stack_ = new RegExpStack();\n  regexp_stack_-\u003eisolate_ = this;\n  date_cache_ = new DateCache();\n  heap_profiler_ = new HeapProfiler(heap());\n  interpreter_ = new interpreter::Interpreter(this);\n\n  compiler_dispatcher_ =\n      new CompilerDispatcher(this, V8::GetCurrentPlatform(), FLAG_stack_size);\n\n  // SetUp the object heap.\n  DCHECK(!heap_.HasBeenSetUp());\n  heap_.SetUp();\n\n  ...\n  InitializeThreadLocal();\n```\nLets take a look at `InitializeThreadLocal`\n\n```c++\nvoid Isolate::InitializeThreadLocal() {\n  thread_local_top()-\u003eInitialize(this);\n  clear_pending_exception();\n  clear_pending_message();\n  clear_scheduled_exception();\n}\n```\n```c++\nvoid Isolate::clear_pending_exception() {\n  DCHECK(!thread_local_top()-\u003epending_exception_.IsException(this));\n  thread_local_top()-\u003epending_exception_ = ReadOnlyRoots(this).the_hole_value();\n}\n```\nReadOnlyRoots \n```c++\n#define ROOT_ACCESSOR(Type, name, CamelName) \\\n  V8_INLINE class Type name() const;         \\\n  V8_INLINE Handle\u003cType\u003e name##_handle() const;\n\n  READ_ONLY_ROOT_LIST(ROOT_ACCESSOR)\n#undef ROOT_ACCESSOR\n```\nThis will expand to a number of function declarations that looks like this:\n```console\n$ clang++ -I./out/x64.release/gen -I. -I./include -E src/roots/roots.h \u003e output\n```\n```c++\ninline __attribute__((always_inline)) class Map free_space_map() const;\ninline __attribute__((always_inline)) Handle\u003cMap\u003e free_space_map_handle() const;\n```\nThe Map class is what all HeapObject use to describe their structure. Notice\nthat there is also a Handle\u003cMap\u003e declared.\nThese are generated by a macro in roots-inl.h:\n```c++\nMap ReadOnlyRoots::free_space_map() const { \n  ((void) 0);\n  return Map::unchecked_cast(Object(at(RootIndex::kFreeSpaceMap)));\n} \n\nHandle\u003cMap\u003e ReadOnlyRoots::free_space_map_handle() const {\n  ((void) 0);\n  return Handle\u003cMap\u003e(\u0026at(RootIndex::kFreeSpaceMap));\n}\n```\nNotice that this is using the RootIndex enum that was mentioned earlier:\n```c++\n  return Map::unchecked_cast(Object(at(RootIndex::kFreeSpaceMap)));\n```\nIn object/map.h there is the following line:\n```c++\n  DECL_CAST(Map)\n```\nWhich can be found in objects/object-macros.h:\n```c++\n#define DECL_CAST(Type)                                 \\\n  V8_INLINE static Type cast(Object object);            \\\n  V8_INLINE static Type unchecked_cast(Object object) { \\\n    return bit_cast\u003cType\u003e(object);                      \\\n  }\n```\nThis will expand to something like\n```c++\n  static Map cast(Object object);\n  static Map unchecked_cast(Object object) {\n    return bit_cast\u003cMap\u003e(object);\n  }\n```\nAnd the `Object` part is the Object contructor that takes an Address: \n```c++\n  explicit constexpr Object(Address ptr) : TaggedImpl(ptr) {}\n```\nThat leaves the at function which is a private function in ReadOnlyRoots:\n```c++\n  V8_INLINE Address\u0026 at(RootIndex root_index) const;\n```\n\nSo we are now back in Isolate::Init after the call to InitializeThreadLocal we\nhave:\n```c++\nsetup_delegate_-\u003eSetupBuiltins(this);\n```\n\nIn the following line in api.cc, where does `i::OBJECT_TEMPLATE_INFO_TYPE` come from:\n```c++\n  i::Handle\u003ci::Struct\u003e struct_obj = isolate-\u003efactory()-\u003eNewStruct(\n      i::OBJECT_TEMPLATE_INFO_TYPE, i::AllocationType::kOld);\n```\n\n### InstanceType\nThe enum `InstanceType` is defined in `src/objects/instance-type.h`:\n```c++\n#include \"torque-generated/instance-types-tq.h\" \n\nenum InstanceType : uint16_t {\n  ...   \n#define MAKE_TORQUE_INSTANCE_TYPE(TYPE, value) TYPE = value,                    \n  TORQUE_ASSIGNED_INSTANCE_TYPES(MAKE_TORQUE_INSTANCE_TYPE)                     \n#undef MAKE_TORQUE_INSTANCE_TYPE \n  ...\n};\n```\nAnd in `gen/torque-generated/instance-types-tq.h` we can find:\n```c++\n#define TORQUE_ASSIGNED_INSTANCE_TYPES(V) \\                                     \n  ...\n  V(OBJECT_TEMPLATE_INFO_TYPE, 79) \\                                      \n  ...\n```\nThere is list in `src/objects/objects-definitions.h`:\n```c++\n#define STRUCT_LIST_GENERATOR_BASE(V, _)                                      \\\n  ...\n  V(_, OBJECT_TEMPLATE_INFO_TYPE, ObjectTemplateInfo, object_template_info)   \\\n  ...\n```\n```c++\ntemplate \u003ctypename Impl\u003e\nHandle\u003cStruct\u003e FactoryBase\u003cImpl\u003e::NewStruct(InstanceType type,\n                                            AllocationType allocation) {\n  Map map = Map::GetInstanceTypeMap(read_only_roots(), type);\n```\nIf we look in `Map::GetInstanceTypeMap` in map.cc we find:\n```c++\n  Map map;\n  switch (type) {\n#define MAKE_CASE(TYPE, Name, name) \\\n  case TYPE:                        \\\n    map = roots.name##_map();       \\\n    break;\n    STRUCT_LIST(MAKE_CASE)\n#undef MAKE_CASE\n```\nNow, we know that our type is:\n```console\n(gdb) p type\n$1 = v8::internal::OBJECT_TEMPLATE_INFO_TYPE\n```\n```c++\n    map = roots.object_template_info_map();       \\\n```\nAnd we can inspect the output of the preprocessor of roots.cc and find:\n```c++\nMap ReadOnlyRoots::object_template_info_map() const { \n  ((void) 0);\n  return Map::unchecked_cast(Object(at(RootIndex::kObjectTemplateInfoMap)));\n}\n```\nAnd this is something we have seen before. \n\nOne things I ran into was wanting to print the InstanceType using the overloaded\n\u003c\u003c operator which is defined for the InstanceType in objects.cc.\n```c++\nstd::ostream\u0026 operator\u003c\u003c(std::ostream\u0026 os, InstanceType instance_type) {\n  switch (instance_type) {\n#define WRITE_TYPE(TYPE) \\\n  case TYPE:             \\\n    return os \u003c\u003c #TYPE;\n    INSTANCE_TYPE_LIST(WRITE_TYPE)\n#undef WRITE_TYPE\n  }\n  UNREACHABLE();\n}\n```\nThe code I'm using is the followig:\n```c++\n  i::InstanceType type = map.instance_type();\n  std::cout \u003c\u003c \"object_template_info_map type: \" \u003c\u003c type \u003c\u003c '\\n';\n```\nThis will cause the `UNREACHABLE()` function to be called and a Fatal error\nthrown. But note that the following line works:\n```c++\n  std::cout \u003c\u003c \"object_template_info_map type: \" \u003c\u003c v8::internal::OBJECT_TEMPLATE_INFO_TYPE \u003c\u003c '\\n';\n```\nAnd prints\n```console\nobject_template_info_map type: OBJECT_TEMPLATE_INFO_TYPE\n```\nIn the switch/case block above the case for this value is:\n```c++\n  case OBJECT_TEMPLATE_INFO_TYPE:\n    return os \u003c\u003c \"OBJECT_TEMPLATE_INFO_TYPE\"\n```\nWhen map.instance_type() is called, it returns a value of `1023` but the value\nof OBJECT_TEMPLATE_INFO_TYPE is:\n```c++\nOBJECT_TEMPLATE_INFO_TYPE = 79\n```\nAnd we can confirm this using:\n```console\n  std::cout \u003c\u003c \"object_template_info_map type: \" \u003c\u003c static_cast\u003cuint16_t\u003e(v8::internal::OBJECT_TEMPLATE_INFO_TYPE) \u003c\u003c '\\n';\n```\nWhich will print:\n```console\nobject_template_info_map type: 79\n```\n\n### IsolateData\n\n### Context creation\nWhen we create a new context using:\n```c++\n  Local\u003cObjectTemplate\u003e global = ObjectTemplate::New(isolate_);\n  Local\u003cContext\u003e context = Context::New(isolate_, nullptr, global);\n```\nThe Context class in `include/v8.h` declares New as follows:\n```c++\nstatic Local\u003cContext\u003e New(Isolate* isolate,\n    ExtensionConfiguration* extensions = nullptr,\n    MaybeLocal\u003cObjectTemplate\u003e global_template = MaybeLocal\u003cObjectTemplate\u003e(),\n    MaybeLocal\u003cValue\u003e global_object = MaybeLocal\u003cValue\u003e(),\n    DeserializeInternalFieldsCallback internal_fields_deserializer = DeserializeInternalFieldsCallback(),\n    MicrotaskQueue* microtask_queue = nullptr);\n```\n\nWhen a step into Context::New(isolate_, nullptr, global) this will first break\nin the constructor of DeserializeInternalFieldsCallback in v8.h which has default\nvalues for the callback function and data_args (both are nullptr). After that\ngdb will break in MaybeLocal\u003cValue\u003e and setting val_ to nullptr. Next it will\nbreak in Local::operator* for the value of `global` which is then passed to the\nMaybeLocal\u003cv8::ObjectTemplate\u003e constructor. After those break points the break\npoint will be in api.cc and v8::Context::New. New will call NewContext in api.cc.\n\nThere will be some checks and logging/tracing and then a call to CreateEnvironment:\n```c++\ni::Handle\u003ci::Context\u003e env = CreateEnvironment\u003ci::Context\u003e(                         \n    isolate,\n    extensions,\n    global_template, \n    global_object,                           \n    context_snapshot_index, \n    embedder_fields_deserializer, \n    microtask_queue); \n```\nThe first line in CreateEnironment is:\n```c++\nENTER_V8_FOR_NEW_CONTEXT(isolate);\n```\nWhich is a macro defined in api.cc\n```c++\ni::VMState\u003cv8::OTHER\u003e __state__((isolate)); \\                                 \ni::DisallowExceptions __no_exceptions__((isolate)) \n```\nSo the first break point we break on will be the execution/vm-state-inl.h and\nVMState's constructor:\n```c++\ntemplate \u003cStateTag Tag\u003e                                                         \nVMState\u003cTag\u003e::VMState(Isolate* isolate)                                         \n    : isolate_(isolate), previous_tag_(isolate-\u003ecurrent_vm_state()) {           \n  isolate_-\u003eset_current_vm_state(Tag);                                          \n} \n```\nIn gdb you'll see this:\n```console\n(gdb) s\nv8::internal::VMState\u003c(v8::StateTag)5\u003e::VMState (isolate=0x372500000000, this=\u003csynthetic pointer\u003e) at ../../src/api/api.cc:6005\n6005\t      context_snapshot_index, embedder_fields_deserializer, microtask_queue);\n(gdb) s\nv8::internal::Isolate::current_vm_state (this=0x372500000000) at ../../src/execution/isolate.h:1072\n1072\t  THREAD_LOCAL_TOP_ACCESSOR(StateTag, current_vm_state)\n```\nNotice that VMState's constructor sets its `previous_tag_` to isolate-\u003ecurrent_vm_state()\nwhich is generated by the macro THREAD_LOCAL_TOP_ACCESSOR.\nThe next break point will be:\n```console\n#0  v8::internal::PerIsolateAssertScopeDebugOnly\u003c(v8::internal::PerIsolateAssertType)5, false\u003e::PerIsolateAssertScopeDebugOnly (\n    isolate=0x372500000000, this=0x7ffc7b51b500) at ../../src/common/assert-scope.h:107\n107\t  explicit PerIsolateAssertScopeDebugOnly(Isolate* isolate)\n```\nWe can find that `DisallowExceptions` is defined in src/common/assert-scope.h as:\n```c++\nusing DisallowExceptions =                                                      \n    PerIsolateAssertScopeDebugOnly\u003cNO_EXCEPTION_ASSERT, false\u003e;\n```\nAfter all that we can start to look at the code in CreateEnvironment.\n\n```c++\n    // Create the environment.                                                       \n    InvokeBootstrapper\u003cObjectType\u003e invoke;                                           \n    result = invoke.Invoke(isolate, maybe_proxy, proxy_template, extensions,    \n                           context_snapshot_index, embedder_fields_deserializer,\n                           microtask_queue);  \n\n\ntemplate \u003ctypename ObjectType\u003e                                                  \nstruct InvokeBootstrapper;                                                        \n                                                                                     \ntemplate \u003c\u003e                                                                     \nstruct InvokeBootstrapper\u003ci::Context\u003e {                                         \n  i::Handle\u003ci::Context\u003e Invoke(                                                 \n      i::Isolate* isolate, i::MaybeHandle\u003ci::JSGlobalProxy\u003e maybe_global_proxy, \n      v8::Local\u003cv8::ObjectTemplate\u003e global_proxy_template,                      \n      v8::ExtensionConfiguration* extensions, size_t context_snapshot_index,    \n      v8::DeserializeInternalFieldsCallback embedder_fields_deserializer,       \n      v8::MicrotaskQueue* microtask_queue) {                                         \n    return isolate-\u003ebootstrapper()-\u003eCreateEnvironment(                               \n        maybe_global_proxy, global_proxy_template, extensions,                       \n        context_snapshot_index, embedder_fields_deserializer, microtask_queue); \n  }                                                                                  \n};\n```\nBootstrapper can be found in `src/init/bootstrapper.cc`:\n```console\nHandleScope scope(isolate_);                                                      \nHandle\u003cContext\u003e env;                                                              \n  {                                                                                 \n    Genesis genesis(isolate_, maybe_global_proxy, global_proxy_template,            \n                    context_snapshot_index, embedder_fields_deserializer,           \n                    microtask_queue);                                               \n    env = genesis.result();                                                         \n    if (env.is_null() || !InstallExtensions(env, extensions)) {                     \n      return Handle\u003cContext\u003e();                                                     \n    }                                                                               \n  }                 \n```\nNotice that the break point will be in the HandleScope constructor. Then a \nnew instance of Genesis is created which performs some actions in its constructor.\n```c++\nglobal_proxy = isolate-\u003efactory()-\u003eNewUninitializedJSGlobalProxy(instance_size);\n```\n\nThis will land in factory.cc:\n```c++\nHandle\u003cMap\u003e map = NewMap(JS_GLOBAL_PROXY_TYPE, size);\n```\n`size` will be 16 in this case. `NewMap` is declared in factory.h which has\ndefault values for its parameters:\n```c++\n  Handle\u003cMap\u003e NewMap(InstanceType type, int instance_size,                      \n                     ElementsKind elements_kind = TERMINAL_FAST_ELEMENTS_KIND,  \n                     int inobject_properties = 0);\n```\n\nIn Factory::InitializeMap we have the following check:\n```c++\nDCHECK_EQ(map.GetInObjectProperties(), inobject_properties);\n```\nRemember that I called `Context::New` with the following arguments:\n```c++\n  Local\u003cObjectTemplate\u003e global = ObjectTemplate::New(isolate_);\n  Local\u003cContext\u003e context = Context::New(isolate_, nullptr, global);\n```\n\n\n### VMState\n\n\n\n### TaggedImpl\nHas a single private member which is declared as:\n```c++\nStorageType ptr_;\n```\nAn instance can be created using:\n```c++\n  i::TaggedImpl\u003ci::HeapObjectReferenceType::STRONG, i::Address\u003e  tagged{};\n```\nStorage type can also be `Tagged_t` which is defined in globals.h:\n```c++\n using Tagged_t = uint32_t;\n```\nIt looks like it can be a different value when using pointer compression.\n\n### Object (internal)\nThis class extends TaggedImpl:\n```c++\nclass Object : public TaggedImpl\u003cHeapObjectReferenceType::STRONG, Address\u003e {       \n```\nAn Object can be created using the default constructor, or by passing in an \nAddress which will delegate to TaggedImpl constructors. Object itself does\nnot have any members (apart from ptr_ which is inherited from TaggedImpl that is). \nSo if we create an Object on the stack this is like a pointer/reference to\nan object: \n```\n+------+\n|Object|\n|------|\n|ptr_  |----\u003e\n+------+\n```\nNow, `ptr_` is a TaggedImpl so it would be a Smi in which case it would just\ncontains the value directly, for example a small integer:\n```\n+------+\n|Object|\n|------|\n|  18  |\n+------+\n```\n\n### Handle\nA Handle is similar to a Object and ObjectSlot in that it also contains\nan Address member (called location_ and declared in HandleBase), but with the\ndifference is that Handles can be relocated by the garbage collector.\n\n### HeapObject\n\n\n### NewContext\nWhen we create a new context using:\n```c++\nconst v8::Local\u003cv8::ObjectTemplate\u003e obt = v8::Local\u003cv8::ObjectTemplate\u003e();\nv8::Handle\u003cv8::Context\u003e context = v8::Context::New(isolate_, nullptr, obt);\n```\nThe above is using the static function New declared in `include/v8.h`\n```c++\nstatic Local\u003cContext\u003e New(                                                    \n    Isolate* isolate,\n    ExtensionConfiguration* extensions = nullptr,           \n    MaybeLocal\u003cObjectTemplate\u003e global_template = MaybeLocal\u003cObjectTemplate\u003e(),\n    MaybeLocal\u003cValue\u003e global_object = MaybeLocal\u003cValue\u003e(),                    \n    DeserializeInternalFieldsCallback internal_fields_deserializer = DeserializeInternalFieldsCallback(),                                  \n    MicrotaskQueue* microtask_queue = nullptr);\n```\nThe implementation for this function can be found in `src/api/api.cc`\nHow does a Local become a MaybeLocal in this above case?  \nThis is because MaybeLocal has a constructor that takes a `Local\u003cS\u003e` and this will\nbe casted into the `val_` member of the MaybeLocal instance.\n\n\n### Genesis\nTODO\n\n\n### What is the difference between a Local and a Handle?\n\nCurrently, the torque generator will generate Print functions that look like\nthe following:\n```c++\ntemplate \u003c\u003e                                                                     \nvoid TorqueGeneratedEnumCache\u003cEnumCache, Struct\u003e::EnumCachePrint(std::ostream\u0026 os) {\n  this-\u003ePrintHeader(os, \"TorqueGeneratedEnumCache\");\n  os \u003c\u003c \"\\n - keys: \" \u003c\u003c Brief(this-\u003ekeys());\n  os \u003c\u003c \"\\n - indices: \" \u003c\u003c Brief(this-\u003eindices());\n  os \u003c\u003c \"\\n\";\n}\n```\nNotice the last line where the newline character is printed as a string. This\nwould just be a char instead `'\\n'`.\n\nThere are a number of things that need to happen only once upon startup for\neach process. These things are placed in `V8::InitializeOncePerProcessImpl` which\ncan be found in `src/init/v8.cc`. This is called by v8::V8::Initialize().\n```c++\n  CpuFeatures::Probe(false);                                                    \n  ElementsAccessor::InitializeOncePerProcess();                                 \n  Bootstrapper::InitializeOncePerProcess();                                     \n  CallDescriptors::InitializeOncePerProcess();                                  \n  wasm::WasmEngine::InitializeOncePerProcess();\n```\nElementsAccessor populates the accessor_array with Elements listed in \n`ELEMENTS_LIST`. TODO: take a closer look at Elements. \n\nv8::Isolate::Initialize will set up the heap.\n```c++\ni_isolate-\u003eheap()-\u003eConfigureHeap(params.constraints);\n```\n\nIt is when we create an new Context that Genesis is created. This will call\nSnapshot::NewContextFromSnapshot.\nSo the context is read from the StartupData* blob with ExtractContextData(blob).\n\nWhat is the global proxy?\n\n### Builtins runtime error\nBuiltins is a member of Isolate and an instance is created by the Isolate constructor.\nWe can inspect the value of `initialized_` and that it is false:\n```console\n(gdb) p *this-\u003ebuiltins()\n$3 = {static kNoBuiltinId = -1, static kFirstWideBytecodeHandler = 1248, static kFirstExtraWideBytecodeHandler = 1398, \n  static kLastBytecodeHandlerPlusOne = 1548, static kAllBuiltinsAreIsolateIndependent = true, isolate_ = 0x0, initialized_ = false, \n  js_entry_handler_offset_ = 0}\n```\nThe above is printed form Isolate's constructor and it is not changes in the\ncontructor.\n\nThis is very strange, while I though that the `initialized_` was being updated\nit now looks like there might be two instances, one with has this value as false\nand the other as true. And also one has a nullptr as the isolate and the other\nas an actual value.\nFor example, when I run the hello-world example:\n```console\n$4 = (v8::internal::Builtins *) 0x33b20000a248\n(gdb) p \u0026builtins_\n$5 = (v8::internal::Builtins *) 0x33b20000a248\n```\nNotice that these are poiting to the same location in memory.\n```console\n(gdb) p \u0026builtins_\n$1 = (v8::internal::Builtins *) 0x25210000a248\n(gdb) p builtins()\n$2 = (v8::internal::Builtins *) 0x25210000a228\n```\nAlright, so after looking into this closer I noticed that I was including\ninternal headers in the test itself.\nWhen I include `src/builtins/builtins.h` I will get an implementation of\nisolate-\u003ebuiltins() in the object file which is in the shared library libv8.so,\nbut the field is part of object file that is part of the cctest. This will be a\ndifferent method and not the method that is in libv8_v8.so shared library.\n\nAs I'm only interested in exploring v8 internals and my goal is only for each\nunit test to verify my understanding I've statically linked those object files\nneeded, like builtins.o and code.o to the test.\n\n```console\n Fatal error in ../../src/snapshot/read-only-deserializer.cc, line 35\n# Debug check failed: !isolate-\u003ebuiltins()-\u003eis_initialized().\n#\n#\n#\n#FailureMessage Object: 0x7ffed92ceb20\n==== C stack trace ===============================\n\n    /home/danielbevenius/work/google/v8_src/v8/out/x64.release_gcc/libv8_libbase.so(v8::base::debug::StackTrace::StackTrace()+0x1d) [0x7fabe6c348c1]\n    /home/danielbevenius/work/google/v8_src/v8/out/x64.release_gcc/libv8_libplatform.so(+0x652d9) [0x7fabe6cac2d9]\n    /home/danielbevenius/work/google/v8_src/v8/out/x64.release_gcc/libv8_libbase.so(V8_Fatal(char const*, int, char const*, ...)+0x172) [0x7fabe6c2416d]\n    /home/danielbevenius/work/google/v8_src/v8/out/x64.release_gcc/libv8_libbase.so(v8::base::SetPrintStackTrace(void (*)())+0) [0x7fabe6c23de0]\n    /home/danielbevenius/work/google/v8_src/v8/out/x64.release_gcc/libv8_libbase.so(V8_Dcheck(char const*, int, char const*)+0x2d) [0x7fabe6c241b1]\n    /home/danielbevenius/work/google/v8_src/v8/out/x64.release_gcc/libv8.so(v8::internal::ReadOnlyDeserializer::DeserializeInto(v8::internal::Isolate*)+0x192) [0x7fabe977c468]\n    /home/danielbevenius/work/google/v8_src/v8/out/x64.release_gcc/libv8.so(v8::internal::ReadOnlyHeap::DeseralizeIntoIsolate(v8::internal::Isolate*, v8::internal::ReadOnlyDeserializer*)+0x4f) [0x7fabe91e5a7d]\n    /home/danielbevenius/work/google/v8_src/v8/out/x64.release_gcc/libv8.so(v8::internal::ReadOnlyHeap::SetUp(v8::internal::Isolate*, v8::internal::ReadOnlyDeserializer*)+0x66) [0x7fabe91e5a2a]\n    /home/danielbevenius/work/google/v8_src/v8/out/x64.release_gcc/libv8.so(v8::internal::Isolate::Init(v8::internal::ReadOnlyDeserializer*, v8::internal::StartupDeserializer*)+0x70b) [0x7fabe90633bb]\n    /home/danielbevenius/work/google/v8_src/v8/out/x64.release_gcc/libv8.so(v8::internal::Isolate::InitWithSnapshot(v8::internal::ReadOnlyDeserializer*, v8::internal::StartupDeserializer*)+0x7b) [0x7fabe906299f]\n    /home/danielbevenius/work/google/v8_src/v8/out/x64.release_gcc/libv8.so(v8::internal::Snapshot::Initialize(v8::internal::Isolate*)+0x1e9) [0x7fabe978d941]\n    /home/danielbevenius/work/google/v8_src/v8/out/x64.release_gcc/libv8.so(v8::Isolate::Initialize(v8::Isolate*, v8::Isolate::CreateParams const\u0026)+0x33d) [0x7fabe8d999e3]\n    /home/danielbevenius/work/google/v8_src/v8/out/x64.release_gcc/libv8.so(v8::Isolate::New(v8::Isolate::CreateParams const\u0026)+0x28) [0x7fabe8d99b66]\n    ./test/builtins_test() [0x4135a2]\n    ./test/builtins_test() [0x43a1b7]\n    ./test/builtins_test() [0x434c99]\n    ./test/builtins_test() [0x41a3a7]\n    ./test/builtins_test() [0x41aafb]\n    ./test/builtins_test() [0x41b085]\n    ./test/builtins_test() [0x4238e0]\n    ./test/builtins_test() [0x43b1aa]\n    ./test/builtins_test() [0x435773]\n    ./test/builtins_test() [0x422836]\n    ./test/builtins_test() [0x412ea4]\n    ./test/builtins_test() [0x412e3d]\n    /lib64/libc.so.6(__libc_start_main+0xf3) [0x7fabe66b31a3]\n    ./test/builtins_test() [0x412d5e]\nIllegal instruction (core dumped)\n```\nThe issue here is that I'm including the header in the test, which means that\ncode will be in the object code of the test, while the implementation part will\nbe in the linked dynamic library which is why these are pointing to different\nareas in memory. The one retreived by the function call will use the\n\n### Goma\nI've goma referenced in a number of places so just makeing a note of what it is\nhere: Goma is googles internal distributed compile service.\n\n### WebAssembly\nThis section is going to take a closer look at how wasm works in V8.\n\nWe can use a wasm module like this:\n```js\n  const buffer = fixtures.readSync('add.wasm'); \n  const module = new WebAssembly.Module(buffer);                             \n  const instance = new WebAssembly.Instance(module);                        \n  instance.exports.add(3, 4);\n```\nWhere is the WebAssembly object setup?  We have sen previously that objects and\nfunction are added in `src/init/bootstrapper.cc` and for Wasm there is a function\nnamed Genisis::InstallSpecialObjects which calls:\n```c++\n  WasmJs::Install(isolate, true);\n```\nThis call will land in `src/wasm/wasm-js.cc` where we can find:\n```c++\nvoid WasmJs::Install(Isolate* isolate, bool exposed_on_global_object) {\n  ...\n  Handle\u003cString\u003e name = v8_str(isolate, \"WebAssembly\")\n  ...\n  NewFunctionArgs args = NewFunctionArgs::ForFunctionWithoutCode(               \n      name, isolate-\u003estrict_function_map(), LanguageMode::kStrict);             \n  Handle\u003cJSFunction\u003e cons = factory-\u003eNewFunction(args);                         \n  JSFunction::SetPrototype(cons, isolate-\u003einitial_object_prototype());          \n  Handle\u003cJSObject\u003e webassembly =                                                \n      factory-\u003eNewJSObject(cons, AllocationType::kOld); \n  JSObject::AddProperty(isolate, webassembly, factory-\u003eto_string_tag_symbol(),  \n                        name, ro_attributes);                                   \n\n  InstallFunc(isolate, webassembly, \"compile\", WebAssemblyCompile, 1);          \n  InstallFunc(isolate, webassembly, \"validate\", WebAssemblyValidate, 1);            \n  InstallFunc(isolate, webassembly, \"instantiate\", WebAssemblyInstantiate, 1);\n  ...\n  Handle\u003cJSFunction\u003e module_constructor =                                       \n      InstallConstructorFunc(isolate, webassembly, \"Module\", WebAssemblyModule);\n  ...\n}\n```\nAnd all the rest of the functions that are available on the `WebAssembly` object\nare setup in the same function.\n```console\n(lldb) br s -name Genesis::InstallSpecialObjects\n```\nNow, lets also set a break point in WebAssemblyModule:\n```console\n(lldb) br s -n WebAssemblyModule\n(lldb) r\n```\n```c++\n  v8::Isolate* isolate = args.GetIsolate();                                         \n  i::Isolate* i_isolate = reinterpret_cast\u003ci::Isolate*\u003e(isolate);                   \n  if (i_isolate-\u003ewasm_module_callback()(args)) return;                              \n```\nNotice the `wasm_module_callback()` function which is a function that is setup\non the internal Isolate in `src/execution/isolate.h`:\n```c++\n#define ISOLATE_INIT_LIST(V)                                                   \\\n  ...\n  V(ExtensionCallback, wasm_module_callback, \u0026NoExtension)                     \\\n  V(ExtensionCallback, wasm_instance_callback, \u0026NoExtension)                   \\\n  V(WasmStreamingCallback, wasm_streaming_callback, nullptr)                   \\\n  V(WasmThreadsEnabledCallback, wasm_threads_enabled_callback, nullptr)        \\\n  V(WasmLoadSourceMapCallback, wasm_load_source_map_callback, nullptr) \n\n#define GLOBAL_ACCESSOR(type, name, initialvalue)                \\              \n  inline type name() const {                                     \\              \n    DCHECK(OFFSET_OF(Isolate, name##_) == name##_debug_offset_); \\              \n    return name##_;                                              \\              \n  }                                                              \\              \n  inline void set_##name(type value) {                           \\              \n    DCHECK(OFFSET_OF(Isolate, name##_) == name##_debug_offset_); \\              \n    name##_ = value;                                             \\              \n  }                                                                             \n  ISOLATE_INIT_LIST(GLOBAL_ACCESSOR)                                            \n#undef GLOBAL_ACCESSOR\n```\nSo this would be expanded by the preprocessor into:\n```c++\ninline ExtensionCallback wasm_module_callback() const {\n  ((void) 0);\n  return wasm_module_callback_;\n}\ninline void set_wasm_module_callback(ExtensionCallback value) {\n  ((void) 0);\n  wasm_module_callback_ = value;\n}\n```\nAlso notice that if `wasm_module_callback()` return true the `WebAssemblyModule`\nfuction will return and no further processing of the instructions in that function\nwill be done. `NoExtension` is a function that looks like this:\n```c++\nbool NoExtension(const v8::FunctionCallbackInfo\u003cv8::Value\u003e\u0026) { return false; }\n```\nAnd is set as the default function for module/instance callbacks.\n\nLooking a little further we can see checks for WASM Threads support (TODO: take\na look at this).\nAnd then we have:\n```c++\n  module_obj = i_isolate-\u003ewasm_engine()-\u003eSyncCompile(                             \n        i_isolate, enabled_features, \u0026thrower, bytes);\n```\n`SyncCompile` can be found in `src/wasm/wasm-engine.cc` and will call\n`DecodeWasmModule` which can be found in `src/wasm/module-decoder.cc`.\n```c++\nModuleResult result = DecodeWasmModule(enabled, bytes.start(), bytes.end(),\n                                       false, kWasmOrigin, \n                                       isolate-\u003ecounters(), allocator()); \n```\n```c++\nModuleResult DecodeWasmModule(const WasmFeatures\u0026 enabled,                      \n                              const byte* module_start, const byte* module_end, \n                              bool verify_functions, ModuleOrigin origin,       \n                              Counters* counters,                               \n                              AccountingAllocator* allocator) {\n  ...\n  ModuleDecoderImpl decoder(enabled, module_start, module_end, origin);\n  return decoder.DecodeModule(counters, allocator, verify_functions);\n```\nDecodeModuleHeader:\n```c++\n  uint32_t magic_word = consume_u32(\"wasm magic\");\n```\nThis will land in `src/wasm/decoder.h` consume_little_endian(name):\n```c++\n\n```\nA wasm module has the following preamble:\n```\nmagic nr: 0x6d736100 \nversion: 0x1\n```\nThese can be found as a constant in `src/wasm/wasm-constants.h`:\n```c++\nconstexpr uint32_t kWasmMagic = 0x6d736100; \nconstexpr uint32_t kWasmVersion = 0x01;\n```\nAfter the DecodeModuleHeader the code will iterate of the sections (type,\nimport, function, table, memory, global, export, start, element, code, data,\ncustom).\nFor each section `DecodeSection` will be called:\n```c++\nDecodeSection(section_iter.section_code(), section_iter.payload(),\n              offset, verify_functions);\n```\nThere is an enum named `SectionCode` in `src/wasm/wasm-constants.h` which\ncontains the various sections which is used in switch statement in DecodeSection\n. Depending on the `section_code` there are Decode\u003cType\u003eSection methods that\nwill be called. In our case section_code is:\n```console\n(lldb) expr section_code\n(v8::internal::wasm::SectionCode) $5 = kTypeSectionCode\n```\nAnd this will match the `kTypeSectionCode` and `DecodeTypeSection` will be\ncalled.\n\nValueType can be found in `src/wasm/value-type.h` and there are types for\neach of the currently supported types:\n```c++\nconstexpr ValueType kWasmI32 = ValueType(ValueType::kI32);                      \nconstexpr ValueType kWasmI64 = ValueType(ValueType::kI64);                      \nconstexpr ValueType kWasmF32 = ValueType(ValueType::kF32);                      \nconstexpr ValueType kWasmF64 = ValueType(ValueType::kF64);                      \nconstexpr ValueType kWasmAnyRef = ValueType(ValueType::kAnyRef);                \nconstexpr ValueType kWasmExnRef = ValueType(ValueType::kExnRef);                \nconstexpr ValueType kWasmFuncRef = ValueType(ValueType::kFuncRef);              \nconstexpr ValueType kWasmNullRef = ValueType(ValueType::kNullRef);              \nconstexpr ValueType kWasmS128 = ValueType(ValueType::kS128);                    \nconstexpr ValueType kWasmStmt = ValueType(ValueType::kStmt);                    \nconstexpr ValueType kWasmBottom = ValueType(ValueType::kBottom);\n```\n\n`FunctionSig` is declared with a `using` statement in value-type.h:\n```c++\nusing FunctionSig = Signature\u003cValueType\u003e;\n```\nWe can find `Signature` in src/codegen/signature.h:\n```c++\ntemplate \u003ctypename T\u003e\nclass Signature : public ZoneObject {\n public:\n  constexpr Signature(size_t return_count, size_t parameter_count,\n                      const T* reps)\n      : return_count_(return_count),\n        parameter_count_(parameter_count),\n        reps_(reps) {}\n```\nThe return count can be zero, one (or greater if multi-value return types are\nenabled). The parameter count also makes sense, but reps is not clear to me what\nthat represents.\n```console\n(lldb) fr v\n(v8::internal::Signature\u003cv8::internal::wasm::ValueType\u003e *) this = 0x0000555555583950\n(size_t) return_count = 1\n(size_t) parameter_count = 2\n(const v8::internal::wasm::ValueType *) reps = 0x0000555555583948\n```\nBefore the call to `Signature`s construtor we have:\n```c++\n    // FunctionSig stores the return types first.                               \n    ValueType* buffer = zone-\u003eNewArray\u003cValueType\u003e(param_count + return_count);  \n    uint32_t b = 0;                                                             \n    for (uint32_t i = 0; i \u003c return_count; ++i) buffer[b++] = returns[i];           \n    for (uint32_t i = 0; i \u003c param_count; ++i) buffer[b++] = params[i];         \n                                                                                \n    return new (zone) FunctionSig(return_count, param_count, buffer);\n```\nSo `reps_` contains the return (re?) and the params (ps?).\n\n\nAfter the DecodeWasmModule has returned in SyncCompile we will have a\nModuleResult. This will be compiled to NativeModule:\n```c++\nModuleResult result =                                                         \n      DecodeWasmModule(enabled, bytes.start(), bytes.end(), false, kWasmOrigin, \n                       isolate-\u003ecounters(), allocator());\nHandle\u003cFixedArray\u003e export_wrappers;                                           \n  std::shared_ptr\u003cNativeModule\u003e native_module =                                 \n      CompileToNativeModule(isolate, enabled, thrower,                          \n                            std::move(result).value(), bytes, \u0026export_wrappers);\n```\n`CompileToNativeModule` can be found in `module-compiler.cc`\n\nTODO: CompileNativeModule...\n\nThere is an example in [wasm_test.cc](./test/wasm_test.cc).\n\n### ExtensionCallback\nIs a typedef defined in `include/v8.h`:\n```c++\ntypedef bool (*ExtensionCallback)(const FunctionCallbackInfo\u003cValue\u003e\u0026); \n```\n\n\n\n\n### JSEntry\nTODO: This section should describe the functions calls below.\n```console\n * frame #0: 0x00007ffff79a52e4 libv8.so`v8::(anonymous namespace)::WebAssemblyModule(v8::FunctionCallbackInfo\u003cv8::Value\u003e const\u0026) [inlined] v8::FunctionCallbackInfo\u003cv8::Value\u003e::GetIsolate(this=0x00007fffffffc9a0) const at v8.h:11204:40\n    frame #1: 0x00007ffff79a52e4 libv8.so`v8::(anonymous namespace)::WebAssemblyModule(args=0x00007fffffffc9a0) at wasm-js.cc:638\n    frame #2: 0x00007ffff6fe9e92 libv8.so`v8::internal::FunctionCallbackArguments::Call(this=0x00007fffffffca40, handler=CallHandlerInfo @ 0x00007fffffffc998) at api-arguments-inl.h:158:3\n    frame #3: 0x00007ffff6fe7c42 libv8.so`v8::internal::MaybeHandle\u003cv8::internal::Object\u003e v8::internal::(anonymous namespace)::HandleApiCallHelper\u003ctrue\u003e(isolate=\u003cunavailable\u003e, function=Handle\u003cv8::internal::HeapObject\u003e @ 0x00007fffffffca20, new_target=\u003cunavailable\u003e, fun_data=\u003cunavailable\u003e, receiver=\u003cunavailable\u003e, args=BuiltinArguments @ 0x00007fffffffcae0) at builtins-api.cc:111:36\n    frame #4: 0x00007ffff6fe67d4 libv8.so`v8::internal::Builtin_Impl_HandleApiCall(args=BuiltinArguments @ 0x00007fffffffcb20, isolate=0x00000f8700000000) at builtins-api.cc:137:5\n    frame #5: 0x00007ffff6fe6319 libv8.so`v8::internal::Builtin_HandleApiCall(args_length=6, args_object=0x00007fffffffcc10, isolate=0x00000f8700000000) at builtins-api.cc:129:1\n    frame #6: 0x00007ffff6b2c23f libv8.so`Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit + 63\n    frame #7: 0x00007ffff68fde25 libv8.so`Builtins_JSBuiltinsConstructStub + 101\n    frame #8: 0x00007ffff6daf46d libv8.so`Builtins_ConstructHandler + 1485\n    frame #9: 0x00007ffff690e1d5 libv8.so`Builtins_InterpreterEntryTrampoline + 213\n    frame #10: 0x00007ffff6904b5a libv8.so`Builtins_JSEntryTrampoline + 90\n    frame #11: 0x00007ffff6904938 libv8.so`Builtins_JSEntry + 120\n    frame #12: 0x00007ffff716ba0c libv8.so`v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const\u0026) [inlined] v8::internal::GeneratedCode\u003cunsigned long, unsigned long, unsigned long, unsigned long, unsigned long, long, unsigned long**\u003e::Call(this=\u003cunavailable\u003e, args=17072495001600, args=\u003cunavailable\u003e, args=17072631376141, args=17072630006049, args=\u003cunavailable\u003e, args=\u003cunavailable\u003e) at simulator.h:142:12\n    frame #13: 0x00007ffff716ba01 libv8.so`v8::internal::(anonymous namespace)::Invoke(isolate=\u003cunavailable\u003e, params=0x00007fffffffcf50)::InvokeParams const\u0026) at execution.cc:367\n    frame #14: 0x00007ffff716aa10 libv8.so`v8::internal::Execution::Call(isolate=0x00000f8700000000, callable=\u003cunavailable\u003e, receiver=\u003cunavailable\u003e, argc=\u003cunavailable\u003e, argv=\u003cunavailable\u003e) at execution.cc:461:10\n\n```\n\n\n### CustomArguments\nSubclasses of CustomArguments, like PropertyCallbackArguments and \nFunctionCallabackArguments are used for setting up and accessing values\non the stack, and also the subclasses provide methods to call various things\nlike `CallNamedSetter` for PropertyCallbackArguments and `Call` for\nFunctionCallbackArguments.\n\n#### FunctionCallbackArguments\n```c++\nclass FunctionCallbackArguments                                                 \n    : public CustomArguments\u003cFunctionCallbackInfo\u003cValue\u003e \u003e {\n  FunctionCallbackArguments(internal::Isolate* isolate, internal::Object data,  \n                            internal::HeapObject callee,                        \n                            internal::Object holder,                            \n                            internal::HeapObject new_target,                    \n                            internal::Address* argv, int argc);\n```\nThis class is in the namespace v8::internal so I'm curious why the explicit\nnamespace is used here?\n\n#### BuiltinArguments\nThis class extends `JavaScriptArguments`\n```c++\nclass BuiltinArguments : public JavaScriptArguments {\n public:\n  BuiltinArguments(int length, Address* arguments)\n      : Arguments(length, arguments) {\n\n  static constexpr int kNewTargetOffset = 0;\n  static constexpr int kTargetOffset = 1;\n  static constexpr int kArgcOffset = 2;\n  static constexpr int kPaddingOffset = 3;\n                                                                                \n  static constexpr int kNumExtraArgs = 4;\n  static constexpr int kNumExtraArgsWithReceiver = 5;\n```\n`JavaScriptArguments is declared in `src/common/global.h`:\n```c++\nusing JavaScriptArguments = Arguments\u003cArgumentsType::kJS\u003e;\n```\n`Arguments` can be found in `src/execution/arguments.h`and is templated with \nthe a type of `ArgumentsType` (in `src/common/globals.h`):\n```c++\nenum class ArgumentsType {                                                          \n  kRuntime,                                                                         \n  kJS,                                                                              \n}; \n```\nAn instance of Arguments only has a length which is the number of arguments,\nand an Address pointer which points to the first argument. The functions it\nprovides allows for getting/setting specific arguments and handling various\ntypes (like `Handle\u003cS\u003e`, smi, etc). It also overloads the operator[] allowing\nto specify an index and getting back an Object to that argument.\nIn `BuiltinArguments` the constants specify the index's and provides functions\nto get them:\n```c++\n  inline Handle\u003cObject\u003e receiver() const;                                       \n  inline Handle\u003cJSFunction\u003e target() const;                                     \n  inline Handle\u003cHeapObject\u003e new_target() const;\n```\n\n### NativeContext\nCan be found in `src/objects/contexts.h` and has the following definition:\n```c++\nclass NativeContext : public Context {\n public:\n\n  DECL_PRIMITIVE_ACCESSORS(microtask_queue, MicrotaskQueue*)\n\n  V8_EXPORT_PRIVATE void AddOptimizedCode(Code code);\n  void SetOptimizedCodeListHead(Object head);\n  Object OptimizedCodeListHead();\n  void SetDeoptimizedCodeListHead(Object head);\n  Object DeoptimizedCodeListHead();\n  inline OSROptimizedCodeCache GetOSROptimizedCodeCache();\n  void ResetErrorsThrown();\n  void IncrementErrorsThrown();\n  int GetErrorsThrown();\n```\n\n`src/parsing/parser.h` we can find:\n```c++\nclass V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase\u003cParser\u003e) { \n  ...\n  enum CompletionKind {                                                             \n    kNormalCompletion,                                                              \n    kThrowCompletion,                                                               \n    kAbruptCompletion                                                               \n  };\n```\nBut I can't find any usages of this enum? \n\n#### Internal fields/methods\nWhen you see something like [[Notation]] you can think of this as a field in\nan object that is not exposed to JavaScript user code but internal to the JavaScript\nengine. These can also be used for internal methods.\n\n\n","funding_links":["https://github.com/sponsors/danbev"],"categories":["C++","Chrome","Basic"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanbev%2Flearning-v8","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanbev%2Flearning-v8","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanbev%2Flearning-v8/lists"}