自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

JS運(yùn)行時(shí)Just源碼解讀

開發(fā) 前端
像Node.js一樣,Just也分為內(nèi)置JS和C++模塊,同樣是在運(yùn)行時(shí)初始化時(shí)會(huì)處理相關(guān)的邏輯。
  • 1 模塊的設(shè)計(jì)
    • 1.1 C++模塊
    • 1.2 內(nèi)置JS模塊
    • 1.3 普通JS模塊
    • 1.4 Addon
  • 2 事件循環(huán)
  • 3 初始化
  • 4 總結(jié)

1 模塊的設(shè)計(jì)

像Node.js一樣,Just也分為內(nèi)置JS和C++模塊,同樣是在運(yùn)行時(shí)初始化時(shí)會(huì)處理相關(guān)的邏輯。

1.1 C++模塊

Node.js在初始化時(shí),會(huì)把C++模塊組織成一個(gè)鏈表,然后加載的時(shí)候通過(guò)模塊名找到對(duì)應(yīng)的模塊配置,然后執(zhí)行對(duì)應(yīng)的鉤子函數(shù)。Just則是用C++的map來(lái)管理C++模塊。目前只有五個(gè)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在初始化時(shí)就會(huì)執(zhí)行以上代碼建立模塊名稱到注冊(cè)函數(shù)地址的關(guān)系。我們看一下C++模塊加載器時(shí)如何實(shí)現(xiàn)C++模塊加載的。

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

just.load是C++實(shí)現(xiàn)的。

  1. void just::Load(const FunctionCallbackInfo<Value> &args) { 
  2.   Isolate *isolate = args.GetIsolate(); 
  3.   Local<Context> context = isolate->GetCurrentContext(); 
  4.   // C++模塊導(dǎo)出的信息 
  5.   Local<ObjectTemplate> exports = ObjectTemplate::New(isolate); 
  6.   // 加載某個(gè)模塊 
  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.     // 執(zhí)行_init拿到函數(shù)地址 
  12.     auto _register = reinterpret_cast<InitializerCallback>(_init()); 
  13.     // 執(zhí)行C++模塊提供的注冊(cè)函數(shù),見C++模塊,導(dǎo)出的屬性在exports對(duì)象中 
  14.     _register(isolate, exports); 
  15.   } 
  16.   // 返回導(dǎo)出的信息 
  17.   args.GetReturnValue().Set(exports->NewInstance(context).ToLocalChecked()); 

1.2 內(nèi)置JS模塊

為了提升加載性能,Node.js的內(nèi)置JS模塊是保存到內(nèi)存里的,加載的時(shí)候,通過(guò)模塊名獲取對(duì)應(yīng)的JS模塊源碼編譯執(zhí)行,而不需要從硬盤加。比如net模塊在內(nèi)存里表示為。

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

以上的數(shù)字轉(zhuǎn)成字符是["/", "/", " ", "C", "o", "p", "y", "r"],我們發(fā)現(xiàn)這些字符是net模塊開始的一些注釋。Just同樣使用了類似的理念,不過(guò)Just是通過(guò)匯編來(lái)處理的。

  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變量,它對(duì)應(yīng)的值是lib/fs.js的內(nèi)容,binary_lib_fs_js_end表示結(jié)束地址。

值得一提的是,以上的內(nèi)容是在代碼段的,所以是不能被修改的。接著我們看看如何注冊(cè)內(nèi)置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三個(gè)參數(shù)分別是模塊名,模塊內(nèi)容的虛擬開始地址,模塊內(nèi)容大小。來(lái)看一下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. // 注冊(cè)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; 

注冊(cè)模塊的邏輯很簡(jiǎn)單,就是建立模塊名和內(nèi)容信息的關(guān)系,接著看如何加載內(nèi)置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.       // 從數(shù)據(jù)結(jié)構(gòu)中獲得模塊對(duì)應(yīng)的源碼 
  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.       // 執(zhí)行 
  15.       fun.call(exports, exports, p => just.require(p, module), module) 
  16.       return module.exports 

加載的邏輯也很簡(jiǎn)單,根據(jù)模塊名從map里獲取源碼編譯執(zhí)行,從而拿到導(dǎo)出的屬性。

1.3 普通JS模塊

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

  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.         // 否則嘗試加載內(nèi)置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文件則編譯執(zhí)行,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類似,但是也有些區(qū)別,Node.js加載JS模塊時(shí),會(huì)優(yōu)先判斷是不是內(nèi)置JS模塊,Just則相反。

