V8 使用時 alert に引数で文字列以外を渡すとエラーになる

  1. V8 対応ということで少し試してみたのですが、V8 JavaScript エンジンを使用するマクロで alert に引数で文字列以外を渡すとエラーになるケースがあるようです。

    エラーメッセージは文字化けしています。以下テストケースです。

    #language = "V8"
    
    const arg = {
      // 従来のエンジンとEdge: "A,B"
      // V8: エラー "��ނ���v���܂���B (0x80020005)"
      '配列': ['A', 'B'],
    
      // 従来のエンジンとEdge: "[object Object]"
      // V8: "A" エラーじゃないけどおかしい?
      'オブジェクト': {a:'A', b:'B'},
    
      // 従来のエンジン: "[object Object]"
      // Edge: "[object Map]"
      // V8: エラー "��ނ���v���܂���B (0x80020005)"
      'Map': new Map(),
    
      // 従来のエンジン: "[object Object]"
      // Edge: "[object Set]"
      // V8: エラー "��ނ���v���܂���B (0x80020005)"
      'Set': new Set(),
    
      // 従来のエンジンとEdge: "function() {}"
      // V8: エラー "��O���������܂����B (0x80020009)"
      '関数': function() {},
    
      // 従来のエンジン: ""
      // Edge: "function alert() { [native code] }"
      // V8: エラー "�p�����[�^�[���Ԉ���Ă��܂��B (0x80070057)"
      'window.alert': window.alert,
    };
    
    const popupMenu = createPopupMenu();
    Object.keys(arg).forEach(function(key, index) {
      popupMenu.add(key, ++index);
    });
    
    const id = popupMenu.track(mePosMouse);
    if (id !== 0) {
      alert(arg[popupMenu.getText(id)]);
    }

    【環境】
    ・Windows 10 22H2 19045.2604
    ・WebView2 Runtime 110.0.1587.57
    ・Mery Version 3.4.3 (64 ビット)

     |  ucky  |  返信
  2. ご報告ありがとうございます。

    現象、確認しました。0x80020005 というのは「型が一致しません。」というエラーのようですね。V8 エンジンだと型のチェックが厳しくなってるのでその影響かも?

    あと、エラーメッセージが文字化けしているのは私も開発段階で遭遇しましたが、今のところ解決策は見つかっていないので仕様上の制限事項ということでご容赦ください。

    以下、調査結果です。

    例えば、sample.js というファイル名で、

    var a;
    a = new Array("A", "B");
    alert(a[0]); // OK
    alert(a[1]); // OK
    alert(a);    // OK

    を用意します。

    これを Mery で実行すると OK ですが、V8 エンジンに書き換えると…

    #language = "V8"
    
    var a;
    a = new Array("A", "B");
    alert(a[0]); // OK
    alert(a[1]); // OK
    alert(a);    // FAIL

    alert(a); のところでエラーになりますね。

    ここの文字型に変換してくれるかどうかのさじ加減はスクリプトエンジンにおまかせしてるので、例えば、sample.vbs というファイル名で…

    Dim a
    a = Array("A", "B")
    alert a(0) ' OK
    alert a(1) ' OK
    alert a    ' FAIL

    を実行すると VBScript エンジンで同じ動作が実行されますが、alert a のところでエラーになります。

    V8 エンジン対応にあたって、Mery 側で alert メソッドなどの仕様変更は発生していないので、V8 エンジン側の仕様による可能性が高いと思いますが、Edge や Chrome だと alert で自動的に適当な文字列に変換されてるじゃないか!…と。

    他のエディターでも確認してみたのですが、EmEditor さんだと Mery と同様…

    #language = "V8"
    
    var a;
    a = new Array("A", "B");
    alert(a[0]); // OK
    alert(a[1]); // OK
    alert(a);    // FAIL

    で同じ「��ނ���v���܂���B (0x80020005)」エラーが発生しました。

    でも、秀丸エディタさんだと…

    jsmode "WebView2";
    js {
      var a;
      a = new Array("A", "B");
      message(a[0]); // OK
      message(a[1]); // OK
      message(a);    // OK
    }

    と、秀丸エディタさんのマクロで V8 エンジンを使って実行してみてもエラーは発生しませんでした。さすが秀丸さん。

    そういうわけで技術的には可能だということは分かりました。

    しかしながらググりまくっても情報が見つからなくて、今のところはお手上げ状態です。何かヒントになりそうなことや、アイデアなどがございましたらご協力いただけると嬉しいです。

     |  Kuro  |  返信
  3. ご確認・調査ありがとうございます。

    > 現象、確認しました。0x80020005 というのは「型が一致しません。」というエラーのようですね。V8 エンジンだと型のチェックが厳しくなってるのでその影響かも?

    そうかもしれませんね。そもそも alert に文字列以外を渡すのはイレギュラーなことかもしれませんし。

    > あと、エラーメッセージが文字化けしているのは私も開発段階で遭遇しましたが、今のところ解決策は見つかっていないので仕様上の制限事項ということでご容赦ください。

    仕様上の制限事項ということで承知しました。ただ、この文字化けエラーのときは、エラー箇所が「行: 1」「文字: 1」になっていて、「編集しますか?」で「はい」を選んでも何も起こらないのが少し気になりますね。

    プログラミング関係ではないので参考になるかわかりませんが、以下に少し気になったことを書いてみます。

    もう確認されているかもしれませんが、Edge などで alert で表示される文字列は、console.log で Mery のアウトプットバーに表示される文字列とほぼ同じ感じです。

    次のようなコードでいくつか試したところ 'window.alert' 以外は同じでした。

    #language = "V8"
    
    const arg = {
      '文字列': '文字列',
      '数値': 1,
      '真偽値': true,
      'null 値': null,
      '未定義': undefined,
      '配列': ['A', 'B'],
      'オブジェクト': {a:'A', b:'B'},
      'Map': new Map(),
      'Set': new Set(),
      '関数': function() {},
      'window.navigator': window.navigator,
      'window.alert': window.alert,
    };
    
    for (const v of Object.values(arg)) {
    	console.log(v); // Mery
    	//alert(v); // ブラウザー
    }

    あと、これは関係ないかもしれませんが、SyntaxError の時は何も言わずに終了してしまいますね。

    // 従来: "const は初期化する必要があります"
    // V8: 何も言わずに終了
    const a;

    一応英語でエラーメッセージは投げられてるみたいです。

    // 従来: "const は初期化する必要があります"
    // V8: "Missing initializer in const declaration"
    try{
      eval('const a;');
    } catch (e) {
      alert(e.message);
    }
     |  ucky  |  返信
  4. ご返信ありがとうございます。

    > ただ、この文字化けエラーのときは、エラー箇所が「行: 1」「文字: 1」になっていて、「編集しますか?」で「はい」を選んでも何も起こらないのが少し気になりますね。

    Mery ではスクリプトの実行時に、内部的に先頭の行に「定数宣言とか補助的な関数」を埋め込んで実行させており、今回のエラーはその部分 (js ファイルの外側) で発生しているため、js ファイルにジャンプできないみたいです。

    それに加えて、この文字化けエラーの実際の内容を確認してみたところ、エラーの箇所が複数に渡っており、Mery は最初の 1 箇所しかパースしないようにしていることも関係しているようです。

    > もう確認されているかもしれませんが、Edge などで alert で表示される文字列は、console.log で Mery のアウトプットバーに表示される文字列とほぼ同じ感じです。

    実は、Mery の console.log はデバッグ用に隠し機能として用意しているもので、上記で説明した先頭の行に埋め込んでるスクリプトに、補助的な関数として以下を忍ばせているだけです。(実際は改行はいれず、1 行になってますが)

    console = {
      clear: function () {
        __me.outputBar.clear();
        return;
      },
      log: function () {
        var s = "";
        for (var i = 0; i < arguments.length; i++) {
          if (i > 0) s += "	";
          s += arguments[i];
        }
        __me.outputBar.writeln(s);
        return;
      }
    };

    なので、文字型への変換は Mery 側でしているのではなく、WebView2 (Edge) 側で変換して、outputBar に出力させているため、エラーなく動作します。

    さすがにこれを alert や prompt、その他多くの文字列型を引数とするメソッドやプロパティ向けに用意するのは現実的ではなさそうですし…。

    // 従来: "const は初期化する必要があります"
    // V8: 何も言わずに終了
    const a;

    これは実際のエラーの内容を確認してみたところ、内容が空っぽでした。EmEditor さんでも同様、エラーは表示されませんでした。

    でも、秀丸エディタさんで…

    jsmode "WebView2";
    js {
      const a;
    }

    を実行すると、「SyntaxError: Missing initializer in const declaration」と、きちんとエラーが表示されるので、こちらも技術的には可能なのだと思います。

    もしかすると、エラーが文字化けしていることも関係していて、WebView2 のロケールを英語にするとエラーが拾えるようになるのかも?と思ったりもしましたが、今のところ情報は見つかっていません。

     |  Kuro  |  返信
  5. console.log はそういう仕組みだったんですね。確かに次のようにすると同じものがアラート表示されました。

    alert(String(/* 引数 */));

    SyntaxError の件、秀丸エディタではエラーが表示されるとのことで、βバージョン改版履歴を見たところ V9.18β3 で対策されたみたいですね。

    フォーラムを見ると V9.18β1 の記事で指摘があり、V9.18β3 の記事で「結局内部的にevalで囲ってしまいました」と書かれているので、秀丸エディタの方でも根本的な解決には至ってないのかもしれません。

    内部的なことはわからないので、素人考えですが

    window.eval('const a;');

    のようにするとエラーは表示されますが、何か副作用がありそうで不安ですね。(こんな単純な話ではないとおもいますが…)

    根本的な解決法が見つかればよいのですが、お役に立てそうにないです。

     |  ucky  |  返信
  6. 情報ありがとうございます。

    > フォーラムを見ると V9.18β1 の記事で指摘があり、V9.18β3 の記事で「結局内部的にevalで囲ってしまいました」と書かれているので、秀丸エディタの方でも根本的な解決には至ってないのかもしれません。

    なるほど、勉強になりました。これはめちゃくちゃ大変そうですね…。

    「evalだらけです。」とも書かれていたので、js 側ですべてのメソッドを eval で囲うなどで対策されているのだと思います。

    文字列型変換の件についても、文字列型が引数になるメソッドにすべて js 側で文字型変換の補助関数 (Mery の console.log みたいなやつ) を組み込まれているような気が…。

    Mery でも出来なくはなさそうですが、膨大な作業量と副作用の心配、それからメンテナンス性を考えるとこの方式はちょっと厳しそうです。

    > 根本的な解決法が見つかればよいのですが、お役に立てそうにないです。

    ほんと、そうですね。根本的な解決法が望ましいですが、そもそも WebView2 のスクリプトエンジンだけ拝借するというのが Microsoft からすると想定外の使い方なのかもしれません。

    V8 エンジンについては「試験的な実装」ということをアピールしつつ、水面下で研究していったほうが良さそうですね。

     |  Kuro  |  返信
  7. WebView2 Runtime が 122.0.2365.52 にアップデートされたタイミングでテストしたところエラーメッセージが文字化けしなくなってました。

    #language = "V8"
    
    const test = new Map([
      ['配列', []], // 種類が一致しません。 (0x80020005)
      ['関数', () => {}], // 例外が発生しました。 (0x80020009)
    ]);
    
    const popupMenu = createPopupMenu();
    [...test.keys()].forEach((item, index) => popupMenu.add(item, ++index));
    
    const id = popupMenu.track(mePosMouse);
    if (id !== 0) {
      alert(test.get(popupMenu.getText(id)));
    }

    頻繁にテストしてたわけではないので今回のアップデートで修正されたのかわかりませんが一応報告します。

     |  ucky  |  返信
  8. ご報告ありがとうございます。

    私の環境でも文字化けが解消されていることを確認しました。

    ひとつ前のバージョン (121.0.2277.128) に戻して試してみましたが、そちらでは文字化けが発生していました。

    今回のアップデートで修正されたようですね。

     |  Kuro  |  返信
スポンサーリンク