JavaScriptのプロパティアクセスエラーを見つける
JavaScriptのプロパティアクセスでよくある間違いを見つける方法。
― 1 分で読む
JavaScriptでは、オブジェクトのプロパティはいつでも変わることができる。この柔軟性がプログラマーに間違いを起こさせやすく、長い間気づかれないエラーを引き起こすことがある。よくある問題の一つは、存在しないプロパティを使おうとすること。JavaScriptは存在しないプロパティにアクセスしてもエラーを投げないから、こういう問題は見つけて修正するのが難しいんだ。
この記事では、こうしたプロパティアクセスエラーを見つける方法について話すよ。この方法は、大きく分けて二つの部分から成り立ってる。ひとつは、実際のJavaScriptコードでプロパティがどう使われているかのデータを集めること、もうひとつはそのデータを分析して潜在的なエラーを特定すること。
プロパティアクセスエラーが起こる理由
JavaScriptはプログラマーがオブジェクトを自由に変更できる。このため、プログラムのライフサイクルのどの時点でもプロパティが追加、変更、削除できちゃう。この機能は便利だけど、間違いも生むんだ。たとえば、ちょっとしたタイプミスで存在しないプロパティを参照しちゃうこともある。
開発者がコードを書くとき、すべてのケースをテストするわけじゃないから、こういう間違いを見逃すことが簡単にある。JavaScriptは存在しないプロパティにアクセスしようとしたときに、クラッシュする代わりに「undefined」を返すから、開発者は間違いを知るのが難しいんだ。
プロパティアクセスエラーの例
いくつかの例で、こうしたエラーがどう発生するかを示すね:
- 間違ったプロパティ名: 開発者が - object.size()と書いちゃって、正しいのは- object.lengthだった場合、プログラムはクラッシュしないけど、期待通りには動かない。
- 動的プロパティ: 一部のライブラリでは、ユーザーがオブジェクトにプロパティを動的に追加できる。開発者は特定のプロパティが常に存在すると思い込んでしまうことがある。 
- 条件付きアクセス: 時々、開発者はプロパティが利用可能かどうかを確認してからアクセスする。でも、これを間違えると、やっぱりエラーが出ちゃう可能性がある。 
こういう問題は、ソースに戻ってトレースするのが難しいバグにつながることがある。
エラー発見の二段階アプローチ
提案されたプロパティアクセスエラーを特定する方法は、データ収集と統計分析という二つの主要なフェーズから構成されてる。
フェーズ1:データ収集
最初のフェーズでは、いろんなJavaScriptプロジェクトでプロパティがどうアクセスされているかのデータを集める。リアルなコードサンプルを広範囲にわたって見て、たくさんのコードベースを分析することで、正しく使われているプロパティと、あまり使われないか、潜在的に間違っているプロパティがわかる。
「アクセスパターン」を集めて、オブジェクトのプロパティがどう通常アクセスされるかを示す。たとえば、fs.readFileがfs.sizeよりよく使われているのを見れば、前者が正しい可能性が高く、後者は間違っているかもしれないって推測できる。
フェーズ2:統計分析
二つ目のフェーズでは、集めたデータを分析する。特定のプロパティがオブジェクトに対してどれくらい使われているかを見る。予想よりも使われていないプロパティがあれば、それはエラーのサインかもしれない。
分析は以下のいくつかのステップから成り立ってる:
- 分類: プロパティアクセスをその一般性に基づいて分類する。特定のオブジェクトと一緒にたくさん使われているプロパティは正しい可能性が高いし、あまり使われていないと問題の兆候かもしれない。 
- フィルタリング: あまり使われていないアクセスが必ずしもバグを意味するわけじゃない。特定の文脈で有効なプロパティもあるから、偽アラームをフィルタリングするために追加のチェックを行う。 
- ヒューリスティックス: アクセスが間違っている可能性があるかどうかを判断するための経験則を使う。たとえば、プロパティの存在が条件チェックで確認されていれば、それをバグとしてフラグする必要はない。 
この二つのフェーズを組み合わせることで、プロパティアクセスエラーを検出するための堅牢なシステムを作ることを目指してる。
アプローチの効果
私たちのアプローチがどれくらい効果的かを見るために、多くのJavaScriptプロジェクトのデータを使って実験を行った。これらの実験では、どれだけのプロパティアクセスエラーを見つけられるか、またそれらをどれだけ正確に特定できるかを定量化することを目指した。
統計的測定
私たちは効果を測るために一般的な統計用語を使った:
- 精度(Precision): これはフラグされたプロパティアクセスのうち、実際にエラーだったものがどれくらいかを教えてくれる。
- 再現率(Recall): これは私たちの分析で実際のエラーをどれくらい見つけられたかを示す。
高い精度は、フラグした潜在的なエラーが実際にエラーであることが多いことを意味する。高い再現率は、実際のバグの大部分をキャッチできていることを示してる。
実験結果
実験では、私たちの方法が多くのプロパティアクセスエラーを効果的に見つけられることがわかった。精度スコアは80%以上で、ほとんどのフラグが実際に間違いだった。再現率スコアも印象的で、コード内のエラーのかなりの部分をキャッチできたことを示してる。
現在のツールの限界
多くの開発者は、VSCodeのような統合開発環境(IDE)に頼ってコードを書くのを助けてもらってる。こうしたツールは、開発者がタイピングミスを避けるためにコード補完の提案を提供してくれる。しかし、私たちの調査結果は、これらの提案が常に信頼できるわけではないことを示している。
VSCodeの提案
VSCodeがプロパティを提案する能力をテストしたところ、間違ったプロパティを提案することはなかったけど、正しい提案をすることがほとんどなかった。評価した100のプロパティアクセスのうち、VSCodeが正しいプロパティを提案したのは18回だけだった。これは低い再現率を意味していて、開発者はこれらの提案の精度だけに頼ることはできない。
結論
JavaScriptのプロパティアクセスエラーは見つけるのが難しいけど、私たちの二段階アプローチはこれらの間違いを特定するための有用なツールを提供してる。広範なデータを集めて徹底した統計分析を適用することで、どこで開発者が間違っているかを特定できる。
VSCodeのような現行のツールは役に立つけど、全ての潜在的なプロパティアクセスエラーをキャッチするには不十分だから、私たちの方法は既存のソリューションに貴重な補完を提供して、開発者がよりクリーンで信頼性の高いコードを書くのを助けるんだ。
タイトル: A statistical approach for finding property-access errors
概要: We study the problem of finding incorrect property accesses in JavaScript where objects do not have a fixed layout, and properties (including methods) can be added, overwritten, and deleted freely throughout the lifetime of an object. Since referencing a non-existent property is not an error in JavaScript, accidental accesses to non-existent properties (caused, perhaps, by a typo or by a misunderstanding of API documentation) can go undetected without thorough testing, and may manifest far from the source of the problem. We propose a two-phase approach for detecting property access errors based on the observation that, in practice, most property accesses will be correct. First a large number of property access patterns is collected from an extensive corpus of real-world JavaScript code, and a statistical analysis is performed to identify anomalous usage patterns. Specific instances of these patterns may not be bugs (due, e.g., dynamic type checks), so a local data-flow analysis filters out instances of anomalous property accesses that are safe and leaves only those likely to be actual bugs. We experimentally validate our approach, showing that on a set of 100 concrete instances of anomalous property accesses, the approach achieves a precision of 82% with a recall of 90%, making it suitable for practical use. We also conducted an experiment to determine how effective the popular VSCode code completion feature is at suggesting object properties, and found that, while it never suggested an incorrect property (precision of 100%), it failed to suggest the correct property in 62 out of 80 cases (recall of 22.5%). This shows that developers cannot rely on VSCode's code completion alone to ensure that all property accesses are valid.
著者: Ellen Arteca, Max Schäfer, Frank Tip
最終更新: 2023-06-14 00:00:00
言語: English
ソースURL: https://arxiv.org/abs/2306.08741
ソースPDF: https://arxiv.org/pdf/2306.08741
ライセンス: https://creativecommons.org/licenses/by/4.0/
変更点: この要約はAIの助けを借りて作成されており、不正確な場合があります。正確な情報については、ここにリンクされている元のソース文書を参照してください。
オープンアクセスの相互運用性を利用させていただいた arxiv に感謝します。
参照リンク
- https://github.com/isaacs/node-graceful-fs/blob/95ec3a283dffe0402282ea92f2356d3c166f6392/test/stats-uid-gid.js
- https://github.com/matthewp/fs/blob/869bb9509549a74b39d8d9efcbee025de479ec72/lib/core.js#L114
- https://github.com/codeclimate/javascript-test-reporter/blob/1ad7ea8ad010d59997fdfdb1e25bac9a78c38fda/formatter.js#L80
- https://github.com/worldline/3loc/blob/f24612bee2d3832aace40fe150c794334924c61d/test/actions/listen.js#L276
- https://www.chaijs.com/api/bdd/
- https://github.com/nodulusteam/-nodulus-codulus/blob/master/routes/codulus.js#L50
- https://dl.acm.org/ccs.cfm
- https://doi.org/10.5281/zenodo.7570291
- https://doi.org/10.5281/zenodo.6525941
- https://github.com/Nextdoor/ndscheduler/blob/d31016aaca480e38a69d75a66a9978a937c6a0b0/ndscheduler/static/js/models/job.js
- https://github.com/nodulusteam/-nodulus-codulus/blob/master/routes/codulus.js
- https://github.com/the-road-to-graphql/fullstack-apollo-express-mongodb-boilerplate/blob/7e5e9088ebe1193ec6df20975100d39bdd798666/src/tests/user.spec.js
- https://github.com/bcho04/galeforce/blob/547d70301e34ff52dfd9add6b6fdafeb1ec35db7/src/galeforce/actions/action.ts
- https://github.com/ariya/phantomjs/blob/0a0b0facb16acfbabb7804822ecaf4f4b9dce3d2/test/module/fs/paths.js
- https://github.com/Himself65/vscode-hentai/blob/b1056f7a4c3993fa87f069c329b322fc3ec4bf4d/src/hentai.ts
- https://github.com/HouseOps/HouseOps/blob/95a6b6fe74c101e93c30d9cb87856f3e0482e5c7/app/containers/ProcessesList.js
- https://github.com/fueledbydreams/zeit-codeclimate-integration/blob/master/src/views/overview.js
- https://github.com/banfstory/React-Forum-Frontend/blob/main/react_frontend/src/components/Forum.jsx
- https://github.com/fueledbydreams/zeit-codeclimate-integration/blob/master/src/index.js
- https://github.com/dawnwords/github-pr-auto-merge/blob/master/lib/auto-merger.js
- https://github.com/rajatonit/weatherApp/blob/master/search/search.js
- https://github.com/jeffreymeng/montavistamun/blob/master/src/components/registration/FormUpload.tsx
- https://github.com/rsksmart/rif-data-vault/blob/develop/modules/ipfs-cpinner-client/src/index.ts
- https://github.com/zix99/simple-auth/blob/master/tests/api/v1/oauth2.js
- https://github.com/tobilg/facebook-events-by-location-core/blob/master/lib/eventSearch.js
- https://github.com/Azure/blackbelt-aks-hackfest/blob/master/app/web/src/components/Leaderboard.vue
- https://github.com/statping/statping/blob/dev/frontend/src/forms/Notifier.vue
- https://github.com/rsksmart/rif-identity.js/blob/develop/packages/rif-id-core/src/operations/authentication.ts
- https://github.com/tiangolo/full-stack-flask-couchdb/blob/master/
- https://github.com/olegp/common-node/blob/master/lib/fs-base.js
- https://github.com/browserify/browserify/blob/0ec6e80ec48b67513718a392a6d09bd5569967d4/index.js
- https://github.com/mozilla/openbadges-bakery/blob/master/lib/stream-type-check.js
- https://github.com/opbeat/opbeat-node/blob/09c99083d067fe8084a311f69a9655c1e850dbe2/lib/instrumentation/shimmer.js
- https://github.com/Alhadis/Utils/blob/master/test/shell.mjs
- https://github.com/webtorrent/create-torrent/blob/master/index.js
- https://github.com/MadLittleMods/postcss-css-variables/blob/master/playground/jspm_packages/system.src.js
- https://github.com/jh12z/codegen/blob/master/lib/codegen.js
- https://github.com/node-modules/compressing/blob/321d9d577b97f6a96fbf6d9c6a46655349a790d5/lib/tar/stream.js
- https://github.com/mozilla/openbadges-bakery/blob/master/lib/svg.js
- https://github.com/highcharts/node-export-server/blob/master/tests/http/side-by-side.js
- https://github.com/mholt/PapaParse/blob/master/papaparse.js
- https://github.com/subtleGradient/node-junitreport/blob/master/parse.js
- https://github.com/willnewii/qiniuClient/blob/master/src/main/util/qetag.js
- https://github.com/TooTallNate/node-get-uri/blob/c9ced38f67b1911b890117696215103a54d001a9/src/file.ts
- https://github.com/regl-project/regl/blob/104e83225336666d29f5d2a6534e837d3b6f5f04/bin/build-gallery.js
- https://github.com/krakenjs/spud/blob/851858ad1824b1907777580eef61fdcdb3d700d2/lib/transcoder.js
- https://github.com/Kong/unirest-nodejs/blob/a06ba4e6c58fea028d377c170cb4cbf7cf3c6049/index.js
- https://github.com/anupamroy/Engage-App-Codebase-Clean/blob/master/users/engagecalci/fullcalendar.js
- https://github.com/tidysource/tidypath/blob/c5dee736d966aaa00c2885f33d4ce98e900315c7/index.js
- https://github.com/jonschlinkert/add-filename-increment/blob/master/test/linux.js
- https://github.com/uber-archive/npm-shrinkwrap/blob/master/analyze-dependency.js
- https://github.com/jonschlinkert/strip-filename-increment/blob/master/index.js
- https://github.com/unifiedjs/unified-engine/blob/91ba25d0355fdb8a57e90be8141d04f42295eec3/lib/finder.js
- https://github.com/bigeasy/timezone/blob/bffc3c12823d61d342492a61951a3e8229e08e18/util/localizer.js
- https://github.com/async-library/react-async/blob/af52ec3491b9648ead9ec1dce66a75a2bf44cbc2/packages/react-async/src/propTypes.ts
- https://github.com/prose/prose/blob/370fe86574684dfcb72b27ffee0ca0ff787114dd/app/models/file.js
- https://github.com/dimagi/Vellum/blob/23e50bd2110a8457705c4f75aadbb4e1e13ef283/src/core.js
- https://github.com/strongloop/strong-log-transformer/blob/master/test/common.js
- https://github.com/pull-stream/stream-to-pull-stream/blob/master/index.js
- https://github.com/kevinchen8621/temple/blob/master/lib/qiniu/upload.js
- https://github.com/jonschlinkert/copy/blob/master/lib/dest.js
- https://github.com/import-js/eslint-plugin-import/blob/add650a1aeb118a4334bf2e9c56699ba1a836565/resolvers/webpack/index.js
- https://github.com/joao-neves95/merger-js/blob/master/modules/fileDownloader.js
- https://github.com/plopjs/node-plop/blob/master/src/actions/_common-action-utils.js
- https://github.com/simov/request-compose/blob/master/request/length.js
- https://github.com/WenlinMao/Furnitrade/blob/master/source/react/src/components/uploadImg/UploadImg.js
- https://github.com/node-modules/compressing/blob/master/lib/tar/uncompress_stream.js
- https://github.com/glslify/glslify-deps/blob/master/index.js
- https://github.com/albertosantini/node-rio/blob/master/lib/main.js
- https://github.com/nasa/cmr-stac/blob/master/search/lib/cmr.js
- https://github.com/mjackson/bufferedstream/blob/master/index.js
- https://github.com/Unitech/gridcontrol/blob/master/grid-cli/lib/common.js
- https://github.com/jonschlinkert/parse-filepath/blob/master/test.js
- https://github.com/Kong/unirest-nodejs/blob/master/index.js
- https://github.com/ymyang/fdfs/blob/master/lib/storage.js
- https://github.com/broccolijs/broccoli-persistent-filter/blob/master/src/dependencies.ts
- https://github.com/jonschlinkert/parse-filepath/blob/master/index.js
- https://github.com/07akioni/cxsjsw-hw6/blob/master/fe/src/components/Order.js
- https://github.com/EllarDevelopment/csv-manipulation-tool/blob/master/index.js
- https://github.com/nodeWechat/wechat4u/blob/master/src/core.js
- https://github.com/hyperledger-archives/composer-sample-applications/blob/master/packages/letters-of-credit/src/components/Pages/AlicePage/AlicePage.js
- https://github.com/joyent/node-http-signature/blob/master/test/examples.test.js
- https://github.com/jakutis/httpinvoke/blob/master/Gruntfile.js
- https://github.com/pgte/pipeline/blob/master/pipeline.js
- https://github.com/nrstott/bogart/blob/master/lib/bogart.js
- https://github.com/apigee-127/swagger-test-templates/blob/24a109434cefd020d45f37d9a05058bc564f2a52/test/robust/compare/supertest/assert/base-path-test.js
- https://github.com/justadudewhohacks/face-recognition.js/blob/409ffd82793784bbb633621aa463df2a62936407/tests/FaceRecognizer/FaceRecognizerTest.js
- https://github.com/steos/reactcards/blob/e8948586a0e6a864eb5dea0f324b1c3ed630c585/test/cards.test.js
- https://github.com/paypal/legalize.js/blob/3389d5b1a1e81087888b82766e7aab19bdba45c0/test/reporting.test.js
- https://github.com/worldline/3loc/blob/f24612bee2d3832aace40fe150c794334924c61d/test/actions/listen.js
- https://github.com/lucybot/jammin/blob/9dd13ae23608681385801eb835f31aef499d6839/test/petstore.js
- https://github.com/mklabs/node-build-script/blob/c5cbcabd829971139f9da4e1334a782950d931a1/test/helpers/index.js
- https://github.com/fshost/node-dir/blob/a57c3b1b571dd91f464ae398090ba40f64ba38a2/test/test.js
- https://github.com/codeclimate/javascript-test-reporter/blob/1ad7ea8ad010d59997fdfdb1e25bac9a78c38fda/formatter.js
- https://github.com/cibernox/ember-power-datepicker/blob/da580474a2c449b715444934ddb626b7c07f46a7/tests/dummy/app/controllers/public-pages/index.js
- https://github.com/tarunyadav1/Doist-React-App-Using-firebase/blob/411010c0aac52502daa5bd5a63a1601fad0f29b2/src/components/AddTask.js
- https://github.com/socketstream/socketstream/blob/790bfbd20cda239e20dba6ed7f05425438c030e8/lib/client/bundler/proto.js
- https://github.com/matthewp/fs/blob/869bb9509549a74b39d8d9efcbee025de479ec72/lib/core.js
- https://github.com/Giveth/milestonetracker-ui/blob/5b0d40900610a799e77ba76d2fa609289fad2c60/dapp/js/components/DeploymentResults.jsx
- https://github.com/webcaetano/shuffle-seed/blob/2cc884d70ef08cce809e9d166a1c8081cf8fec3e/test.js
- https://github.com/js-kyle/mincer/blob/c7791df2605fbca423fd24684d5f421e0da8e8fc/lib/mincer/assets/bundled.js
- https://github.com/levelgraph/levelgraph-jsonld/blob/075945bd39fd30decba182dc7b089f1b3f04d1c4/test/get_spec.js
- https://github.com/rsksmart/rif-data-vault/blob/develop/modules/ipfs-cpinner-client/src/index.ts#L37
- https://github.com/jeffreymeng/montavistamun/blob/master/src/components/registration/FormUpload.tsx#L53
- https://github.com/rsksmart/rif-identity.js/blob/develop/packages/rif-id-core/src/operations/authentication.ts#L36
- https://github.com/async-library/react-async/blob/af52ec3491b9648ead9ec1dce66a75a2bf44cbc2/packages/react-async/src/propTypes.ts#L51
- https://github.com/Giveth/milestonetracker-ui
- https://github.com/StateOfJS/StateOfJS-2019
- https://github.com/YaleDHLab/intertext
- https://github.com/chifei/spring-demo-project
- https://github.com/async-library/react-async
- https://github.com/TooTallNate/node-get-uri
- https://github.com/SUI-Components/sui
- https://github.com/krakenjs/spud
- https://github.com/lakenen/node-box-view
- https://github.com/airtap/airtap
- https://github.com/jeffpar/pcjs
- https://github.com/yeoman/stringify-object
- https://github.com/mklabs/node-build-script
- https://github.com/node-gfx/node-canvas-prebuilt
- https://github.com/marko-js-archive/async-writer
- https://github.com/mbostock/gistup
- https://github.com/marionebl/share-cli
- https://github.com/minio/minio-js
- https://github.com/mieweb/wikiGDrive
- https://github.com/meyda/meyda
- https://github.com/electrodejs/deprecated-generator-electrode
- https://github.com/endpoints/endpoints
- https://github.com/cooliejs/coolie-cli
- https://github.com/codeclimate/javascript-test-reporter
- https://github.com/architectcodes/6-to-library
- https://github.com/ariya/phantomjs
- https://github.com/bigeasy/timezone
- https://github.com/wp-pot/wp-pot
- https://github.com/vanwagonet/modules
- https://github.com/tessel/t1-cli
- https://github.com/substance/dar-server
- https://github.com/pulumi/docs
- https://github.com/opbeat/opbeat-node
- https://github.com/npm/cacache
- https://github.com/smhg/gettext-parser
- https://github.com/Alhadis/Utils
- https://github.com/atlassianlabs/ac-koa-hipchat
- https://github.com/auth0/node-jws
- https://github.com/webtorrent/create-torrent
- https://github.com/albertosantini/node-rio
- https://github.com/nodemailer/nodemailer
- https://github.com/facebookarchive/jsgrep
- https://github.com/christkv/node-git
- https://github.com/cj/node-sd-api
- https://github.com/dmester/jdenticon
- https://github.com/mayflower/PHProjekt
- https://github.com/devongovett/node-wkhtmltopdf
- https://github.com/tarranjones/macOS-defaults
- https://github.com/Kong/unirest-nodejs
- https://github.com/twbs/icons