1.4 Addon

Node.js里的Addon是動(dòng)態(tài)庫(kù),Just里同樣是,原理也類似。

  1. function loadLibrary (path, name) { 
  2.       if (cache[name]) return cache[name
  3.       // 打開動(dòng)態(tài)庫(kù) 
  4.       const handle = just.sys.dlopen(path, just.sys.RTLD_LAZY) 
  5.       // 找到動(dòng)態(tài)庫(kù)里約定格式的函數(shù)的虛擬地址 
  6.       const ptr = just.sys.dlsym(handle, `_register_${name}`) 
  7.       // 以該虛擬地址為入口執(zhí)行函數(shù) 
  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++實(shí)現(xiàn)的函數(shù)。

  1. void just::Load(const FunctionCallbackInfo<Value> &args) { 
  2.   Isolate *isolate = args.GetIsolate(); 
  3.   Local<Context> context = isolate->GetCurrentContext(); 
  4.   // C++模塊導(dǎo)出的信息 
  5.   Local<ObjectTemplate> exports = ObjectTemplate::New(isolate); 
  6.   // 傳入的是注冊(cè)函數(shù)的虛擬地址(動(dòng)態(tài)庫(kù)) 
  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.   // 返回導(dǎo)出的信息 
  13.   args.GetReturnValue().Set(exports->NewInstance(context).ToLocalChecked()); 

因?yàn)锳ddon是動(dòng)態(tài)庫(kù),所以底層原理都是對(duì)系統(tǒng)API的封裝,再通過(guò)V8暴露給JS層使用。

2 事件循環(huán)

Just的事件循環(huán)是基于epoll的,所有生產(chǎn)者生產(chǎn)的任務(wù)都是基于文件描述符的,相比Node.js清晰且簡(jiǎn)潔了很多,也沒(méi)有了各種階段。Just支持多個(gè)事件循環(huán),不過(guò)目前只有內(nèi)置的一個(gè)。我們看看如何創(chuàng)建一個(gè)事件循環(huán)。

  1. // 創(chuàng)建一個(gè)事件循環(huán) 
  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.   // 創(chuàng)建一個(gè)epoll 
  11.   const loopfd = create(EPOLL_CLOEXEC) 
  12.   const handles = {} 
  13.   // 判斷是否有事件觸發(fā) 
  14.   function poll (timeout = -1, sigmask) { 
  15.     let r = 0 
  16.     // 對(duì)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.         // 事件觸發(fā),執(zhí)行回調(diào) 
  27.         handles[fd](fd, events[off]) 
  28.         off += 3 
  29.       } 
  30.     } 
  31.     return r 
  32.   } 
  33.   // 注冊(cè)新的fd和事件 
  34.   function add (fd, callback, events = EPOLLIN) { 
  35.     const r = control(loopfd, EPOLL_CTL_ADD, fd, events) 
  36.     // 保存回調(diào) 
  37.     if (r === 0) { 
  38.       handles[fd] = callback 
  39.       instance.count++ 
  40.     } 
  41.     return r 
  42.   } 
  43.   // 刪除之前注冊(cè)的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.   // 更新之前注冊(cè)的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 

事件循環(huán)本質(zhì)是epoll的封裝,一個(gè)事件循環(huán)對(duì)應(yīng)一個(gè)epoll fd,后續(xù)生產(chǎn)任務(wù)的時(shí)候,就通過(guò)操作epoll fd,進(jìn)行增刪改查,比如注冊(cè)一個(gè)新的fd和事件到epoll中,并保存對(duì)應(yīng)的回調(diào)。然后通過(guò)wait進(jìn)入事件循環(huán),有事件觸發(fā)后,就執(zhí)行對(duì)應(yīng)的回調(diào)。接著看一下事件循環(huán)的執(zhí)行。

  1.         // 執(zhí)行事件循環(huán),即遍歷每個(gè)事件循環(huán) 
  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.       // 執(zhí)行微任務(wù) 
  12.       runMicroTasks() 
  13.       ... 
  14.   }, 
  15.  
  16.   stop: () => { 
  17.     factory.paused = true 
  18.   }, 

Just初始化完畢后就會(huì)通過(guò)run進(jìn)入事件循環(huán),這個(gè)和Node.js是類似的。

3 初始化

