deno-原理篇一啓動加載

以前篇章

deno-基礎篇,主要是deno的一些基本概念介紹。c++

deno執行過程概述

deno初始化時會讀取內嵌的Typescript代碼和加載v8 isolate實例,將須要執行的文件路徑做爲參數傳入,在內部解析傳入的Typescript/Javascript文件地址,加載須要執行的代碼,若是是Typescript代碼,經過初始化加載的Typescript編譯器將代碼編譯成Javascript,而後將Javacript傳給v8 isolate實例,並獲取可執行句柄對象,調用執行方法執行具體的代碼。typescript

deno初始化過程

初始化流程主要分爲幾步:segmentfault

  1. 解析外部輸入
  2. 加載全局狀態信息,
  3. 建立權限實例對象
  4. 讀取Typescript編譯器及運行時API代碼
  5. 初始化v8 isolate
  6. 啓動運行時

deno啓動入口是rust,啓動代碼包含在main.rs的rust文件中,執行deno時會去執行main.rs文件的main方法。下面是main方法中的部分代碼:後端

fn main() {
  // ...
  let args = env::args().collect();
  let (mut flags, mut rest_argv, usage_string) = flags::set_flags(args)
    .unwrap_or_else(|err| {
      eprintln!("{}", err);
      std::process::exit(1)
    });

 // ....

  let should_prefetch = flags.prefetch || flags.info;
  let should_display_info = flags.info;

  let state = Arc::new(isolate::IsolateState::new(flags, rest_argv, None));
  let isolate_init = isolate_init::deno_isolate_init();
  let permissions = permissions::DenoPermissions::from_flags(&state.flags);
  let mut isolate =
    isolate::Isolate::new(isolate_init, state, ops::dispatch, permissions);

  tokio_util::init(|| {
    // Setup runtime.
    isolate
      .execute("denoMain();")
      .map_err(errors::RustOrJsError::from)
      .unwrap_or_else(print_err_and_exit);

    // Execute main module.
    if let Some(main_module) = isolate.state.main_module() {
      debug!("main_module {}", main_module);
      isolate
        .execute_mod(&main_module, should_prefetch)
        .unwrap_or_else(print_err_and_exit);
      if should_display_info {
        // Display file info and exit. Do not run file
        modules::print_file_info(
          &isolate.modules.borrow(),
          &isolate.state.dir,
          main_module,
        );
        std::process::exit(0);
      }
    }

    isolate
      .event_loop()
      .map_err(errors::RustOrJsError::from)
      .unwrap_or_else(print_err_and_exit);
  });
}
解析外部輸入

首先經過rust的env模塊去搜集在命令行運行deno命令時傳入的參數,而後根據傳入參數變量做爲條件去完成初始化和執行過程的任務。安全

let args = env::args().collect();
加載全局狀態消息
let state = Arc::new(isolate::IsolateState::new(flags, rest_argv, None));

Arc是rust的一個crate,它能夠建立一個線程安全的,會記錄引用數的指針(A thread-safe reference-counting pointer)。上面代碼將獲取的外部輸入做爲參數,構造了一個IsolateState對象。該對象的數據結構以下:網絡

pub struct IsolateState {
  pub dir: deno_dir::DenoDir,
  pub argv: Vec<String>,
  pub flags: flags::DenoFlags,
  pub metrics: Metrics,
  pub worker_channels: Option<Mutex<WorkerChannels>>,
}

這裏對IsolateState除了包含了外部傳入的基本數據信息,還包含了處理數據的基本方法,這裏對它不作詳細的解析。獲取到state變量後,它因爲被Arc包裹後,能夠被多個線程安全的訪問,實現線程以前的狀態共享。這裏對IsolateState不作過多的敘述。數據結構

建立權限實例
let permissions = permissions::DenoPermissions::from_flags(&state.flags);

permission實例結構以下,它是一個struct類型:異步

pub struct DenoPermissions {
  // Keep in sync with src/permissions.ts
  pub allow_read: AtomicBool,
  pub allow_write: AtomicBool,
  pub allow_net: AtomicBool,
  pub allow_env: AtomicBool,
  pub allow_run: AtomicBool,
}

經過from_flags方法,對上面結構體中的變量賦值async

pub fn from_flags(flags: &DenoFlags) -> Self {
    Self {
      allow_read: AtomicBool::new(flags.allow_read),
      allow_write: AtomicBool::new(flags.allow_write),
      allow_env: AtomicBool::new(flags.allow_env),
      allow_net: AtomicBool::new(flags.allow_net),
      allow_run: AtomicBool::new(flags.allow_run),
    }
  }

