Skip to content

Instantly share code, notes, and snippets.

@mizchi
Created January 8, 2019 19:33
Show Gist options
  • Select an option

  • Save mizchi/31e5628751330b624a0e8ada9e739b1e to your computer and use it in GitHub Desktop.

Select an option

Save mizchi/31e5628751330b624a0e8ada9e739b1e to your computer and use it in GitHub Desktop.

deno ใฎใ‚ณใƒผใƒ‰ใ‚’่ชญใ‚“ใ ใƒกใƒขใ€‚

ใใ“ใพใงๅคงใใชใƒ—ใƒญใ‚ธใ‚งใ‚ฏใƒˆใงใ‚‚ใชใ„ใฎใงใ€rust ใจ cpp ใใ“ใพใง็ฟ’็†Ÿใ—ใฆใชใใฆใ‚‚่ชญใ‚ใ‚‹ใ ใ‚ใ†ใจใ„ใ†ๆฐ—ๆŒใกใ€‚

ใƒ–ใƒผใƒˆใƒ—ใƒญใ‚ปใ‚น

https://denolib.gitbook.io/guide/installing-deno

่ตทๅ‹•ใƒ—ใƒญใ‚ปใ‚นใฃใฝใ„ใจใ“ใ‚ใ€‚

// src/main.rs
fn main() {
  // ... ใƒญใ‚ฌใƒผใ‚„ใƒ•ใƒฉใ‚ฐใฎใฎ่จญๅฎšใจใ‹
  let state = Arc::new(isolate::IsolateState::new(flags, rest_argv));
  let snapshot = snapshot::deno_snapshot();
  let isolate = isolate::Isolate::new(snapshot, state, ops::dispatch);
  tokio_util::init(|| {
    isolate
      .execute("denoMain();")
      .unwrap_or_else(print_err_and_exit);
    isolate.event_loop().unwrap_or_else(print_err_and_exit);
  });
}

isolate.event_loop ใจใ„ใ†ใฎใŒใƒกใ‚คใƒณใƒซใƒผใƒ—ใ‹ใชใ€‚

IsolateState ใจใฏใชใ‚“ใ ใ‚ใ†ใ‹ใ€‚

// src/isolate.rs
pub struct IsolateState {
  pub dir: deno_dir::DenoDir,
  pub argv: Vec<String>,
  pub permissions: DenoPermissions,
  pub flags: flags::DenoFlags,
  pub metrics: Metrics,
}

impl IsolateState {
  pub fn new(flags: flags::DenoFlags, argv_rest: Vec<String>) -> Self {
    let custom_root = env::var("DENO_DIR").map(|s| s.into()).ok();
    Self {
      dir: deno_dir::DenoDir::new(flags.reload, custom_root).unwrap(),
      argv: argv_rest,
      permissions: DenoPermissions::new(&flags),
      flags,
      metrics: Metrics::default(),
    }
  }
  //...

ใƒ‘ใƒผใƒŸใƒƒใ‚ทใƒงใƒณใ‚„่ตทๅ‹•ใƒ•ใƒฉใ‚ฐใ‚’ๆธกใ—ใฆใ„ใ‚‹ใ€‚

snapshot ใจใฏ

// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
use libdeno::deno_buf;

pub fn deno_snapshot() -> deno_buf {
  #[cfg(not(feature = "check-only"))]
  let data =
    include_bytes!(concat!(env!("GN_OUT_DIR"), "/gen/snapshot_deno.bin"));
  // The snapshot blob is not available when the Rust Language Server runs
  // 'cargo check'.
  #[cfg(feature = "check-only")]
  let data = vec![];

  unsafe { deno_buf::from_raw_parts(data.as_ptr(), data.len()) }
}

deno_bufใ€ ใŸใถใ‚“ใƒฉใƒณใ‚ฟใ‚คใƒ ไธŠใฎใ‚คใƒณใƒกใƒขใƒช็Šถๆ…‹ใ ใ‚ใ†ใ‹ใ€‚

// src/libdeno.rs

/// If "alloc_ptr" is not null, this type represents a buffer which is created
/// in C side, and then passed to Rust side by `deno_recv_cb`. Finally it should
/// be moved back to C side by `deno_respond`. If it is not passed to
/// `deno_respond` in the end, it will be leaked.
///
/// If "alloc_ptr" is null, this type represents a borrowed slice.
#[repr(C)]
pub struct deno_buf {
  alloc_ptr: *const u8,
  alloc_len: usize,
  data_ptr: *const u8,
  data_len: usize,
}

ใƒกใƒขใƒช็ขบไฟ็Šถๆ…‹ใ‚’ๆŒใฃใฆใ‚‹ใฃใฝใ„ใ€‚

ใงใ€ใ“ใฎ state ใจ snapshot ใ‚’ไฝฟใฃใฆใ€isolate ใ‚คใƒณใ‚นใ‚ฟใƒณใ‚นใ‚’็”Ÿๆˆใ™ใ‚‹ใ€‚

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