了解了一些核心的實(shí)現(xiàn)后,來(lái)看一下Just的初始化。

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

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

  1. int just::CreateIsolate(...) { 
  2.   Isolate::CreateParams create_params; 
  3.   int statusCode = 0; 
  4.   // 分配ArrayBuffer的內(nèi)存分配器 
  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.     // 新建一個(gè)對(duì)象為全局對(duì)象 
  12.     Local<ObjectTemplate> global = ObjectTemplate::New(isolate); 
  13.     // 新建一個(gè)對(duì)象為核心對(duì)象,也是個(gè)全局對(duì)象 
  14.     Local<ObjectTemplate> just = ObjectTemplate::New(isolate); 
  15.     // 設(shè)置一些屬性到j(luò)ust對(duì)象 
  16.     just::Init(isolate, just); 
  17.     // 設(shè)置全局屬性just 
  18.     global->Set(String::NewFromUtf8Literal(isolate, "just", NewStringType::kNormal), just); 
  19.     // 新建上下文,并且以global為全局對(duì)象 
  20.     Local<Context> context = Context::New(isolate, NULLglobal); 
  21.     Context::Scope context_scope(context); 
  22.     Local<Object> globalInstance = context->Global(); 
  23.     // 設(shè)置全局屬性global指向全局對(duì)象 
  24.     globalInstance->Set(context, String::NewFromUtf8Literal(isolate,  
  25.       "global",  
  26.       NewStringType::kNormal), globalInstance).Check(); 
  27.  
  28.     // 編譯執(zhí)行just.js,just.js是核心的jS代碼 
  29.     MaybeLocal<Value> maybe_result = script->Run(context); 
  30.   } 

初始化的時(shí)候設(shè)置了全局對(duì)象global和just,所以在JS里可以直接訪問(wèn),然后再給just對(duì)象設(shè)置各種屬性,接著看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.     // 環(huán)境變量 
  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.     // 執(zhí)行用戶js 
  29.     just.vm.runScript(just.fs.readFile(just.args[1]), scriptName) 
  30.     // 進(jìn)入時(shí)間循環(huán) 
  31.     just.factory.run() 
  32.   } 

4 總結(jié)

Just的底層實(shí)現(xiàn)在modules里,里面的實(shí)現(xiàn)非常清晰,里面對(duì)大量系統(tǒng)API和開源庫(kù)進(jìn)行了封裝。另外使用了timerfd支持定時(shí)器,而不是自己去維護(hù)相關(guān)邏輯。核心模塊代碼非常值得學(xué)習(xí),有興趣的可以直接去看對(duì)應(yīng)模塊的源碼。Just的代碼整體很清晰,而且目前的代碼量不大,通過(guò)閱讀里面的代碼,對(duì)系統(tǒng)、網(wǎng)絡(luò)、V8的學(xué)習(xí)都有幫助,另外里面用到了很多開源庫(kù),也可以學(xué)到如何使用一些優(yōu)秀的開源庫(kù),甚至閱讀庫(kù)的源碼。

源碼解析地址:

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

 

責(zé)任編輯:武曉燕 來(lái)源: 編程雜技
相關(guān)推薦

2024-03-21 09:15:58

JS運(yùn)行的JavaScrip

2015-07-20 15:44:46

Swift框架MJExtension反射

2022-10-08 00:00:00

V8channel對(duì)象

2023-09-12 17:38:41

2022-10-08 00:06:00

JS運(yùn)行V8

2019-07-12 09:30:12

DashboardDockerDNS

2021-09-11 15:38:23

容器運(yùn)行鏡像開放

2021-09-07 11:19:42

操作系統(tǒng)華為鴻蒙

2021-07-10 07:39:38

Node.js C++V8

2021-08-18 08:32:09

代碼運(yùn)行時(shí)間示波器

2013-11-26 16:49:55

Android開發(fā)運(yùn)行時(shí)KitKat

2020-12-07 13:31:43

GoMutex開發(fā)者

2023-01-03 09:10:21

2023-07-28 10:42:43

2022-01-19 08:50:53

設(shè)備樹Linux文件系統(tǒng)

2024-03-20 10:46:00

云原生容器

2021-10-14 09:53:38

鴻蒙HarmonyOS應(yīng)用

2023-02-12 12:00:57

2023-08-29 08:20:35

Kubernete跨云容器

2022-12-30 08:08:30

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)