Moment: React-ネイティブリリースビルド、ロケール変更時のディープクラッシュ

作成日 2019年10月10日  ·  4コメント  ·  ソース: moment/moment

react-native 0.59.10を使用し、momentJSのロケールを設定すると、リリースビルドのみで非常に残酷なクラッシュが発生しました。 このクラッシュは、デバッガーを接続した状態では再現できませんでした。 このクラッシュは、iOSとAndroidの両方で発生しました。 すべての瞬間の使用法をラップするtry-catchステートメントはクラッシュをキャッチしませんでした!

再現するには

  1. アプリケーション固有のロジックを介して、試したいロケール/言語を収集します。 例: ["fr-CA", "en-US", "fr", "en"]
  2. 配列セッターを使用するのではなく、一度に1つずつこれらをループして、他のインストルメンテーションを呼び出し、スローされたJS例外をキャッチして、次の候補を試すことができるようにします。
  3. try-catchブロック内でmoment.locale(localeCandidate)呼び出しても、アプリケーションはこの行でクラッシュします⁇

これは起動時のクラッシュでしたが、リリースビルドの場合のみです!! これにより、有用なエラーメッセージ/ログを抽出するのが非常に難しくなりました。

Bugsnag統合とシステムコンソールログを介して次のエラーメッセージが表示されました

  • iOS: Exception in HostFunction: Error loading module0from RAM Bundle: unspecified iostream_category error
  • Android: Exception in HostFunction: Module not found: 0
  • 1日後、iOSとAndroidでもRequiring unknown module "./locale/en-us".が表示されましたが、奇妙なことに、このエラーはタイムリーに処理されていませんでした。 おそらくreact-native / bugsnagの問題です。
  1. 最終的なトレースで、クラッシュの原因となった単一のポイントが見つかりました。
    https://github.com/moment/moment/blob/96d0d6791ab495859d09a868803d31a55c917de1/moment.js#L1852 -L1853
    これはここから来ると私は信じています: https

回避策:これらの2行をコメントアウトすると、クラッシュが停止しました。

期待される動作

  • Momentはクラッシュして動作するべきではありませんが、特にリリースビルドのみでは動作しません。
  • Momentの例外はキャッチ可能である必要があります(catchステートメントでクラッシュを防ぐことができたはずです)。 - (リリースでrequire()をいじる場合、react-nativeの問題になる可能性があります)

スマートフォン(以下の情報を入力してください):

  • デバイス:iPhone X、iPhone 11 Pro、Samsung(?-正確に何をキャプチャしなかった)、Google Pixel 3(react-native 0.59.10)
  • OS:iOSとAndroid
  • iOS JavaScriptコア(下記のiOSバージョン用)、およびjsc-android(+ intl)245459.0.0
  • バージョン:iOS12.4およびiOS13.1、iOS 13.1.2、およびiOS 13.2(ベータ版?)、Android28など。

モーメント固有の環境

  • 太平洋時間、そしておそらくロンドン時間
  • コードのバグは、過去2週間(2019-10-09)、1日中一貫して発生しました。
  • 使用中の他のライブラリ:moment-timezoneおよびmoment-with-locales、TypeScript、react-native 0.59.10、Apollo-Clientなど

ご使用の環境で次のコードを実行し、出力を含めてください。

        console.log([
            new Date().toString(),
            new Date().toLocaleString(),
            new Date().getTimezoneOffset(),
            navigator && navigator.userAgent, // react-native might not have a navigator
            moment.version,
        ]);

出力:

[
  "Wed Oct 09 2019 18:52:16 GMT-0700 (PDT)",
  "09/10/2019 à 18:52:16", // This particular device is configured as fr-FR
  420,
  null,
  "2.24.0"
]

追加のコンテキスト

関連チケット

  • #5214-ここではコンテキストが少なく、異なるAPIを使用し、異なるエラーメッセージが表示されるため、異なります。 これはリリースビルドでのみ発生するため、同様です。これは、react-nativeも示唆していますが、ios-simulator / iosでのみ再現されています。
  • #3872
  • #2979

