成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

JS運行時Just源碼解讀

開發 前端
像Node.js一樣,Just也分為內置JS和C++模塊,同樣是在運行時初始化時會處理相關的邏輯。
  • 1 模塊的設計
    • 1.1 C++模塊
    • 1.2 內置JS模塊
    • 1.3 普通JS模塊
    • 1.4 Addon
  • 2 事件循環
  • 3 初始化
  • 4 總結

1 模塊的設計

像Node.js一樣,Just也分為內置JS和C++模塊,同樣是在運行時初始化時會處理相關的邏輯。

1.1 C++模塊

Node.js在初始化時,會把C++模塊組織成一個鏈表,然后加載的時候通過模塊名找到對應的模塊配置,然后執行對應的鉤子函數。Just則是用C++的map來管理C++模塊。目前只有五個C++模塊。

  1. just::modules["sys"] = &_register_sys; 
  2. just::modules["fs"] = &_register_fs; 
  3. just::modules["net"] = &_register_net; 
  4. just::modules["vm"] = &_register_vm; 
  5. just::modules["epoll"] = &_register_epoll; 

Just在初始化時就會執行以上代碼建立模塊名稱到注冊函數地址的關系。我們看一下C++模塊加載器時如何實現C++模塊加載的。

  1. // 加載C++模塊 
  2. function library (name, path) { 
  3.   // 有緩存則直接返回 
  4.   if (cache[name]) return cache[name
  5.   // 調用 
  6.   const lib = just.load(name
  7.   lib.type = 'module' 
  8.   // 緩存起來 
  9.   cache[name] = lib 
  10.   return lib 

just.load是C++實現的。

  1. void just::Load(const FunctionCallbackInfo<Value> &args) { 
  2.   Isolate *isolate = args.GetIsolate(); 
  3.   Local<Context> context = isolate->GetCurrentContext(); 
  4.   // C++模塊導出的信息 
  5.   Local<ObjectTemplate> exports = ObjectTemplate::New(isolate); 
  6.   // 加載某個模塊 
  7.   if (args[0]->IsString()) { 
  8.     String::Utf8Value name(isolate, args[0]); 
  9.     auto iter = just::modules.find(*name); 
  10.     register_plugin _init = (*iter->second); 
  11.     // 執行_init拿到函數地址 
  12.     auto _register = reinterpret_cast<InitializerCallback>(_init()); 
  13.     // 執行C++模塊提供的注冊函數,見C++模塊,導出的屬性在exports對象中 
  14.     _register(isolate, exports); 
  15.   } 
  16.   // 返回導出的信息 
  17.   args.GetReturnValue().Set(exports->NewInstance(context).ToLocalChecked()); 

1.2 內置JS模塊

為了提升加載性能,Node.js的內置JS模塊是保存到內存里的,加載的時候,通過模塊名獲取對應的JS模塊源碼編譯執行,而不需要從硬盤加。比如net模塊在內存里表示為。

  1. static const uint16_t net_raw[] = { 
  2.  47, 47, 32, 67,111,112,121,114... 
  3. }; 

以上的數字轉成字符是["/", "/", " ", "C", "o", "p", "y", "r"],我們發現這些字符是net模塊開始的一些注釋。Just同樣使用了類似的理念,不過Just是通過匯編來處理的。

  1. .global _binary_lib_fs_js_start 
  2. _binary_lib_fs_js_start: 
  3.         .incbin "lib/fs.js" 
  4.         .global _binary_lib_fs_js_end 
  5. _binary_lib_fs_js_end: 
  6. ... 

Just定義里一系列的全局變量 ,比如以上的binary_lib_fs_js_start變量,它對應的值是lib/fs.js的內容,binary_lib_fs_js_end表示結束地址。

值得一提的是,以上的內容是在代碼段的,所以是不能被修改的。接著我們看看如何注冊內置JS模塊,以fs模塊為例。

  1. // builtins.S匯編文件里定義 
  2. extern char _binary_lib_fs_js_start[]; 
  3. extern char _binary_lib_fs_js_end[]; 
  4.  
  5. just::builtins_add("lib/fs.js", _binary_lib_fs_js_start, _binary_lib_fs_js_end - _binary_lib_fs_js_start); 

builtins_add三個參數分別是模塊名,模塊內容的虛擬開始地址,模塊內容大小。來看一下builtins_add的邏輯。

  1. struct builtin { 
  2.   unsigned int size
  3.   const char* source; 
  4. }; 
  5.  
  6. std::map<std::string, just::builtin*> just::builtins; 
  7.  
  8. // 注冊JS模塊 
  9. void just::builtins_add (const charname, const char* source,  unsigned int size) { 
  10.   struct builtin* b = new builtin(); 
  11.   b->size = size
  12.   b->source = source; 
  13.   builtins[name] = b; 

注冊模塊的邏輯很簡單,就是建立模塊名和內容信息的關系,接著看如何加載內置JS模塊。

  1. function requireNative (path) { 
  2.       path = `lib/${path}.js` 
  3.       if (cache[path]) return cache[path].exports 
  4.       const { vm } = just 
  5.       const params = ['exports''require''module'
  6.       const exports = {} 
  7.       const module = { exports, type: 'native', dirName: appRoot } 
  8.       // 從數據結構中獲得模塊對應的源碼 
  9.       module.text = just.builtin(path) 
  10.       // 編譯 
  11.       const fun = vm.compile(module.text, path, params, []) 
  12.       module.function = fun 
  13.       cache[path] = module 
  14.       // 執行 
  15.       fun.call(exports, exports, p => just.require(p, module), module) 
  16.       return module.exports 

加載的邏輯也很簡單,根據模塊名從map里獲取源碼編譯執行,從而拿到導出的屬性。

1.3 普通JS模塊

普通JS模塊就是用戶自定義的模塊。用戶自定義的模塊首次加載時都是需要從硬盤實時加載的,所以只需要看加載的邏輯。

  1. // 一般JS模塊加載器 
  2.   function require (path, parent = { dirName: appRoot }) { 
  3.     const { join, baseName, fileName } = just.path 
  4.     if (path[0] === '@') path = `${appRoot}/lib/${path.slice(1)}/${fileName(path.slice(1))}.js` 
  5.     const ext = path.split('.').slice(-1)[0] 
  6.     // js或json文件 
  7.     if (ext === 'js' || ext === 'json') { 
  8.       let dirName = parent.dirName 
  9.       const fileName = join(dirName, path) 
  10.       // 有緩存則返回 
  11.       if (cache[fileName]) return cache[fileName].exports 
  12.       dirName = baseName(fileName) 
  13.       const params = ['exports''require''module'
  14.       const exports = {} 
  15.       const module = { exports, dirName, fileName, type: ext } 
  16.       // 文件存在則直接加載 
  17.       if (just.fs.isFile(fileName)) { 
  18.         module.text = just.fs.readFile(fileName) 
  19.       } else { 
  20.         // 否則嘗試加載內置JS模塊 
  21.         path = fileName.replace(appRoot, ''
  22.         if (path[0] === '/') path = path.slice(1) 
  23.            module.text = just.builtin(path) 
  24.         } 
  25.       } 
  26.       cache[fileName] = module 
  27.       // js文件則編譯執行,json則直接parse 
  28.       if (ext === 'js') { 
  29.         const fun = just.vm.compile(module.text, fileName, params, []) 
  30.         fun.call(exports, exports, p => require(p, module), module) 
  31.       } else { 
  32.         // 是json文件則直接parse 
  33.         module.exports = JSON.parse(module.text) 
  34.       } 
  35.       return module.exports 
  36.     } 

Just里,普通JS模塊的加載原理和Node.js類似,但是也有些區別,Node.js加載JS模塊時,會優先判斷是不是內置JS模塊,Just則相反。

1.4 Addon

Node.js里的Addon是動態庫,Just里同樣是,原理也類似。

  1. function loadLibrary (path, name) { 
  2.       if (cache[name]) return cache[name
  3.       // 打開動態庫 
  4.       const handle = just.sys.dlopen(path, just.sys.RTLD_LAZY) 
  5.       // 找到動態庫里約定格式的函數的虛擬地址 
  6.       const ptr = just.sys.dlsym(handle, `_register_${name}`) 
  7.       // 以該虛擬地址為入口執行函數 
  8.       const lib = just.load(ptr) 
  9.       lib.close = () => just.sys.dlclose(handle) 
  10.       lib.type = 'module-external' 
  11.       cache[name] = lib 
  12.       return lib 

just.load是C++實現的函數。

  1. void just::Load(const FunctionCallbackInfo<Value> &args) { 
  2.   Isolate *isolate = args.GetIsolate(); 
  3.   Local<Context> context = isolate->GetCurrentContext(); 
  4.   // C++模塊導出的信息 
  5.   Local<ObjectTemplate> exports = ObjectTemplate::New(isolate); 
  6.   // 傳入的是注冊函數的虛擬地址(動態庫) 
  7.    Local<BigInt> address64 = Local<BigInt>::Cast(args[0]); 
  8.    void* ptr = reinterpret_cast<void*>(address64->Uint64Value()); 
  9.    register_plugin _init = reinterpret_cast<register_plugin>(ptr); 
  10.    auto _register = reinterpret_cast<InitializerCallback>(_init()); 
  11.    _register(isolate, exports); 
  12.   // 返回導出的信息 
  13.   args.GetReturnValue().Set(exports->NewInstance(context).ToLocalChecked()); 

因為Addon是動態庫,所以底層原理都是對系統API的封裝,再通過V8暴露給JS層使用。

2 事件循環

Just的事件循環是基于epoll的,所有生產者生產的任務都是基于文件描述符的,相比Node.js清晰且簡潔了很多,也沒有了各種階段。Just支持多個事件循環,不過目前只有內置的一個。我們看看如何創建一個事件循環。

  1. // 創建一個事件循環 
  2. function create(nevents = 128) { 
  3.   const loop = createLoop(nevents) 
  4.   factory.loops.push(loop) 
  5.   return loop 
  6.  
  7. function createLoop (nevents = 128) { 
  8.   const evbuf = new ArrayBuffer(nevents * 12) 
  9.   const events = new Uint32Array(evbuf) 
  10.   // 創建一個epoll 
  11.   const loopfd = create(EPOLL_CLOEXEC) 
  12.   const handles = {} 
  13.   // 判斷是否有事件觸發 
  14.   function poll (timeout = -1, sigmask) { 
  15.     let r = 0 
  16.     // 對epoll_wait的封裝 
  17.     if (sigmask) { 
  18.       r = wait(loopfd, evbuf, timeout, sigmask) 
  19.     } else { 
  20.       r = wait(loopfd, evbuf, timeout) 
  21.     } 
  22.     if (r > 0) { 
  23.       let off = 0 
  24.       for (let i = 0; i < r; i++) { 
  25.         const fd = events[off + 1] 
  26.         // 事件觸發,執行回調 
  27.         handles[fd](fd, events[off]) 
  28.         off += 3 
  29.       } 
  30.     } 
  31.     return r 
  32.   } 
  33.   // 注冊新的fd和事件 
  34.   function add (fd, callback, events = EPOLLIN) { 
  35.     const r = control(loopfd, EPOLL_CTL_ADD, fd, events) 
  36.     // 保存回調 
  37.     if (r === 0) { 
  38.       handles[fd] = callback 
  39.       instance.count++ 
  40.     } 
  41.     return r 
  42.   } 
  43.   // 刪除之前注冊的fd和事件 
  44.   function remove (fd) { 
  45.     const r = control(loopfd, EPOLL_CTL_DEL, fd) 
  46.     if (r === 0) { 
  47.       delete handles[fd] 
  48.       instance.count-- 
  49.     } 
  50.     return r 
  51.   } 
  52.   // 更新之前注冊的fd和事件 
  53.   function update (fd, events = EPOLLIN) { 
  54.     const r = control(loopfd, EPOLL_CTL_MOD, fd, events) 
  55.     return r 
  56.   } 
  57.   const instance = { fd: loopfd, poll, add, remove, update, handles, count: 0 } 
  58.   return instance 

事件循環本質是epoll的封裝,一個事件循環對應一個epoll fd,后續生產任務的時候,就通過操作epoll fd,進行增刪改查,比如注冊一個新的fd和事件到epoll中,并保存對應的回調。然后通過wait進入事件循環,有事件觸發后,就執行對應的回調。接著看一下事件循環的執行。

  1.         // 執行事件循環,即遍歷每個事件循環 
  2.   run: (ms = -1) => { 
  3.     factory.paused = false 
  4.     let empty = 0 
  5.     while (!factory.paused) { 
  6.       let total = 0 
  7.       for (const loop of factory.loops) { 
  8.         if (loop.count > 0) loop.poll(ms) 
  9.         total += loop.count 
  10.       } 
  11.       // 執行微任務 
  12.       runMicroTasks() 
  13.       ... 
  14.   }, 
  15.  
  16.   stop: () => { 
  17.     factory.paused = true 
  18.   }, 

Just初始化完畢后就會通過run進入事件循環,這個和Node.js是類似的。

3 初始化

了解了一些核心的實現后,來看一下Just的初始化。

  1. int main(int argc, char** argv) { 
  2.   // 忽略V8的一些邏輯 
  3.   // 注冊內置模塊 
  4.   register_builtins(); 
  5.   // 初始化isolate 
  6.   just::CreateIsolate(argc, argv, just_js, just_js_len); 
  7.   return 0; 

繼續看CreateIsolate(只列出核心代碼)

  1. int just::CreateIsolate(...) { 
  2.   Isolate::CreateParams create_params; 
  3.   int statusCode = 0; 
  4.   // 分配ArrayBuffer的內存分配器 
  5.   create_params.array_buffer_allocator =  ArrayBuffer::Allocator::NewDefaultAllocator(); 
  6.   Isolate *isolate = Isolate::New(create_params); 
  7.   { 
  8.     Isolate::Scope isolate_scope(isolate); 
  9.     HandleScope handle_scope(isolate); 
  10.  
  11.     // 新建一個對象為全局對象 
  12.     Local<ObjectTemplate> global = ObjectTemplate::New(isolate); 
  13.     // 新建一個對象為核心對象,也是個全局對象 
  14.     Local<ObjectTemplate> just = ObjectTemplate::New(isolate); 
  15.     // 設置一些屬性到just對象 
  16.     just::Init(isolate, just); 
  17.     // 設置全局屬性just 
  18.     global->Set(String::NewFromUtf8Literal(isolate, "just", NewStringType::kNormal), just); 
  19.     // 新建上下文,并且以global為全局對象 
  20.     Local<Context> context = Context::New(isolate, NULLglobal); 
  21.     Context::Scope context_scope(context); 
  22.     Local<Object> globalInstance = context->Global(); 
  23.     // 設置全局屬性global指向全局對象 
  24.     globalInstance->Set(context, String::NewFromUtf8Literal(isolate,  
  25.       "global",  
  26.       NewStringType::kNormal), globalInstance).Check(); 
  27.  
  28.     // 編譯執行just.js,just.js是核心的jS代碼 
  29.     MaybeLocal<Value> maybe_result = script->Run(context); 
  30.   } 

初始化的時候設置了全局對象global和just,所以在JS里可以直接訪問,然后再給just對象設置各種屬性,接著看just.js的邏輯。

  1. function main (opts) { 
  2.     // 獲得C++模塊加載器和緩存 
  3.     const { library, cache } = wrapLibrary() 
  4.  
  5.     // 掛載C++模塊到JS 
  6.     just.vm = library('vm').vm 
  7.     just.loop = library('epoll').epoll 
  8.     just.fs = library('fs').fs 
  9.     just.net = library('net').net 
  10.     just.sys = library('sys').sys 
  11.     // 環境變量 
  12.     just.env = wrapEnv(just.sys.env) 
  13.     // JS模塊加載器 
  14.     const { requireNative, require } = wrapRequire(cache) 
  15.  
  16.     Object.assign(just.fs, requireNative('fs')) 
  17.  
  18.     just.path = requireNative('path'
  19.     just.factory = requireNative('loop').factory 
  20.     just.factory.loop = just.factory.create(128) 
  21.     just.process = requireNative('process'
  22.     just.setTimeout = setTimeout 
  23.     just.library = library 
  24.     just.requireNative = requireNative 
  25.     just.net.setNonBlocking = setNonBlocking 
  26.     just.require = global.require = require 
  27.     just.require.cache = cache 
  28.     // 執行用戶js 
  29.     just.vm.runScript(just.fs.readFile(just.args[1]), scriptName) 
  30.     // 進入時間循環 
  31.     just.factory.run() 
  32.   } 

4 總結

Just的底層實現在modules里,里面的實現非常清晰,里面對大量系統API和開源庫進行了封裝。另外使用了timerfd支持定時器,而不是自己去維護相關邏輯。核心模塊代碼非常值得學習,有興趣的可以直接去看對應模塊的源碼。Just的代碼整體很清晰,而且目前的代碼量不大,通過閱讀里面的代碼,對系統、網絡、V8的學習都有幫助,另外里面用到了很多開源庫,也可以學到如何使用一些優秀的開源庫,甚至閱讀庫的源碼。

源碼解析地址:

https://github.com/theanarkh/read-just-0.1.4-code

 

責任編輯:武曉燕 來源: 編程雜技
相關推薦

2024-03-21 09:15:58

JS運行的JavaScrip

2015-07-20 15:44:46

Swift框架MJExtension反射

2022-10-08 00:00:00

V8channel對象

2023-09-12 17:38:41

2022-10-08 00:06:00

JS運行V8

2019-07-12 09:30:12

DashboardDockerDNS

2021-09-11 15:38:23

容器運行鏡像開放

2021-07-10 07:39:38

Node.js C++V8

2021-09-07 11:19:42

操作系統華為鴻蒙

2013-11-26 16:49:55

Android開發運行時KitKat

2021-08-18 08:32:09

代碼運行時間示波器

2020-12-07 13:31:43

GoMutex開發者

2023-07-28 10:42:43

2023-01-03 09:10:21

2024-03-20 10:46:00

云原生容器

2022-01-19 08:50:53

設備樹Linux文件系統

2023-02-12 12:00:57

2023-08-29 08:20:35

Kubernete跨云容器

2022-12-30 08:08:30

2021-10-14 09:53:38

鴻蒙HarmonyOS應用
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 五月槐花香| 一区二区三区国产好 | 91在线播 | 日日夜夜av | 综合精品久久久 | 天天天操操操 | 午夜国产羞羞视频免费网站 | 91精品国产乱码久久久久久久久 | 欧美视频成人 | 久久精品中文字幕 | 麻豆久久久久 | 欧美激情精品久久久久久 | 夜夜操操操 | 国产激情在线播放 | 欧美精品1区2区3区 精品国产欧美一区二区 | 亚洲 成人 在线 | 色婷婷在线视频 | 国产日韩91 | 久久精彩视频 | 亚洲精品久久久久久宅男 | 日韩精品一区二区三区中文字幕 | 极品粉嫩国产48尤物在线播放 | 伊人91在线 | 天天玩天天操天天干 | 91久久国产精品 | 亚洲一区二区三区免费视频 | 中文字幕视频在线观看 | 午夜网站视频 | 国产精品免费观看视频 | 在线免费观看成人 | 国产精品成人一区 | 国产高清在线观看 | 日韩综合在线 | 久久国产精品久久久久 | 中文欧美日韩 | 精品视频导航 | 久久婷婷国产 | 久久99国产精一区二区三区 | 日韩午夜电影在线观看 | 影音先锋中文字幕在线观看 | 欧美精品91 |