permission實例中還包含了一些權限檢查的方法,主要針對是否容許讀文件、寫文件、設置環境變量、訪問網絡請求、代碼執行等幾個方面。這也是算是deno的一大特色吧,在安全方面能夠作到很好的限制。ide

讀取Typescript編譯器及運行時API代碼
let isolate_init = isolate_init::deno_isolate_init();

deno_isolate_init方法初始化構造了v8 isolate初始化時須要加載的數據。主要是建立了一個IsolateInit實例,它包含的結構以下:

pub struct IsolateInitScript {
  pub source: String,
  pub filename: String,
}

pub struct IsolateInit {
  pub snapshot: Option<deno_buf>,
  pub init_script: Option<IsolateInitScript>,
}

在deno_isolate_init方法中,主要作的事情是加載typescript編譯器和User API的代碼。

pub fn deno_isolate_init() -> IsolateInit {
  if cfg!(not(feature = "check-only")) {
    if cfg!(feature = "use-snapshot-init") {
      let data =
        include_bytes!(concat!(env!("GN_OUT_DIR"), "/gen/snapshot_deno.bin"));

      unsafe {
        IsolateInit {
          snapshot: Some(deno_buf::from_raw_parts(data.as_ptr(), data.len())),
          init_script: None,
        }
      }
    } else {
      #[cfg(not(feature = "check-only"))]
      let source_bytes =
        include_bytes!(concat!(env!("GN_OUT_DIR"), "/gen/bundle/main.js"));

      #[cfg(feature = "check-only")]
      let source_bytes = vec![];

      IsolateInit {
        snapshot: None,
        init_script: Some(IsolateInitScript {
          filename: "gen/bundle/main.js".to_string(),
          source: std::str::from_utf8(source_bytes).unwrap().to_string(),
        }),
      }
    }
  } else {
    IsolateInit {
      snapshot: None,
      init_script: None,
    }
  }
}

上面的方法中主要經過兩種方式加載初始化代碼,一種是加載二進制的形式的Typescript代碼,二進制代碼都包含在snapshot_deno.bin文件中,第二中是直接經過Javascript的文件方式初始化,代碼主要包含在main.js文件中。

初始化v8 isolate
let mut isolate =
    isolate::Isolate::new(isolate_init, state, ops::dispatch, permissions);

上面這行代碼將全局狀態數據、初始化Typescript代碼數據、權限數據傳給Isolate的new方法,構造了Isolate實例,Isolate的new方法可執行代碼以下:

pub fn new(
    init: IsolateInit,
    state: Arc<IsolateState>,
    dispatch: Dispatch,
    permissions: DenoPermissions,
  ) -> Self {
    DENO_INIT.call_once(|| {
      unsafe { libdeno::deno_init() };
    });
    let config = libdeno::deno_config {
      will_snapshot: 0,
      load_snapshot: match init.snapshot {
        Some(s) => s,
        None => libdeno::deno_buf::empty(),
      },
      shared: libdeno::deno_buf::empty(), // TODO Use for message passing.
      recv_cb: pre_dispatch,
    };
    let libdeno_isolate = unsafe { libdeno::deno_new(config) };
    // This channel handles sending async messages back to the runtime.
    let (tx, rx) = mpsc::channel::<(usize, Buf)>();

    let new_isolate = Self {
      libdeno_isolate,
      dispatch,
      rx,
      tx,
      ntasks: Cell::new(0),
      timeout_due: Cell::new(None),
      modules: RefCell::new(Modules::new()),
      state,
      permissions: Arc::new(permissions),
    };

    // Run init script if present.
    match init.init_script {
      Some(init_script) => new_isolate
        .execute2(init_script.filename.as_str(), init_script.source.as_str())
        .unwrap(),
      None => {}
    };

    new_isolate
  }

上面代碼中很重要的一個變量是libdeno,它的主要實現使用的c++,包含在deno的中間層,它主要做用是初始化v8引擎,以及實現Javascript和rust之間的消息傳遞。libdeno::deno_new方法的代碼以下:

Deno* deno_new(deno_config config) {
  if (config.will_snapshot) {
    return deno_new_snapshotter(config);
  }
  deno::DenoIsolate* d = new deno::DenoIsolate(config);
  v8::Isolate::CreateParams params;
  params.array_buffer_allocator = d->array_buffer_allocator_;
  params.external_references = deno::external_references;

  if (config.load_snapshot.data_ptr) {
    params.snapshot_blob = &d->snapshot_;
  }

  v8::Isolate* isolate = v8::Isolate::New(params);
  d->AddIsolate(isolate);

  v8::Locker locker(isolate);
  v8::Isolate::Scope isolate_scope(isolate);
  {
    v8::HandleScope handle_scope(isolate);
    auto context =
        v8::Context::New(isolate, nullptr, v8::MaybeLocal<v8::ObjectTemplate>(),
                         v8::MaybeLocal<v8::Value>(),
                         v8::DeserializeInternalFieldsCallback(
                             deno::DeserializeInternalFields, nullptr));
    if (!config.load_snapshot.data_ptr) {
      // If no snapshot is provided, we initialize the context with empty
      // main source code and source maps.
      deno::InitializeContext(isolate, context);
    }
    d->context_.Reset(isolate, context);
  }

  return reinterpret_cast<Deno*>(d);
}