最も参考になるコメント

参照されている行は、実行時にモジュールを「自動的に」要求しようとしていますが、ロケールimport "moment/locale/frロケールを読み込むことができることが示されています。 インポートする必要があるファイルを「認識」するためにreact-nativeパッケージマネージャーが必要なため、パッケージャーがバンドルする必要のあるすべてのファイルを「認識」できるように、そのスタイルのインポートを試みました。

最終的に、インポート行は次のようになりました。

import moment from "moment";
import "moment/min/locales"; // Import all moment-locales -- it's just 400kb
import "moment-timezone";

require()の正確な実装は、使用しているランタイムによって注入されます。これは、デバッグビルドとリリースビルドの間で動作が大幅に異なることは間違いありません。

react-nativeには、リリースモードのJavaScriptバンドルには、オールインワンファイル、オールインセパレートファイル、RAMバンドルなど、いくつかの異なるフレーバーがあります。 これらはそれぞれ、requireの動作方法も変更します。 デバッグrequire()は、ローカルhttpサーバーで実行されているMetroBundlerに接続します。 これはおそらくwebpack / jspm / otherデバッグサーバーと非常に似ているため、エイリアシングrequireがその環境で問題を引き起こさないのはおそらくそのためです。

全てのコメント4件

参照されている行は、実行時にモジュールを「自動的に」要求しようとしていますが、ロケールimport "moment/locale/frロケールを読み込むことができることが示されています。 インポートする必要があるファイルを「認識」するためにreact-nativeパッケージマネージャーが必要なため、パッケージャーがバンドルする必要のあるすべてのファイルを「認識」できるように、そのスタイルのインポートを試みました。

最終的に、インポート行は次のようになりました。

import moment from "moment";
import "moment/min/locales"; // Import all moment-locales -- it's just 400kb
import "moment-timezone";

require()の正確な実装は、使用しているランタイムによって注入されます。これは、デバッグビルドとリリースビルドの間で動作が大幅に異なることは間違いありません。

react-nativeには、リリースモードのJavaScriptバンドルには、オールインワンファイル、オールインセパレートファイル、RAMバンドルなど、いくつかの異なるフレーバーがあります。 これらはそれぞれ、requireの動作方法も変更します。 デバッグrequire()は、ローカルhttpサーバーで実行されているMetroBundlerに接続します。 これはおそらくwebpack / jspm / otherデバッグサーバーと非常に似ているため、エイリアシングrequireがその環境で問題を引き起こさないのはおそらくそのためです。

私の修正提案:

A. aliasedRequire完全に削除してください。そうしない場合は、それ以上の操作を行う必要があります。それに加えて、インストール手順を微調整しますか?
B. react-nativeとbrowserを検出し( navigatorはreact-nativeでは使用できませんが、ここには他の手法があります)、どの状況にあるかによって動作が異なりますか? 例えば。 react-native && DEVの場合、ロケールが理論的にサポートされているが、まだrequiredになっていない場合はconsole.errorを出力します(+ドキュメントを更新)。
C. aliasedRequireをその関数のローカル変数から「セミグローバル」に移動します。 moment.aliasedRequire 、そうすれば、no-op / do-nothing関数を挿入して、 aliasedRequireがreact-nativeを激しくクラッシュさせないようにすることができます。

メンテナが私に実装してほしいオプションを教えてくれれば、これらのオプションのいずれかを実装できれば幸いです。提案B / Cは、どの実装を受け入れる傾向があるかを正確に改善するのに役立ちます。

@ marwahaha-モーメントのプロセスがわからない。 私の修正提案について意見がありますか? 寄稿者/メンテナがどのルートを受け入れることができるかについてアドバイスをもらったら、PRを実装できれば幸いです。

このページは役に立ちましたか?
0 / 5 - 0 評価