ElectronにおけるネイティブからJavaScriptへの橋渡し
ElectronのC++またはObjective-Cで記述された機能は、どのようにJavaScriptに渡され、エンドユーザーが利用できるようになるのでしょうか?
背景
Electronは、プラットフォーム固有の実装を気にすることなく、開発者が堅牢なデスクトップアプリを構築するための参入障壁を下げることを主な目的とするJavaScriptプラットフォームです。しかし、その中核では、Electron自体は、特定のシステム言語で記述されたプラットフォーム固有の機能が必要です。
実際には、Electronはネイティブコードを処理するため、単一のJavaScript APIに集中できます。
しかし、それはどのように機能するのでしょうか?ElectronのC++またはObjective-Cで記述された機能は、どのようにJavaScriptに渡され、エンドユーザーが利用できるようになるのでしょうか?
この経路を辿るために、まずapp
モジュールから始めましょう。
lib/
ディレクトリ内のapp.ts
ファイルを開くと、上部に次のコード行が見つかります。
const binding = process.electronBinding('app');
この行は、ElectronのC++/Objective-CモジュールをJavaScriptにバインドして開発者が使用できるようにするメカニズムを直接指しています。この関数は、ElectronBindings
クラスのヘッダーと実装ファイルによって作成されます。
process.electronBinding
これらのファイルは、Node.jsのprocess.binding
と同様に動作するprocess.electronBinding
関数を追加します。process.binding
は、Node.jsのrequire()
メソッドの低レベル実装であり、JSで記述された他のコードの代わりにネイティブコードをrequire
できます。このカスタムprocess.electronBinding
関数は、Electronからネイティブコードをロードする機能を提供します。
最上位のJavaScriptモジュール(app
など)がこのネイティブコードを必要とするとき、そのネイティブコードの状態はどのように決定および設定されるのでしょうか?メソッドはJavaScriptにどのように公開されるのでしょうか?プロパティはどうでしょうか?
native_mate
現在、この質問の答えはnative_mate
にあります。これは、C++とJavaScriptの間で型をマーシャリングしやすくするためのChromiumのgin
ライブラリのフォークです。
native_mate/native_mate
内には、object_template_builder
のヘッダーと実装ファイルがあります。これにより、JavaScript開発者が期待する形状に準拠するネイティブコードでモジュールを形成できます。
mate::ObjectTemplateBuilder
すべてのElectronモジュールをobject
として見ると、それらを構築するためにobject_template_builder
を使用したい理由がより明確になります。このクラスは、C++で記述されたGoogleのオープンソース高性能JavaScriptおよびWebAssemblyエンジンであるV8によって公開されたクラスに基づいて構築されています。V8はJavaScript(ECMAScript)仕様を実装しているため、そのネイティブ機能の実装はJavaScriptでの実装に直接関連付けることができます。たとえば、v8::ObjectTemplate
は、専用のコンストラクター関数とプロトタイプを持たないJavaScriptオブジェクトを提供します。これは、Object[.prototype]
を使用し、JavaScriptではObject.create()
と同等です。
これを実際に確認するには、appモジュールの実装ファイルであるatom_api_app.cc
を参照してください。一番下に次のコードがあります。
mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
.SetMethod("getGPUInfo", &App::GetGPUInfo)
上記の行では、mate::ObjectTemplateBuilder
に対して.SetMethod
が呼び出されています。.SetMethod
は、ObjectTemplateBuilder
クラスの任意のインスタンスで呼び出して、JavaScriptのObjectプロトタイプにメソッドを設定できます。構文は次のとおりです。
.SetMethod("method_name", &function_to_bind)
これは、JavaScriptで次のようになります。
function App{}
App.prototype.getGPUInfo = function () {
// implementation here
}
このクラスには、モジュールにプロパティを設定する関数も含まれています。
.SetProperty("property_name", &getter_function_to_bind)
または
.SetProperty("property_name", &getter_function_to_bind, &setter_function_to_bind)
これらは、Object.definePropertyのJavaScript実装になります。
function App {}
Object.defineProperty(App.prototype, 'myProperty', {
get() {
return _myProperty
}
})
そして
function App {}
Object.defineProperty(App.prototype, 'myProperty', {
get() {
return _myProperty
}
set(newPropertyValue) {
_myProperty = newPropertyValue
}
})
開発者が期待するとおりにプロトタイプとプロパティで形成されたJavaScriptオブジェクトを作成し、このより低いシステムレベルで実装された関数とプロパティについてより明確に推論することができます。
特定のモジュールメソッドを実装する場所の決定自体は複雑で、多くの場合非決定的なものであり、今後の投稿で取り上げます。