    Self {
      libdeno_isolate,
      dispatch,
      rx,
      tx,
      ntasks: Cell::new(0),
      timeout_due: Cell::new(None),
      state,
    }
  }

็ฌฌไธ‰ๅผ•ๆ•ฐใฎ dispatch ใฃใฆใชใ‚“ใ ใ€ใจๆ€ใฃใŸใ‚‰ใ‚ณใƒกใƒณใƒˆใซ่‰ฒใ€…ๆ›ธใ„ใฆใ‚ใ‚‹ใ€‚

// src/ops.rs

/// Processes raw messages from JavaScript.
/// This functions invoked every time libdeno.send() is called.
/// control corresponds to the first argument of libdeno.send().
/// data corresponds to the second argument of libdeno.send().
pub fn dispatch(
  isolate: &Isolate,
  control: libdeno::deno_buf,
  data: libdeno::deno_buf,
) -> (bool, Box<Op>) {
  let base = msg::get_root_as_base(&control);
  let is_sync = base.sync();
  let inner_type = base.inner_type();
  let cmd_id = base.cmd_id();

  let op: Box<Op> = if inner_type == msg::Any::SetTimeout {
    // SetTimeout is an exceptional op: the global timeout field is part of the
    // Isolate state (not the IsolateState state) and it must be updated on the
    // main thread.
    assert_eq!(is_sync, true);
    op_set_timeout(isolate, &base, data)
  } else {
    // Handle regular ops.
    let op_creator: OpCreator = match inner_type {
      msg::Any::Accept => op_accept,
      msg::Any::Chdir => op_chdir,
      msg::Any::Chmod => op_chmod,
      msg::Any::Close => op_close,
      msg::Any::CodeCache => op_code_cache,

js ใ‹ใ‚‰ใฎๆฅใ‚‹่ซธใ€…ใ‚’ Rust ใงๆŒใ„ใฆใ‚‹้ƒจๅˆ†ใฃใฝใ่ฆ‹ใˆใ‚‹ใ€‚

ใงใ€ libdeno::deno_init() ใŒๅ‘ผใฐใ‚Œใฆ่ตทๅ‹•ใ—ใฆใ‚‹ใ‚ใ‘ใงโ€ฆ

// src/libdeno.rs
extern "C" {
  pub fn deno_init();
  pub fn deno_v8_version() -> *const c_char;
  pub fn deno_set_v8_flags(argc: *mut c_int, argv: *mut *mut c_char);

C ใƒใ‚คใƒณใƒ‡ใ‚ฃใƒณใ‚ฐใ€‚ cpp ๅดใซใ‚‚ๅŒใ˜ใƒกใ‚ฝใƒƒใƒ‰ๅฎš็พฉใŒใ‚ใ‚‹ใฏใšใชใฎใง grep ใ™ใ‚‹ใจใ€ libdeno/deno.h ใจ libdeno/api.cc ใŒๅผ•ใฃใ‹ใ‹ใ‚‹ใ€‚

deno_init(); ใŒไฝ•ใ‚’ใ—ใฆใ„ใ‚‹ใ‹ใจใ„ใ†ใจใ€‚

// libdeno/api.cc
void deno_init() {
  // v8::V8::InitializeICUDefaultLocation(argv[0]);
  // v8::V8::InitializeExternalStartupData(argv[0]);
  auto* p = v8::platform::CreateDefaultPlatform();
  v8::V8::InitializePlatform(p);
  v8::V8::Initialize();
}

ใชใ‚‹ใปใฉใ€ใ“ใ“ใง V8 ใŒ่ตทๅ‹•ใ™ใ‚‹ใ‚ใ‘ใ‹ใ€‚ใ‚ใจใฏ IsolateState ใฎใ‚ชใƒ—ใ‚ทใƒงใƒณใ‚’ๆธกใ—ใŸใ‚Š ่‰ฒใ€…ใ—ใฆใ„ใ‚‹ใ€‚

ๆฌกใฏ src/main.rs ใฎใ“ใ“ใ‚’่ฟฝใฃใฆใฟใ‚‹ใ€‚

  tokio_util::init(|| {
    isolate
      .execute("denoMain();")
      .unwrap_or_else(print_err_and_exit);
    isolate.event_loop().unwrap_or_else(print_err_and_exit);
  });

tokioใ€ ๆญฃ็›ดใกใ‚ƒใ‚“ใจ็†่งฃใ—ใฆใชใ„ใ‚“ใ ใ‘ใฉโ€ฆๅ…ˆใซ isolate.execute("denoMain();"); ใ‚’่ฟฝใ†ใ€‚JS ๅฎŸ่กŒใ—ใฆใใ†ใ€‚

ใจใ€ใ“ใ“ใงไปŠใพใงใฎๆตใ‚Œใ‚’่ฆ็ด„ใ—ใŸใ‚ˆใ†ใชใƒ†ใ‚นใƒˆใ‚ณใƒผใƒ‰ใ‚’็™บ่ฆ‹ใ—ใŸใ€‚

// src/isolate.rs
  fn test_dispatch_sync() {
    let argv = vec![String::from("./deno"), String::from("hello.js")];
    let (flags, rest_argv, _) = flags::set_flags(argv).unwrap();

    let state = Arc::new(IsolateState::new(flags, rest_argv));
    let snapshot = libdeno::deno_buf::empty();
    let isolate = Isolate::new(snapshot, state, dispatch_sync);
    tokio_util::init(|| {
      isolate
        .execute(
          r#"
          const m = new Uint8Array([4, 5, 6]);
          let n = libdeno.send(m);
          if (!(n.byteLength === 3 &&
                n[0] === 1 &&
                n[1] === 2 &&
                n[2] === 3)) {
            throw Error("assert error");
          }
        "#,
        ).expect("execute error");
      isolate.event_loop().ok();
    });
  }

ใงใ€ใ“ใ‚Œใ‚’่ธใพใˆใŸไธŠใง execute ใฎๅฎŸ่ฃ…ใ‚’่ฆ‹ใ‚‹ใ€‚

// src/isolate.rs

  /// Same as execute2() but the filename defaults to "<anonymous>".
  pub fn execute(&self, js_source: &str) -> Result<(), JSError> {
    self.execute2("<anonymous>", js_source)
  }

  /// Executes the provided JavaScript source code. The js_filename argument is
  /// provided only for debugging purposes.
  pub fn execute2(
    &self,
    js_filename: &str,
    js_source: &str,
  ) -> Result<(), JSError> {
    let filename = CString::new(js_filename).unwrap();
    let source = CString::new(js_source).unwrap();
    let r = unsafe {
      libdeno::deno_execute(
        self.libdeno_isolate,
        self.as_raw_ptr(),
        filename.as_ptr(),
        source.as_ptr(),
      )
    };
    if r == 0 {
      let js_error = self.last_exception().unwrap();
      return Err(js_error);
    }
    Ok(())
  }

CString ใฃใฆ C ใซใ‚ใŸใ™ FFI ๅ‘ผใถใจใใซใ‚ˆใ่ฆ‹ใ‚‹ใ‚„ใคใ ใ€‚ ๅฎŸ่ณช libdeno::deno_execute() ใธใฎใƒ•ใ‚กใ‚ตใƒผใƒ‰ใซใชใฃใฆใ„ใ‚‹ใ€‚

ใ“ใฎ C++ ๅดใฎๅฎŸ่ฃ…ใ‚’่ฆ‹ใ‚‹ใจโ€ฆ

// libdeno/api.cc
int deno_execute(Deno* d_, void* user_data, const char* js_filename,
                 const char* js_source) {
  auto* d = unwrap(d_);
  deno::UserDataScope user_data_scope(d, user_data);
  auto* isolate = d->isolate_;
  v8::Locker locker(isolate);
  v8::Isolate::Scope isolate_scope(isolate);
  v8::HandleScope handle_scope(isolate);
  auto context = d->context_.Get(d->isolate_);
  CHECK(!context.IsEmpty());
  return deno::Execute(context, js_filename, js_source) ? 1 : 0;
}

v8::Locker ใŒใชใ‚“ใชใฎใ‹ใ‚ˆใใ‚ใ‹ใ‚‰ใชใ„ใŒใ€ๅๅ‰ใจไฝฟใ‚ใ‚Œๆ–นใฟใ‚‹ใจใ€ๅฎŸ่กŒใ‚ณใƒณใƒ†ใ‚ญใ‚นใƒˆ ใฎๆŽ’ไป–ๅˆถๅพกใจใ‹ใใ‚“ใชๆ„Ÿใ˜ใ ใ‚ใ†ใ‹ใ€‚

่‰ฒใ€…้ฃ›ใฐใ—ใฆใ€ return deno::Execute ใŒ v8 ใฎๅ‘ผใณๅ‡บใ™ๅฎŸไฝ“ใ‚’ๆŒใฃใฆใใ†ใ€‚่ฟฝใฃใฆใฟ ใ‚‹ใ€‚

// libdeno/binding.cc

bool Execute(v8::Local<v8::Context> context, const char* js_filename,
             const char* js_source) {
  auto* isolate = context->GetIsolate();
  v8::Isolate::Scope isolate_scope(isolate);
  v8::HandleScope handle_scope(isolate);
  v8::Context::Scope context_scope(context);

  auto source = v8_str(js_source, true);
  auto name = v8_str(js_filename, true);

  v8::TryCatch try_catch(isolate);

  v8::ScriptOrigin origin(name);

  auto script = v8::Script::Compile(context, source, &origin);

  if (script.IsEmpty()) {
    DCHECK(try_catch.HasCaught());
    HandleException(context, try_catch.Exception());
    return false;
  }

  auto result = script.ToLocalChecked()->Run(context);

  if (result.IsEmpty()) {
    DCHECK(try_catch.HasCaught());
    HandleException(context, try_catch.Exception());
    return false;
  }

  return true;
}

v8 ใฎๅ‘ผใณๅ‡บใ—่‡ชไฝ“ใฏใ€ๅ˜ใซใ“ใฎ 2 ่กŒใฃใฝใ่ฆ‹ใˆใ‚‹ใ€‚

auto script = v8::Script::Compile(context, source, &origin);
// ...
auto result = script.ToLocalChecked()->Run(context);

ใจใ„ใ†ใ‚ใ‘ใงใ€JS ใฎ denoMain(); ใ‚’ๅ‘ผใ‚“ใงใ‚‹ใฎใฏใ‚ใ‹ใฃใŸใ€‚ใŒใ€ใใ‚‚ใใ‚‚ใ“ใ‚Œใฏใฉใ† ๅฎš็พฉใ•ใ‚ŒใŸใฎใ‹ใ€‚ใŸใถใ‚“ใฉใ“ใ‹ใงใƒ—ใƒฌใƒญใƒผใƒ‰ใ•ใ‚ŒใŸใ‚“ใ ใ‚ใ†ใŒโ€ฆ

grep ใ™ใ‚‹ใจใ“ใ“ใฃใฝใ„ใ€‚

// js/main.ts

export default function denoMain() {
  libdeno.recv(handleAsyncMsgFromRust);

  // First we send an empty "Start" message to let the privileged side know we
  // are ready. The response should be a "StartRes" message containing the CLI
  // args and other info.
  const startResMsg = sendStart();
  // ...

ใ“ใ‚ŒใŒใฉใ†ๆ‰ฑใ‚ใ‚Œใฆใ„ใ‚‹ใ‹ใ€‚

BUILD.gn ใซ main.js ใซใƒ“ใƒซใƒ‰ใ—ใฆใใ†ใชใ‚ฟใ‚นใ‚ฏใ‚’่ฆ‹ใคใ‘ใŸใ€‚


run_node("bundle") {
  out_dir = "$target_gen_dir/bundle/"
  outputs = [
    out_dir + "main.js",
    out_dir + "main.js.map",
  ]
  depfile = out_dir + "main.d"
  deps = [
    ":deno_runtime_declaration",
    ":msg_ts",
  ]
  args = [
    rebase_path("third_party/node_modules/rollup/bin/rollup", root_build_dir),
    "-c",
    rebase_path("rollup.config.js", root_build_dir),
    "-i",
    rebase_path("js/main.ts", root_build_dir),
    "-o",
    rebase_path(out_dir + "main.js", root_build_dir),
    "--sourcemapFile",
    rebase_path("."),
    "--silent",
  ]
}

ใชใ‚‹ใปใฉใ€ใคใพใ‚Š rollup ใง js ใซใƒ“ใƒซใƒ‰ใ—ใฆใ„ใ‚‹ใ€‚ ่ฟฝใฃใฆใ„ใใจ target/debug/gen/bundle/main.js ใซ sourcemap ใจใจใ‚‚ใซๅ‡บๅŠ›ใ•ใ‚Œใฆใ„ใŸใ€‚(ๆœฌ็•ชใƒ“ใƒซใƒ‰ใ ใจ release)

ใ“ใ“ใงใ€ใ“ใฎ main.js ใ‚’ใฉใ“ใ‹ใง่ฉ•ไพกใ—ใฆใ‚‹ใฏใšใ ใ€ใจๆ€ใฃใฆ่ฟฝใฃใŸใŒใ€main.js ใง grep ใ—ใฆใ‚‚ใ“ใ“ใใ‚‰ใ„ใ—ใ‹ใฟใคใ‹ใ‚‰ใชใ„

// src/js_errors.rs
fn parse_map_string(
  script_name: &str,
  getter: &SourceMapGetter,
) -> Option<SourceMap> {
  match script_name {
    // The bundle does not get built for 'cargo check', so we don't embed the
    // bundle source map.
    #[cfg(not(feature = "check-only"))]
    "gen/bundle/main.js" => {
      let s =
        include_str!(concat!(env!("GN_OUT_DIR"), "/gen/bundle/main.js.map"));
      SourceMap::from_json(s)
    }
    _ => match getter.get_source_map(script_name) {
      None => None,
      Some(raw_source_map) => SourceMap::from_json(&raw_source_map),
    },
  }
}

ใ“ใ‚Œใฏใ‚จใƒฉใƒผใŒ่ตทใใŸใจใใซ ใใฎใ‚จใƒฉใƒผใฎๅ‡บๆœฌใŒใ“ใฎ main.js ใ‹ใฉใ†ใ‹ใ‚’ๅˆคๅฎšใ—ใฆใ„ใ‚‹ ใ ใ‘ใงใ€่ชญใฟ่พผใ‚“ใงใ‚‹็ฎ‡ๆ‰€ใงใฏใชใ„ใ€‚

่ฟฝใฃใŸใŒใ‚ใ‹ใ‚‰ใชใ‹ใฃใŸใฎใง้ฃ›ใฐใ™ใ€‚ใชใ‚“ใ‹็Ÿฅใ‚‰ใ‚“ใŒ่ชญใฟ่พผใพใ‚Œใฆใ‚‹ใ‚“ใ ใ‚ใ†ใจใ„ใ†้›‘ใช ็†่งฃใง้€ƒใ’ใ‚‹ใ€‚

ใพใ‚ใชใ‚“ใ ใ‹ใ‚“ใ ใงใ€ใ“ใ“ใพใงๆฅใŸใฎใงใ€ src/main.rs ใงใ€ใ‚คใƒ™ใƒณใƒˆใƒซใƒผใƒ—ใ‚’้–‹ๅง‹ใ— ใ€ setTimeout ใ‚„ Promise ใฎใƒžใ‚คใ‚ฏใƒญใ‚ฟใ‚นใ‚ฏใ‚ญใƒฅใƒผใŒๅ‹•ใๅ‡บใ™ใ‚“ใงใ—ใ‚‡ใ†ใ€‚ใŸใถใ‚“ใ€‚

// src/main.rs
isolate.event_loop().unwrap_or_else(print_err_and_exit);
// src/isolate.rs
  pub fn event_loop(&self) -> Result<(), JSError> {
    // Main thread event loop.
    while !self.is_idle() {
      match recv_deadline(&self.rx, self.get_timeout_due()) {
        Ok((req_id, buf)) => self.complete_op(req_id, buf),
        Err(mpsc::RecvTimeoutError::Timeout) => self.timeout(),
        Err(e) => panic!("recv_deadline() failed: {:?}", e),
      }
      self.check_promise_errors();
      if let Some(err) = self.last_exception() {
        return Err(err);
      }
    }
    // Check on done
    self.check_promise_errors();
    if let Some(err) = self.last_exception() {
      return Err(err);
    }
    Ok(())
  }

ใ‚ขใ‚คใƒ‰ใƒซ็Šถๆ…‹ใงใชใ‘ใ‚Œใฐใ€ใƒกใ‚คใƒณใƒซใƒผใƒ—ๅ›žใ—ใฆ promise ใ‚จใƒฉใƒผใจใ‹ๅŽ้›†ใ—ใฆใ‚ใฃใŸใ‚‰ใ‚จ ใƒฉใƒผใ‚’ๅใใ€‚ recv_deadline ใจใ„ใ†ใฎใŒ Timeout ใชใฉใ‚’ๆ‰ฑใฃใฆใ„ใ‚‹๏ผŸใฎใ‹ใชใ€‚JS ใฃใฆ 60 fps ใงๅˆถๅพกใ•ใ‚Œใฆใ‚‹ใจใ„ใ†็†่งฃใ ใฃใŸใ‚“ใ ใ‘ใฉใ€ใ“ใ‚Œใฉใ“ใ‹ใง sleep ใ™ใ‚‹ใ‚“ใ ใ‚ใ†ใ‹ใ€‚

// src/isolate.rs

fn recv_deadline<T>(
  rx: &mpsc::Receiver<T>,
  maybe_due: Option<Instant>,
) -> Result<T, mpsc::RecvTimeoutError> {
  match maybe_due {
    None => rx.recv().map_err(|e| e.into()),
    Some(due) => {
      // Subtracting two Instants causes a panic if the resulting duration
      // would become negative. Avoid this.
      let now = Instant::now();
      let timeout = if due > now {
        due - now
      } else {
        Duration::new(0, 0)
      };
      // TODO: use recv_deadline() instead of recv_timeout() when this
      // feature becomes stable/available.
      rx.recv_timeout(timeout)
    }
  }
}

ใ‚ˆใใ‚ใ‹ใ‚‰ใชใ„ใฎใง้ฃ›ใฐใ™

denoMain()

่ฆใฏใ“ใ“ใพใงใ‚„ใฃใฆ v8 ใŒ่ตทๅ‹•ใ—ใฆใ„ใ‚‹ใ“ใจใŒใ‚ใ‹ใฃใŸใ€‚ใ˜ใ‚ƒใ‚ใฉใ†ใ„ใ†ใ‚นใ‚ฏใƒชใƒ—ใƒˆใŒ่ตท ๅ‹•ใ—ใฆใ„ใ‚‹ใฎใ‹ใ€‚

// js/main.ts

export default function denoMain() {
  libdeno.recv(handleAsyncMsgFromRust);

  // First we send an empty "Start" message to let the privileged side know we
  // are ready. The response should be a "StartRes" message containing the CLI
  // args and other info.
  const startResMsg = sendStart();

  setLogDebug(startResMsg.debugFlag());

  // handle `--types`
  if (startResMsg.typesFlag()) {
    const defaultLibFileName = compiler.getDefaultLibFileName();
    const defaultLibModule = compiler.resolveModule(defaultLibFileName, "");
    console.log(defaultLibModule.sourceCode);
    os.exit(0);
  }

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

  os.setPid(startResMsg.pid());

  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);
  const inputFn = args[0];

  compiler.recompile = startResMsg.recompileFlag();

  if (inputFn) {
    compiler.run(inputFn, `${cwd}/`);
  } else {
    replLoop();
  }
}

ใ•ใฃใใ libdeno.recv(handleAsyncMsgFromRust) ใจใ„ใ†ใ‚ใ‹ใ‚Šใ‚„ใ™ใ„ใƒกใ‚ฝใƒƒใƒ‰ใŒๅ‡บใฆ ใใŸใ€‚

// src/libdeno.ts

interface Libdeno {
  recv(cb: MessageCallback): void;

  send(control: ArrayBufferView, data?: ArrayBufferView): null | Uint8Array;

  print(x: string, isErr?: boolean): void;

  shared: ArrayBuffer;

  builtinModules: { [s: string]: object };

  setGlobalErrorHandler: (
    handler: (
      message: string,
      source: string,
      line: number,
      col: number,
      error: Error
    ) => void
  ) => void;

  setPromiseRejectHandler: (
    handler: (
      error: Error | string,
      event: PromiseRejectEvent,
      /* tslint:disable-next-line:no-any */
      promise: Promise<any>
    ) => void
  ) => void;

  setPromiseErrorExaminer: (handler: () => boolean) => void;
}

const window = globalEval("this");
export const libdeno = window.libdeno as Libdeno;

ใ“ใ“ใซ libdeno.recv ใฎๅฎŸ่ฃ…ใฏใชใใ€ใ‚ญใƒฃใ‚นใƒˆใ•ใ‚Œใฆใ„ใ‚‹ใ€‚ใƒˆใƒƒใƒ—ใƒฌใƒ™ใƒซใงใฎ globalEval("this") ใฏใชใ‚“ใ‹ไธ็ฉใชๆฐ—้…ใŒใ™ใ‚‹ใŒใ€่ฆใฏใฉใ“ใ‹ใง this ใจใ„ใ†ใ‚ซใ‚น ใ‚ฟใƒžใ‚คใ‚บใ•ใ‚ŒใŸ global ใ‚ณใƒณใƒ†ใ‚ญใ‚นใƒˆใซๅฏพใ—ใ€ recv ใจใ„ใ†ใƒกใ‚ฝใƒƒใƒ‰ใ‚’ๅฎš็พฉใ—ใฆใ„ใ‚‹ใ‚„ ใคใŒใ„ใ‚‹ใ€ใ‚ˆใ†ใชๆฐ—ใŒใ™ใ‚‹ใ€‚

ใŸใถใ‚“ใ“ใ‚Œใฏ v8 ใƒใ‚คใƒณใƒ‡ใ‚ฃใƒณใ‚ฐใงใฏใชใ„ใ‹ใ€‚ใคใพใ‚Š libdeno ใฏ rust ๅดใ‹ใ‚‰ๅ‘ผใถ็ตŒ่ทฏ ใจใ€JS ใ‹ใ‚‰ๅ‘ผใถ็ตŒ่ทฏใŒใ‚ใ‚Šใใ†ใ€‚js ใ‹ใ‚‰ๅ‘ผใ‚“ใ ใ‚‚ใฎใŒใ€ ops::dispatch ใชใฉใงใƒใƒณ ใƒ‰ใƒซใ•ใ‚Œใ‚‹ใ‚“ใ ใ‚ใ†ใ€‚

src/main.ts ใซๆˆปใ‚‹

// First we send an empty "Start" message to let the privileged side know we
// are ready. The response should be a "StartRes" message containing the CLI
// args and other info.
const startResMsg = sendStart();

sendStart ใฎๅฎŸ่ฃ…ใฏใ“ใ‚Œ

function sendStart(): msg.StartRes {
  const builder = flatbuffers.createBuilder();
  msg.Start.startStart(builder);
  const startOffset = msg.Start.endStart(builder);
  const baseRes = sendSync(builder, msg.Any.Start, startOffset);
  assert(baseRes != null);
  assert(msg.Any.StartRes === baseRes!.innerType());
  const startRes = new msg.StartRes();
  assert(baseRes!.inner(startRes) != null);
  return startRes;
}

ใ‚ˆใใ‚ใ‹ใ‚‰ใชใ„ใŒ flatbuffers ใฎ RPC ไฝฟใ†ๆบ–ๅ‚™ใ—ใฆใใ†ใ€‚

ใ‚ใจใ€ๆฐ—ใซใชใ‚‹ใฎใฏใ€ compiler ใจใ„ใ†ใ‚„ใคใ ใ‚ใ†ใ‹ใ€‚ไฝ•ใ‚’ใ—ใฆใ„ใ‚‹ใ‚“ใ ใ‚ใ†

const compiler = DenoCompiler.instance();
// src/compiler.ts

/** A singleton class that combines the TypeScript Language Service host API
 * with Deno specific APIs to provide an interface for compiling and running
 * TypeScript and JavaScript modules.
 */
export class DenoCompiler
  implements ts.LanguageServiceHost, ts.FormatDiagnosticsHost {
  // Modules are usually referenced by their ModuleSpecifier and ContainingFile,
  // and keeping a map of the resolved module file name allows more efficient
  // future resolution
  private readonly _fileNamesMap = new Map<
    ContainingFile,
    Map<ModuleSpecifier, ModuleFileName>
  >();

  // ...
  compile(moduleMetaData: ModuleMetaData): OutputCode {
    const recompile = !!this.recompile;
    if (!recompile && moduleMetaData.outputCode) {
      return moduleMetaData.outputCode;
    }
    const { fileName, sourceCode, mediaType, moduleId } = moduleMetaData;
    console.warn("Compiling", moduleId);

moduleMetaData ใจใ„ใ†ใฎใ‚’้ฃŸใฃใฆใ‚ณใƒผใƒ‰ใ‚’็”Ÿๆˆใ—ใฆใ„ใ‚‹ใ€‚

/** A simple object structure for caching resolved modules and their contents.
 *
 * Named `ModuleMetaData` to clarify it is just a representation of meta data of
 * the module, not the actual module instance.
 */
export class ModuleMetaData implements ts.IScriptSnapshot {
  public deps?: ModuleFileName[];
  public exports = {};
  public factory?: AmdFactory;
  public gatheringDeps = false;
  public hasRun = false;
  public scriptVersion = "";

typescript ใฎๅ†…้ƒจใ‚ชใƒ–ใ‚ธใ‚งใ‚ฏใƒˆใ‚’็ถ™ๆ‰ฟใ—ใฆใ‚‹ใฃใฝใ„ใ€‚ใชใ‚“ใ‹่‰ฒใ€…ใ‚„ใฃใฆใ‚ณใƒณใƒ‘ใ‚คใƒซใ—ใฆ ใ‚ญใƒฃใƒƒใ‚ทใƒฅใ‚’ไฝœใฃใฆใ‚‹ใ€‚็•ฅใ€‚

็–ฒใ‚ŒใฆใใŸใฎใงใ€ใ“ใ“ใง็ต‚ไบ†ใ€‚ๆฆ‚็•ฅใฏใ‚ใ‹ใฃใŸๆฐ—ใŒใ™ใ‚‹ใ€‚

@kt3k
Copy link

kt3k commented Jan 9, 2019

่‰ฒใ€…ๅ‹‰ๅผทใซใชใ‚Šใพใ—ใŸใƒผ ๐Ÿ‘๐Ÿ‘

ไปฅไธ‹ใ€ๆ€ใฃใŸใ“ใจใ‚ณใƒกใƒณใƒˆใ—ใพใ™ใ€‚

snapshot ใจใฏ deno_bufใ€ ใŸใถใ‚“ใƒฉใƒณใ‚ฟใ‚คใƒ ไธŠใฎใ‚คใƒณใƒกใƒขใƒช็Šถๆ…‹ใ ใ‚ใ†ใ‹ใ€‚

ใงใ€ใ‚ใฃใฆใ‚‹ใจๆ€ใ„ใพใ™ใ€‚ใ“ใ‚ŒใŒใ‚ใ‚‹ใŠใ‹ใ’ใง ts ใ‚’ไธธใ”ใจๅซใ‚“ใงใ„ใ‚‹ main.js ใฎ (v8 ็š„ใช) compile ใจใ‹ JIT ๆœ€้ฉๅŒ–ใŒใ‚ใ‚‹็จ‹ๅบฆ็ต‚ใ‚ใฃใŸ็Šถๆ…‹ใง่ตทๅ‹•ใงใใ‚‹ใฎใง้€Ÿใ„ใจใ„ใ†่ฉฑใ ใจๆ€ใฃใฆใ„ใพใ™ใ€‚
( snapshot ใง startup time ใ‚’ๆ—ฉใใ™ใ‚‹่ฉฑใŒๅ‡บใฆใใ‚‹ issue: denoland/deno#45

JS ใฃใฆ 60 fps ใงๅˆถๅพกใ•ใ‚Œใฆใ‚‹ใจใ„ใ†็†่งฃใ ใฃใŸใ‚“ใ ใ‘ใฉ

chrome ใงไธ‹ใฎใ‚ˆใ†ใชใ‚ณใƒผใƒ‰ใ‚’ๅฎŸ่กŒใ™ใ‚‹ใจ 60 ไปฅไธŠใฎๆ•ฐๅญ—ใŒๅ‡บใฆใใ‚‹ใฎใงใ€60fps ใจใ„ใ†็ธ›ใ‚ŠใฏๆŒใฃใฆใ„ใชใ„ใ‚ˆใ†ใชๆฐ—ใŒใ—ใพใ™ใŒใ€ใฉใ†ใงใ—ใ‚‡ใ†?

i = 0; setInterval(() => { console.log(i); i = 0; }, 1000); a = () => {i += 1; setTimeout(a);}; a();

ใŸใถใ‚“ใ“ใ‚Œใฏ v8 ใƒใ‚คใƒณใƒ‡ใ‚ฃใƒณใ‚ฐใงใฏใชใ„ใ‹ใ€‚ใคใพใ‚Š libdeno ใฏ rust ๅดใ‹ใ‚‰ๅ‘ผใถ็ตŒ่ทฏ ใจใ€JS ใ‹ใ‚‰ๅ‘ผใถ็ตŒ่ทฏใŒใ‚ใ‚Šใใ†ใ€‚

ใงใ™ใญใ€libdeno ใฏ v8 ใƒใ‚คใƒณใƒ‡ใ‚ฃใƒณใ‚ฐใงใ‚ใฃใฆใ‚‹ใจๆ€ใ„ใพใ™ใ€‚ใ‚ฝใƒผใ‚นใฏ /libdeno ไปฅไธ‹ใซ้š”้›ขใ•ใ‚Œใฆใ„ใพใ™

typescript ใฎๅ†…้ƒจใ‚ชใƒ–ใ‚ธใ‚งใ‚ฏใƒˆใ‚’็ถ™ๆ‰ฟใ—ใฆใ‚‹ใฃใฝใ„ใ€‚ใชใ‚“ใ‹่‰ฒใ€…ใ‚„ใฃใฆใ‚ณใƒณใƒ‘ใ‚คใƒซใ—ใฆ ใ‚ญใƒฃใƒƒใ‚ทใƒฅใ‚’ไฝœใฃใฆใ‚‹ใ€‚็•ฅใ€‚

ใงใ™ใญใ€DenoCompiler = ใ€Œtypescript ใฎ API ใ‚’ใƒฉใƒƒใƒ—ใ—ใฆใ€deno ็š„ใช ts ใ‚ณใƒณใƒ‘ใ‚คใƒซใ™ใ‚‹ใ‚„ใคใ€ ใ ใจๆ€ใ„ใพใ™ใ€‚ใ“ใฎ่พบใฏ @ry ใ‚ˆใ‚Šใ‚‚ @kitsonk ใจใ„ใ†ไบบใŒไธญๅฟƒใซใชใฃใฆ้–‹็™บใ—ใฆใ‚‹ใฝใ„ใงใ™ใ€‚

ใ‚คใƒ™ใƒณใƒˆใƒซใƒผใƒ—ใซใคใ„ใฆใฎไฝ™่ซ‡ใงใ™ใŒใ€ไปฅๅ‰ event_loop ใ‚’ JS (ใจ libdeno) ๅดใซ็งปๅ‹•ใ™ใ‚‹(ใ—ใŸใ„)ใจใ„ใ† PR ใŒใ‚ใ‚Šใพใ—ใŸใŒใ€ๅ…จใฆใฎใƒ™ใƒณใƒใƒžใƒผใ‚ฏใŒใ‚ˆใใชใ‚‹ใ‚ใ‘ใงใฏใชใ„ใ‚‰ใ—ใใ€ไธ€ๆ—ฆใƒšใƒณใƒ‡ใ‚ฃใƒณใ‚ฐใซใชใฃใŸใฟใŸใ„ใงใ™ใ€‚
denoland/deno#1003

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment