アドベントペアプロ #5 denoland/deno

ファシリテーター: @ggtmtmgg
セクリタリ: @ggtmtmgg
参加者: @binaryta, @Haga

情報の共通化

unit_testsのエラーについて

$ ./tools/unit_tests.py target/debug/deno
...
HttpOther: an error occurred trying to connect: Connection refused (os error 61)

というエラーがでる問題と戦ってみた。

os error 61 が何か分からなかった。
サンドボックスのせいかな?--allow-netが上手く渡せてない?
もしくはlocalhost:4545をたてれてない?

replでconst, letが動かない問題について

replへの入力をreplLoop()が監視している
evaluate(code)で一行ずつ読み込む
evaluateのなかで呼んでるeval.call(window, code)に渡してるスコープが間違っていそう
cost window = globalEval("this");
globalEvalとevalがある。
単なるevalはそのスコープで動く。呼ばれたfunciton内のスコープで動く。
globalEvalはグローバルなスコープで動くっぽい

つまりreplの入力一行ごとに関数のブロックスコープが作られそのなかでconstで変数を定義してしまっている。

だから二行で書くと動かない、一行でやれば動く

> const a = 1;
undefined
> a
ReferenceError: a is not defined
> const a = 1; console.log(a);
1
undefined
> var c = 1; console.log(c);
1
undefined
> 

方針の決定

denoのts側のソースコードの問題に着手するというコンセプトで行く

Error("ErrTooLarge") を DenoError(TooLarge)に書き換える作業が良さそう

DenoErrorの使う

gen/以下からErrorKindをインポートしている。
genはたぶんGNでビルドされたやつ。
genはgRPCみたいに資源共有するやつが出力されてる。
msg.fbsが型共有の仕組みの実装部分。そこに記述してビルドすればgen/以下に出力されるはず。
なので、今回はmsg.fbにErrorKindにTooLargeを追加してビルドする。
その後、

new DenoError(
  ErrorKind.TooLarge,
  ""
)

に書き換える

エラーメッセージを考える

grow()はバッファの容量を増やす関数 今回のエラーはgrow()でこれ以上バッファを増やせないよってときに吐くエラー

      throw new DenoError(                                             
        ErrorKind.TooLarge,
        "The buffer can't grow because it becomes too large"           
      );

src/msg.fbs

enum ErrorKind: byte {
  // ...
  TooLarge,
}

テストがないのでテストを書く

やること

  • Bufferのwrite, readのどちらかもしくは両方で_grow()を使ってる部分を確認する
  • どれくらいの大きさでTooLargeになるのか把握する
  • TooLargeな状況で_grow()が呼ばれる状態をjs/buffer_test.tsで再現する
  • それをassertする
$ git remote add binaryta git@github.com:Tnarita0000/deno.git
$ git fetch binaryta js/modify-buffer-large-error
$ git checkout js/modify-buffer-large-error

c > MAXSIZE - c - nのときにTooLargeになる
cの実態に関して
c = this.capasitythis.capasity = this.buf.buffer.byteLength
this.bufはUint8Array型

nの実態に関して
write(p: Uint8Array)で渡される。
n = p.buffer.byteLength
書き込むデータの大きさ。

bufferTestGrow()がそのまま使えそう。

test(async function bufferTestGrow() {
  const tmp = new Uint8Array(72);
  for (let startLen of [0, 100, 1000, 10000, 100000]) {
    const xBytes = repeat("x", startLen);
    for (let growLen of [0, 100, 1000, 10000, 100000]) {
      const buf = new Buffer(xBytes.buffer as ArrayBuffer);
      // If we read, this affects buf.off, which is good to test.
      const { nread, eof } = await buf.read(tmp);
      buf.grow(growLen);
      const yBytes = repeat("y", growLen);
      await buf.write(yBytes);
      // Check that buffer has correct data.
      assertEqual(
        buf.bytes().subarray(0, startLen - nread),
        xBytes.subarray(nread)
      );
      assertEqual(
        buf.bytes().subarray(startLen - nread, startLen - nread + growLen),
        yBytes
      );
    }
  }
});

Number.MAX_VALUEをgrowLenに渡してみる。

$ deno
> 
1.7976931348623157e+308

こんな感じでtestを書いてみる

 
test(async function bufferTooLargeByteWrites() {
  init();
   const tmp = new Uint8Array(72);
  const growLen = Number.MAX_VALUE;
  const xBytes = repeat("x", 0);
  const buf = new Buffer(xBytes.buffer as ArrayBuffer);
  // If we read, this affects buf.off, which is good to test.
  const { nread, eof } = await buf.read(tmp);
   try {
    buf.grow(growLen);
  } catch (e) {
    assertEqual(stringify(e).split("\n")[0], "TooLarge: The buffer can't grow because it becomes too large");
  }
});

そしてPRを送って解散

https://github.com/denoland/deno/pull/1292