代碼能夠看出,deno_config是在rust代碼中調用libdeno:new時傳入的配置參數,而後調用v8::Isolate::New方法初始化了一個v8 isolate實例。libdeno這裏不詳細說明了,後面一節講Javascript和rust傳遞消息機制時在詳細說明。

到這裏,deno的初始化階段基本完成了80%了。
啓動運行時
isolate
      .execute("denoMain();")
      .map_err(errors::RustOrJsError::from)
      .unwrap_or_else(print_err_and_exit);

由前面的步驟知道,isolate對象包裹了一個v8 isolate實例,並加載了運行時所需Ttypescript代碼,isolate.execute("denoMain()")其實是去調用v8 isolate實例的方法執行初始化過程當中加載的Typescript代碼,固然不是直接執行Typescript,而是由Typescript編譯後的Javascript代碼或者是二進制字節碼。doMain方法包含的代碼以下:

export default function denoMain() {
  const startResMsg = os.start();

  // TODO(kitsonk) remove when import "deno" no longer supported
  libdeno.builtinModules["deno"] = deno;
  Object.freeze(libdeno.builtinModules);

  setVersions(startResMsg.denoVersion()!, startResMsg.v8Version()!);

  // handle `--version`
  if (startResMsg.versionFlag()) {
    console.log("deno:", deno.version.deno);
    console.log("v8:", deno.version.v8);
    console.log("typescript:", deno.version.typescript);
    os.exit(0);
  }

  // handle `--types`
  // TODO(kitsonk) move to Rust fetching from compiler
  if (startResMsg.typesFlag()) {
    console.log(libDts);
    os.exit(0);
  }

  const mainModule = startResMsg.mainModule();
  if (mainModule) {
    assert(mainModule.length > 0);
    setLocation(mainModule);
  }

  const cwd = startResMsg.cwd();
  log("cwd", cwd);

  for (let i = 1; i < startResMsg.argvLength(); i++) {
    args.push(startResMsg.argv(i));
  }
  log("args", args);
  Object.freeze(args);

  if (!mainModule) {
    replLoop();
  }
}

上面的代碼中關鍵的兩行代碼以下

const startResMsg = os.start();
  libdeno.builtinModules["deno"] = deno;

第一行代碼表示向deno的後端發送啓動消息,並獲取基本狀態信息和版本等數據,返回的數據都包含在startResMSG中。返回的消息數據格式定義以下:

table StartRes {
  cwd: string;
  pid: uint32;
  argv: [string];
  exec_path: string;
  main_module: string; // Absolute URL.
  debug_flag: bool;
  deps_flag: bool;
  types_flag: bool;
  version_flag: bool;
  deno_version: string;
  v8_version: string;
  no_color: bool;
}

StartRes表示從rust返回的消息格式,主要包含v8的版本信息,當前pid,以及主模塊等。
第二行代碼表示將deno對象賦值給libdeno.buildModules["deno"]變量,deno對象主要包含全部User API。

執行Typescript/Javascript代碼

deno啓動工做完成後,就時執行Javascript/Typescript的代碼,此處主要分析deno直接執行文件代碼的形式,交互式的方式在後邊章節會分析。在命令行執行deno ./**.ts的時候,在解析外部輸入的環節能夠獲取須要執行的文件路徑,將路徑基於當前環境上下文作一些處理,構形成完整的文件地址。接着調用isolate對象的execute_mod方法去執行具體的代碼。代碼細節以下:

if let Some(main_module) = isolate.state.main_module() {
      debug!("main_module {}", main_module);
      isolate
        .execute_mod(&main_module, should_prefetch)
        .unwrap_or_else(print_err_and_exit);
      if should_display_info {
        // Display file info and exit. Do not run file
        modules::print_file_info(
          &isolate.modules.borrow(),
          &isolate.state.dir,
          main_module,
        );
        std::process::exit(0);
      }
    }

主線程加載完執行的代碼後啓動EventLoop,去執行異步操做

isolate
      .event_loop()
      .map_err(errors::RustOrJsError::from)
      .unwrap_or_else(print_err_and_exit);

文末

~~下一節聊聊deno內部的Event Loop以及和Typescript和rust之間交互的實現~~

相關文章
相關標籤/搜索