JavaScript - TECH PLAY - TECH PLAY

TECH PLAY

JavaScript

イベント

マガジン

技術ブログ

本記事は 2026 年 8 月 6 日に公開された Libby Clark、James Ward による “ AWS Supports Agent Plugins: An Open Standard for Portable Agent Extensions ” を翻訳したものです。 MCP サーバーやエージェントスキルを構築したことがある方なら、それをパッケージ化するには 1 つのクライアント向けに調整し、別のクライアント向けに書き直し、チームで使うツールごとにその作業を繰り返すことになるとご存知でしょう。 Agent Plugins 1.0.0 は、AI エージェントの拡張機能に共通のパッケージ形式を提供するオープンソースかつベンダーニュートラルな仕様です。拡張機能を 1 度パッケージ化すれば、Kiro、VS Code、Cursor など、この仕様を実装した任意のクライアントに配布できます。 AWS は、Cursor、Microsoft、OpenAI、Vercel と並ぶ Agent Plugins Technical Steering Committee の設立メンバーであり、エージェント間でスキルや MCP のパッケージングにポータビリティをもたらすこの標準を支持できることを嬉しく思います。すでに AWS Agent Toolkit を対応させており、 Kiro でも Agent Plugins 仕様のサポートを順次展開しています。 拡張機能とは、スキル、MCP サーバー、フック、サブエージェントのように、AI エージェントに新たな能力を与えたり、外部ツールやデータへ接続したり、エージェントに代わってタスクを自動化したりする再利用可能なコンポーネントのことです。Agent Plugins の初版では、現在最も採用が進んでいる 2 つのコンポーネントタイプ、Agent Skills と MCP サーバーを標準化しています。フックやカスタムエージェントといった追加の拡張機能タイプは、将来のバージョンに向けたロードマップに含まれています。 モデル選択が、どの AI を使うかについての柔軟性を開発者に与えるのに対して、Agent Plugins は、その選択を特定のクライアントやプロバイダーに縛られることなく、AI がどのツールを使うかについての柔軟性を与えます。 エージェント型 AI にとってオープン標準が重要な理由 これまで、エージェント拡張の管理は、パッケージマネージャー登場前のライブラリ配布や、標準的なコンテナ形式が確立する前のアプリケーション配布に少し似ていました。JavaScript が package.json という共通の形式に落ち着いたことで、npm、yarn、pnpm のいずれからも同じパッケージをインストールできるようになり、手作業でのスクリプトダウンロードやコピー&ペーストによる依存関係管理は終焉を迎えました。同様に、OCI がベンダーニュートラルな形式をコンテナイメージにもたらしたことで、1 度ビルドしたイメージが Docker、containerd、Podman、あるいは仕様に準拠した任意のランタイムで実行できるようになり、Docker 独自のイメージ仕様を置き換えました。オープン標準は、その上に築ける共通の土台を全員に提供します。 AWS は、オープンソースとオープン標準による構築こそが AI アーキテクチャの柔軟性と相互運用性を保つと考えています。オープン標準は、開発者に自らの条件でワークロードを組み合わせ、拡張し、移行する自由を与えます。私たちが Model Context Protocol (MCP)、 Agent Client Protocol (ACP)、 x402 、そして Agent Plugins のような 標準に投資している のは、モデル、ツール、クライアントに至るまで、皆さんが選んだいかなる決定も特定のベンダーに縛られないようにするためです。 Agent Plugins とは Agent Plugins は、ツール間で容易に共有・バージョン管理・インストールできるように、エージェント拡張をどうパッケージ化するかを定義します。内部的には、プラグインはディレクトリであり、その中にプラグインの識別情報とエントリーポイントを宣言する JSON マニフェストと、コンポーネントの固定配置場所を持ちます。互換性のあるクライアントは、ディレクトリ構造をスキャンして内部に何があるかを検出します。skills/ フォルダーがあればスキルを読み込み、MCP 設定があればそれらのサーバーに接続します。 フォーマットは意図的に小さく保たれています。バージョン 1.0.0 では、Agent Skills (エージェント向けの再利用可能な指示およびリソース) と MCP サーバー (外部ツールやデータへの接続) を標準化しています。クライアントがプラグインをどのようにインストール、表示、配布するかは、意図的に仕様の対象外としています。これらは、共通のフォーマットを共有しつつ、クライアントが差別化できる領域だからです。 これは正しい設計思想です。共有フォーマットは、相互運用に不可欠なコンポーネントを定義したうえで、それ以上のことには介入しないべきです。拡張機能の作者は 1 度パッケージ化するだけで、互換性のあるクライアントが同じ構造からコンポーネントを検出して読み込みます。クライアントは、名前空間で分離した拡張機能によってプロプライエタリな機能を追加する自由を保ちつつ、ポータブルなコアを煩雑にしないでいられます。 Agent Plugins はコミュニティによる取り組み Agent Plugins は、エージェント向けツールを構築するあらゆるチームが独立して直面してきた実務的な課題から生まれました。拡張機能の作者は同じコンポーネントを異なるクライアント向けに調整する冗長な作業を強いられ、開発者は互換性のないパッケージング慣習を渡り歩くことで時間を失っていました。 Vercel が仕様の初期ドラフトを公開し、その後、それぞれのやり方でこの問題を解決してきた各社のチームからなるワーキンググループを結成しました。AWS、Cursor、Microsoft、OpenAI、Vercel の代表者たちは、日々開発者が使うエージェントクライアントを構築してきた自らの経験を持ち寄って、共同で仕様を洗練させました。このグループは、1 社のプロダクトロードマップがフォーマットの方向性を左右することがないようにするガバナンス構造を確立しました。 その結果として得られたのは、拡張機能の作者とクライアント実装者の両方の視点が場に揃っていたからこそ実現できた、両者の実際のニーズを反映した仕様です。Technical Steering Committee には設立 5 社すべてからコアメンテナーが参加しており、プロジェクトのコントリビューションプロセスと技術的な意思決定は完全に公開されています。 これこそが、最良の標準が生まれるあり方です。1 社のビジョンをエコシステムに押し付けるのではなく、共有された課題を各社が共に解決していく中での収束から生まれるのです。ツール連携における MCP、可観測性における OpenTelemetry、そしてそれ以前のコンテナ標準でも、私たちは同じパターンを目の当たりにしてきました。 AWS による Agent Plugins のサポート 私たちは、開発者が AWS 上で AI エージェントを構築・拡張する 2 つの主要なオファリングである Kiro と AWS Agent Toolkit の両方で、Agent Plugins のサポートをローンチと同時に提供します。 Kiro は、開発ライフサイクル全体を通じてエージェントを開発者の意図に沿わせ続けるための spec、フック、自動テストといったステアリング機構を備えたエージェントハーネスです。今回のローンチにより、インストール可能なパッケージによって Kiro を拡張する Kiro Powers が Agent Plugins 仕様をネイティブにサポートするようになりました。開発者は、Skills と MCP サーバーをまとめてバンドルしたプラグインをインストールでき、それぞれのコンポーネントを手動で構成することなく、シームレスに動作する機能を利用できます。 AWS Agent Toolkit は、AI コーディングエージェントが AWS 上で信頼性高く構築できるようにする、公式にサポートされた MCP サーバー、スキル、エージェントプラグインのコレクションです。これらは複数のパッケージング形式で提供され、Lambda、S3、DynamoDB、CDK などのサービスをカバーする 30 以上の厳選されたスキルを、複数のプラグインとしてバンドルしています。新しい Agent Plugins 仕様をサポートすることで、異なるハーネスやコード支援ツールが同じパッケージング形式を利用できるようになり、AWS Agent Toolkit のようなエージェント拡張のパッケージをエージェントがどのように取り込むかに関するフラグメンテーションが最終的に減少します。 今後の展望 Agent Plugins 1.0.0 は出発点です。仕様は、ポータビリティの恩恵を受ける追加のコンポーネントタイプをエコシステムが見出すにつれて進化していきます。Technical Steering Committee では、フック、サブエージェント、その他の拡張機能タイプが将来のバージョンでどのように Plugins に加わりうるかについて、すでに検討が始まっています。 私たちがこの取り組みに真剣に向き合っているのは、オープン標準が開発者の生産性にもたらす効果を見てきたからです。オープン標準は、優れたツールがそれを必要とする人々の手元に届くのを妨げている摩擦を取り除きます。今日、その対象はエージェント拡張ですが、エージェント型 AI エコシステムが進化を続けるにつれて、さらに多くの標準が生まれてくると私たちは考えています。 仕様の詳細は agent-plugins.org でご確認いただけます。 GitHub リポジトリ を確認したり、Kiro で最初のプラグインをインストールしたりしてみてください。 著者について Libby Clark Libby Clark は AI に注力するプリンシパルオープンソースストラテジストです。 James Ward James Ward は AWS のプリンシパルデベロッパーアドボケイトです。James は世界を飛び回りながら、エンタープライズ開発者が信頼性の高いシステムを構築する方法を学ぶのを支援しています。現在は、Spring AI、Embabel、Strands Agents、Amazon Bedrock、MCP、A2A を用いた AI エージェントシステムを開発者が構築できるように支援することに注力しています。 翻訳は Solutions Architect の吉村が担当いたしました。
はじめに こんにちは、セーフィーに新卒で入社し、サーバーサイドエンジニアをしている古谷です。 入社後、同期の坂上さんと一緒に VApp(Vulnerable Application) という、意図的に脆弱性を仕込んだ学習用Webアプリケーションを作りました。社内のセキュリティ学習を目的に始めたプロジェクトでしたが、作ってみると 「Webフレームワークを使っていても、脆弱な実装をしないように気をつけなければならない」 という、当たり前のようで意外と実感しにくいことを実装ベースで確かめられました。 この記事では、VAppを紹介しつつ、それを通して 自分の中でセキュリティとの向き合い方がど
なぜC++/JS間のメモリ管理は難しいのか 皆様、はじめまして。グローバルプロダクト開発本部 グローバルシステム部 技術推進ブロックの村木と申します。私たちのチームでは、OpenCVによる画像処理やニューラルネットワークによる画像認識を含む大規模なC++資産を、EmscriptenでWebAssemblyにコンパイルし、ブラウザへ移植するプロジェクトに取り組んでいます。 その中で私にとって難所となったのが、メモリ管理の理解でした。Emscriptenを使用する開発では、メモリの考え方が異なる2つの言語、C++とJavaScriptをまたいだ処理を実装することがあります。C++ではオブジェクトの寿命がコンストラクタとデストラクタによるRAIIで制御されるのに対し、JavaScriptではオブジェクトの寿命をGC(ガベージコレクタ)が管理し、通常、開発者が解放を意識することはありません。 このメモリモデルの違いに加え、C++のオブジェクトをJavaScript側へ渡すとき、その寿命や所有権に関する情報は引き継がれないことを、しっかりと理解しておく必要があります。 JavaScript側から見ると、C++のオブジェクトは単なる数値(メモリ上のアドレス)に過ぎず、この「数値」には、「誰が確保したのか」「いつまで有効なのか」「誰が解放すべきか」といった所有権の情報は含まれていません。また、JSのGCはC++側のメモリ領域を認識できません。 そのため、Wasmの境界を越えるオブジェクトについては、所有者と有効期間、解放の責任を明確にしたうえで実装する必要があります。この点を理解しておくことが、Emscriptenにおける開発の重要なポイントでした。 Emscriptenが提供するembindを使えば、C++のクラスや std::vector をJavaScriptのオブジェクトに近い形で扱えます。ただし、その抽象化されたAPIの背後で、オブジェクトの受け渡しと寿命管理がどのように行われているのかを理解しておく必要があります。 本記事では、まずWasmのメモリモデルの基礎を概観し、その上でembindによる実装を詳しく解説します。 目次 なぜC++/JS間のメモリ管理は難しいのか 目次 Wasmのメモリモデル C++オブジェクトの実体 境界を越えると寿命の情報が失われる embindは何を隠し、何を隠さないのか value_objectはコピー class_はハンドル typed_memory_viewはビュー おわりに Wasmのメモリモデル WebAssembly(Wasm)のメモリモデルは非常にシンプルです。Wasmモジュールは、 Linear Memory(線形メモリ) と呼ばれる、単一の連続したバイト列を自身のメモリ空間として持ちます。この線形メモリは、JavaScript側からは WebAssembly.Memory オブジェクトの buffer として見えます(型は ArrayBuffer )。 C++コードがWasmにコンパイルされると、グローバル変数やスタック領域、そして malloc / new によって確保されるヒープ領域は、すべてこの線形メモリの中に配置されます。したがって、C++オブジェクトの「実体」とは、この線形メモリ上の特定のアドレスから始まる領域にほかなりません。JavaScriptの視点で言い換えれば、C++のオブジェクトは、巨大な ArrayBuffer (= WebAssembly.Memory が内部に保持する一本の配列)の中に、バイト列として格納されているのです。 まずはこのことを、簡単なコードで確認していきます。 C++側で「指定されたアドレスから始まるバイト列の合計値を計算する」関数を実装します。 // C++側:ptrは線形メモリ内のオフセット(アドレス) extern "C" int sum_bytes ( const uint8_t * ptr, int len) { int total = 0 ; for ( int i = 0 ; i < len; i++) total += ptr[i]; return total; } JavaScript側では、Emscriptenが提供する _malloc で線形メモリ上の領域を確保し、 HEAPU8 を通じて値を書き込みます。このとき、関数に渡す ptr は単なる数値(アドレス)です。 const bytes = new Uint8Array ([ 10 , 20 , 30 ]) ; // 1. 線形メモリ上に3バイト確保 const ptr = Module . _malloc ( bytes . length ) ; // 2. JSから線形メモリへ書き込む Module . HEAPU8 . set ( bytes , ptr ) ; // 3. C++側で同じメモリ領域を読み取って合計を計算 const total = Module . _sum_bytes ( ptr , bytes . length ) ; // 4. メモリを解放 Module . _free ( ptr ) ; console . log ( total ) ; // 60 ptr はC++から見れば「ポインタ」ですが、JSから見れば HEAPU8 の「添字(number)」に過ぎません。 HEAPU8[ptr] と記述することで、C++側がポインタを通じてアクセスするのと、全く同じメモリ領域をJS側でも参照できます。 C++オブジェクトの実体 EmscriptenでコンパイルされたC++コードにおいて、 new や std::vector などによる動的なメモリ確保は、すべてWasmの線形メモリ内(のヒープ領域)に対して行われます。JS側から呼び出す _malloc も、同じヒープ領域からメモリを切り出します。 C++オブジェクトの「実体」とは、linear memory上の特定のアドレスから始まる、数バイトから数メガバイトの領域のことです。例えば、以下のクラスを考えます。 class ByteBuffer { public : explicit ByteBuffer ( size_t n) : data_ (n, 0 ) {} size_t size () const { return data_. size (); } private : std :: vector < uint8_t > data_; // 中身はヒープ上の別領域を指す }; new ByteBuffer(1024) を実行すると、まず ByteBuffer 本体(vectorのポインタやサイズ情報など)がヒープに確保され、その内部のvectorがさらに1024バイトをヒープに確保します。これらはいずれもlinear memory内に存在します。 C++の中で完結している限り、この寿命管理は言語仕様によって保証されています。スコープを抜ければスタック上のオブジェクトは破棄され、 std::vector が確保したメモリも自動的に解放されます。 delete を呼び出せばデストラクタが連鎖し、メモリは安全に回収されます。つまり、 RAII(Resource Acquisition Is Initialization)とデストラクタの仕組みが、メモリの確保と解放の対応を担保している のです。 しかし、この保証が有効なのは「C++のライフタイム管理が届く範囲にある」間だけです。オブジェクトへの参照がJS側に渡ると、この前提は崩れます。 境界を越えると寿命の情報が失われる JavaScriptでは、どこからも参照されなくなったオブジェクトはGC(ガベージコレクション)によって自動的に回収されます。しかし、GCが寿命を管理できるのは「JSヒープ上にあるJSの値」だけです。C++オブジェクトの実体は、前述のとおり線形メモリ(巨大な ArrayBuffer )の一領域にすぎず、GCから見れば「生存している1つの大きな箱」の内側でしかありません。その箱の内部のどこが有効で、どこが解放済みなのかを、GCは関知しないのです。 そのため、C++オブジェクトのアドレスを保持していたJS変数が使われなくなり、GCに回収されたとしても、線形メモリ上の実体は確保されたまま残り続けます。C++のデストラクタは呼ばれず、メモリはリークします。 逆のケースも同様で、C++側で free を呼んでメモリを解放しても、JS変数が握っているアドレス(ただの数値)はそのまま残ります。すでに無効になった領域を指し続けるその数値を使えば、解放済みメモリを読み書きする「Use-after-free」を引き起こします。 いずれもC++/JS間の境界を越えた際に、「このアドレスはByteBufferを指し、所有権はこちらにある」という型と所有権のメタ情報が単なる数値に変換されて失われます。その結果、「誰がいつ解放するのか」という所有権の所在が型システムからもランタイムからも見えなくなり、問題が発生します。 embindは何を隠し、何を隠さないのか これまで、WebAssemblyとJavaScript間でデータをやり取りするために、以下の低レイヤーな手順が必要であることを確認しました。 _malloc でメモリを確保する HEAPU8.set 等を介して手動でデータをコピーする 確保したメモリのアドレスを数値として管理する embindは、こうした低レイヤーの「配管」処理を宣言的な記述に置き換え、JavaScriptとC++の間のバインディング実装を省力化可能な、Emscriptenの強力な機能です。 embindを有効にするには、ビルド時に -lembind オプションを付与します。利用にあたっては、C++側で EMSCRIPTEN_BINDINGS ブロックを記述し、公開したい関数やクラスを明示します。 #include <emscripten/bind.h> using namespace emscripten; // 構造体:単純な値のセット struct Point { float x; float y; }; Point midpoint ( const Point& a, const Point& b) { return Point{(a.x + b.x) / 2 , (a.y + b.y) / 2 }; } // クラス:状態を持つオブジェクト class ByteBuffer { public : explicit ByteBuffer ( size_t n) : data_ (n, 0 ) {} size_t size () const { return data_. size (); } void fill ( uint8_t v) { std :: fill (data_. begin (), data_. end (), v); } private : std :: vector < uint8_t > data_; }; EMSCRIPTEN_BINDINGS (demo) { // 構造体をJSのプレーンなオブジェクトとしてマッピング value_object<Point>( "Point" ) . field ( "x" , &Point::x) . field ( "y" , &Point::y); function ( "midpoint" , &midpoint); // クラスをJSのクラスとしてマッピング class_<ByteBuffer>( "ByteBuffer" ) .constructor< size_t >() . function ( "size" , &ByteBuffer::size) . function ( "fill" , &ByteBuffer::fill); // 型登録(JSから操作可能にする) register_vector< float >( "VectorFloat" ); } この定義により、JavaScript側からは、あたかもネイティブなオブジェクトを扱うかのように、C++側の機能を利用できます。 // JS側からの利用例 const m = Module . midpoint ({ x : 0 , y : 0 } , { x : 10 , y : 4 }) ; console . log ( m . x , m . y ) ; // 5 2 const buf = new Module . ByteBuffer ( 1024 ) ; buf . fill ( 0xff ) ; console . log ( buf . size ()) ; // 1024 embindを利用することで、 _malloc による手動メモリ確保や HEAPU8 の直接操作といった低レベルな記述は隠蔽され、コードも簡潔になります。 しかし私は、前節で述べた低レイヤーにおける処理の理解は重要だと考えています。私自身について言えば、前節で見た線形メモリの挙動を一度自分の手で確かめることで、ようやくembindの宣言的な記法を使いこなせるようになったからです。また、いつハンドルを delete() すべきか、なぜ確保したはずのビューが壊れることがあるのかといった疑問にも、筋道を立てて考えられるようになりました。 以降では、embindを使ってJS/C++間でデータを受け渡す手法を見ていきます。この観点で整理すると、本記事が扱う代表的なパターンは、 コピー 、 ハンドル 、 ゼロコピーのビュー の3つに分けられます。 value_object はコピー value_object<Point> として公開した構造体は、JS/C++の境界を越えるたびに、JSのプレーンなオブジェクトへ詰め替えられます。逆方向も同じです。そのため、返ってきた m はC++側の実体への参照ではなく、独立した複製です。たとえば m.x = 99 と書き換えてもC++側には影響しません。使い終わったあとは、通常のJSオブジェクトと同じようにGCによって回収されます。 つまり、 value_object は、所有権を意識せずに扱える一方で、JS/C++の境界を越えるたびにコピーが発生する仕組みです。 この仕組みは、座標や計測値のような小さな値のまとまりを扱うのに向いています。一方で、大きな配列を内包する構造体を毎フレーム往復させるような用途には向きません。 また、 value_object は純粋な値の束であることが前提です。フィールドに生ポインタや所有権を持つリソースを含める設計は避けるべきです。コピーされた先で、そのポインタが何を指しているのか、誰が解放するのかという情報が失われ、単なるアドレス値だけが残ってしまうからです。 class_ はハンドル new Module.ByteBuffer(1024) を実行すると、C++のヒープ、すなわちlinear memory内に ByteBuffer の実体が new されます。JS側に返ってくるのは、その実体を指すハンドルを内蔵した薄いラッパーオブジェクトです。 第1部で見たとおり、C++オブジェクトの実体はlinear memory上にあります。JSの変数が保持しているのは、その実体そのものではなく、実体を指すハンドルです。 buf.fill(0xff) を呼ぶたびに、呼び出しはJSからWasm上の同じC++実体に届きます。ここではコピーは起きません。 その代わり、 実体の解放はJS側が引き受け、明示的に delete() を実行する 必要があります。 register_vector で公開した std::vector も、基本的には class_ と同じ系統です。返ってくるのは素のJS配列ではなく、 .size() 、 .get(i) 、 .set(i, x) 、 .push_back(x) などを持つハンドルです。 この仕組みのため、要素アクセスのたびにJSからC++側への呼び出しが発生します。よって、巨大な配列を .get(i) のループで一要素ずつ走査すると、JS/C++間呼び出しのコストがその回数分だけ積み上がります。また、ハンドル自体には明示的な破棄が必要です。 要素そのものはコピーによって安全にやり取りされますが、ハンドルの後始末はJS側が担う必要があります。次に見る typed_memory_view と対比すると、 register_vector は「中身の扱いは安全寄りだが、破棄の責務は残る配列」と位置づけられます。 typed_memory_view はビュー typed_memory_view が提供するのは、ゼロコピーのアクセスです。C++側の連続領域を一切複製せず、JSの TypedArray としてそのまま覗かせます。たとえば、カメラ画像のようなRGBAバッファを返す例を考えてみます。 #include <emscripten/val.h> class FrameBuffer { public : FrameBuffer ( int w, int h) : pixels_ (w * h * 4 , 0 ) {} // 内部バッファを指すビューを返す。コピーは発生しない emscripten::val pixels () { return emscripten:: val ( emscripten:: typed_memory_view (pixels_. size (), pixels_. data ())); } private : std :: vector < uint8_t > pixels_; // RGBA }; const frame = new Module . FrameBuffer ( 640 , 480 ) ; const view: Uint8Array = frame . pixels () ; // linear memoryを直接指す窓 view [ 0 ] = 255 ; // C++側のpixels_[0]が直接書き換わる この view の正体は、第1部で見た HEAPU8 の部分ビューです。linear memory上の pixels_.data() から始まる領域を、JSの Uint8Array として直接見ているだけです。コピーが発生しないため、メガバイト級のバッファでもほぼ一瞬でJS側へ「渡す」ことができます。 ただし、ビューはあくまで「ビュー」なため、その有効期限は、元のバッファの寿命に完全に支配されます。具体的には、次のいずれかが起きた時点でビューは無効になります。 元のオブジェクト、つまり frame が破棄された → viewは解放済み領域を指すことになる 内部の std::vector が再確保された → viewは古いアドレスを指したままになる 線形メモリが成長した 第1部で予告したとおり、 ArrayBuffer が差し替わり、古いビューはdetachされる。成長は別の場所の malloc をきっかけに起こることもあるため、自分のコードがそのバッファに何もしていなくても、ビューが無効になる可能性がある。 したがって typed_memory_view の鉄則は「 取得したら即座に読み書きし、持ち越さない 」です。保持して使用し続けたいデータは、 view.slice() などでその場でJS側へコピーし、切り離しておく必要があります。 3つの渡し方をまとめると、次のように整理できます。 コピー( value_object )は、所有権を考えなくてよい代わりにコピーコストを払う。 ハンドル( class_ / register_vector )は、コピーを避けられる代わりに、JS側が解放の責務を負う。 ビュー( typed_memory_view )は高速だが、元バッファの寿命と完全に結びつく。 おわりに 本記事では、まずWasmの線形メモリという土台を確認し、その上でembindが提供する3つのデータの渡し方を見てきました。 embindの宣言的なAPIは、 _malloc や HEAPU8 といった低レイヤーを隠蔽してくれます。しかし所有権と寿命の責務は、プログラマが十分に認識し、管理する必要があります。本記事では、低レイヤーの挙動から順を追って解説することで、embindを実装に採用した場合も、そのAPIの背後で所有権と寿命がどう扱われているかを見通せるようになることを目指しました。 同じようにEmscriptenと向き合う方にとって、本記事がメモリ管理を筋道立てて考えるための一助となれば幸いです。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com

動画

書籍