count: false class: middle # typescripten ## إنشاء روابط JavaScript من النوع الآمن لـ emscripten سيباستيان ثيوفيل، think-cell Software، برلين [stheophil@think-cell.com](stheophil@think-cell.com) --- # JavaScript للمبتدئين
**مسألة بسيطة:** تحويل البيانات إلى تنسيق جدولي - قد تكون البيانات `number|string|date` - فرز، تحويل إلى فريد، إجراء بحث ثنائي --- # JavaScript للمبتدئين
**مسألة بسيطة:** تحويل البيانات إلى تنسيق جدولي - قد تكون البيانات `number|string|date` - فرز، تحويل إلى فريد، إجراء بحث ثنائي --- # JavaScript للمبتدئين [MDN Web Docs: Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) _"ترتيب الفرز الافتراضي تصاعدي، مبني على تحويل العناصر إلى سلاسل، ثم مقارنة تسلسلاتها من قيم وحدات رموز UTF-16._ _
لا يمكن ضمان تعقيد الزمن والمساحة للفرز
لأنه يعتمد على التنفيذ."_ 🚫 دون 🚫 فريد« دون بحث ثنائي ??? Erm، لا شكرًا. يوجد Array.sort ولكنني أحتاج إلى كتابة المُقارن بنفسي. يتضمن ذلك التحقق من نوع القيمة الموحدة وتحويل الجدول إلى شيء قابل للمقارنة. لا تقدم ضمانات للأداء. دون بحث ثنائي! اسحبه من npm! لا تنس التحقق مما إذا كان يطبق بالفعل البحث الثنائي! --- # JavaScript للمبتدئين
--- # في الوقت ذاته، في C++ land، قد يكون كل شيء سهلاً للغاية: ```C++ using datavalue = std::variant
std::vector
vecdata; // Fill vecdata auto const rng = std::ranges::unique(std::ranges::sort(vecdata)); std::ranges::binary_search(rng, x) ``` ??? -> في الوقت ذاته، أحتاج إلى التفاعل مع مكتبة JavaScript تقدمها خدمة ويب معينة (استخدم قائمة مهام كبيرة كبنية --- الفئة: largetext # ما الذي أحتاج إلي ه؟ التحويل البرمجي للغة ++C لـ Web Call JavaScript من الاتصالات من النوع الآمن إلى JS --- # إدخال WebAssembly **CppCon 2014:** ألون ذاكاي "Emscripten and asm.js: C++'s role in the modern web" **CppCon 2014:** شاد أوستين "Embind and Emscripten: Blending C++11, JavaScript, and the Web Browser" **CppCon 2016:** دان جوهمان "C++ on the Web: Let's have some serious fun." **CppCon 2017:** لوكاس بيرجدول "Web | C++" **CppCon 2018:** داميان بول "C++ Everywhere with WebAssembly" **CppCon 2019:** بين سميث "Applied WebAssembly: Compiling and Running C++ in Your Web Browser" **CppCon 2019:** بوريسلاف ستانيميروف "Embrace Modern Technology: Using HTML 5 for GUI in C++" ??? راجع كلمات المقدمة في CppCon، لا داعي لتكرار كل شيء --- # إدخال WebAssembly أقصر مقدمة حول WebAssembly - محولة برمجيًا، تنسيق ثنائي، موحدة ومدعومة عن طريق جميع موردي المستعرضات الكبار - سريعة وصغيرة الحجم - أنواع بيانات منخفضة المستوى: أرقام صحيحة وذات فاصلة عائمة - آلية تحديد وصول آمنة حسب التطبيق، تعمل في الجهاز الظاهري للمستعرض
??? لا يوجد وصول إلى التعليمات البرمجية والبيانات المنفصلة للبيئة (بنية Harvard)، بمعني أن التعليمات البرمجية غير قابلة للتغيير --- # إدخال WebAssembly يتم إنشاء WebAssembly من JavaScript ويتفاعل مع JavaScript
--- # إدخال WebAssembly يتم إنشاء WebAssembly من JavaScript ويتفاعل مع JavaScript ```javascript function _abort() { abort(); } function _handle_stack_overflow() { abort("stack overflow"); } var imports = { "_handle_stack_overflow": _handle_stack_overflow, "abort": _abort } var instance = WebAssembly.instantiate( binary, {"env": imports} ).instance; instance.exports["exported_func"](); ``` https://hacks.mozilla.org/2018/10/calls-between-javascript-and-webassembly-are-finally-fast-🎉/ ??? ولكن WebAssembly يدعم فقط قيم الأرقام الصحيحة وذات الفاصلة العائمة --- # إدخال WebAssembly ```wasm (func $strcmp (param $0 i32) (param $1 i32) (result i32) (local $2 i32) (local $3 i32) (local.set $2 (call $SAFE_HEAP_LOAD_i32_1_U_1 (local.get $1) (i32.const 0) ) ) (block $label$1 (br_if $label$1 (i32.eqz (local.tee $3 (call $SAFE_HEAP_LOAD_i32_1_U_1 (local.get $0) (i32.const 0) ) ) ) ) ... ``` --- # التحويل البرمجي للغة ++C للويب - سلسلة الأدوات المستندة إلى clang/llvm مع خلفية WebAssembly - DirectMedia Layer API (SDL) بسيط للوصول إلى جهاز الإدخال وخرج الرسومات - الوصول إلى أحداث إدخال OpenGL API وHTML5 - نظام ملفات ظاهري
--- # emscripten **لغة C أو ++C محمولة سهلة التحويل البرمجي إلى WebAssembly وتشغيلها في المستعرض**
--- الفئة: largetext # ما الذي أحتاج إليه؟ ✅ التحويل البرمجي للغة ++C للويب — اتصال **WebAssembly وemscripten** JavaScript من اتصالات ++C من النوع الآمن إلى JS --- # emscripten **كيفية الاتصال بـ JavaScript؟** 1. تطبيق وظائف C في JS - يقوم WebAssembly بالاستيراد أو التصدير - مقتصر على أنواع WebAssembly المدعومة، أو الأرقام الصحيحة أو الفاصلة العائمة 2. التضمين المباشر ```C++ int x = EM_ASM_INT({ console.log('I received: ' + $0); return $0 + 1; }, 100); printf("%d\n", x); ``` - قيم الإرجاع `int` أو `double` --- # emscripten **`emscripten::val` "الترجمة الصوتية لـ JavaScript" إلى ++C** ```C++ using namespace emscripten; int main() { val AudioContext = val::global("AudioContext"); val context = AudioContext.new_(); val oscillator = context.call
("createOscillator"); oscillator.set("type", val("triangle")); oscillator["frequency"].set("value", val(261.63)); // Middle C } ``` --- # emscripten **`emscripten::val` "الترجمة الصوتية لـ JavaScript" إلى ++C** ```C++ using namespace emscripten; int main() { val AudioContext = val::global("AudioContext"); val context = AudioContext.new_(); * val oscillator = context.call
("createOscillator"); oscillator.set("type", val("triangle")); oscillator["frequency"].set("value", val(261.63)); // Middle C } ``` -- **Pro:** التفاعل الملائم مع كائنات JS **Con:** جمع عيوب كلا اللغتين: 1. تم التحويل البرمجي 2. ليس من النوع الآمن --- # emscripten **`emscripten::val` استنادًا إلى عمليات استيراد WebAssembly المطبقة في JS** ```C++ EM_VAL _emval_new_object(); EM_VAL _emval_new_cstring(const char*); void _emval_incref(EM_VAL value); void _emval_decref(EM_VAL value); void _emval_call_void_method( EM_METHOD_CALLER caller, EM_VAL handle, const char* methodName, EM_VAR_ARGS argv); ``` --- # emscripten
`EM_VAL` = الإشارة إلى كائن JavaScript المخزن في جدول، من الممكن مع عدد الإشارات --- الفئة: largetext # ما الذي أحتاج إليه؟ ✅ التحويل البرمجي للغة ++C للويب — اتصال **WebAssembly وemscripten** ✅ الاتصال بـ JavaScript من C++ — **emscripten** مكالمات من النوع الآمن إلى JS ??? ما ال متبقي؟ إنشاء واجهات أفضل (اصطلاحية) لواجهات برمجة التطبيقات، يمكن إنشاؤها تلقائيًا لأي شيء ولذلك يمكننا استخدام ++C لكتابة تطبيقات الويب وإنشائها لمكتبات JS الخاصة بجهات خارجية --- class: middle # التعليمات البرمجية المباشرة ??? التعليمات البرمجية - النموذج الأول للتعليمات البرمجية، من JavaScript إلى ++C - واجهة ++C مطابقة لواجهات JavaScript، ولكن تم التحقق منها في وقت التحويل البرمجي - في هذا المثال، تم كشف DOM API بالكامل - التحويل البرمجي -> خطأ في المحول البرمجي -> فهمت، يجب أن يكون حجم الخط في الواقع سلسلة! - إظهار html، إظهار lib.dom.d.h، لا يخفي _impl_js_jHTMLElement emscripten API بالنسبة لنا، كيف يمكننا إنشاء هذه الواجهات؟ --- # التعليمات البرمجية المباشرة
--- # TypeScript مكتبات تعريف نوع: ```typescript interface Document extends Node, NonElementParentNode, DocumentOrShadowRoot { readonly URL: string; readonly activeElement: Element | null; readonly anchors: HTMLCollectionOf
; title: string; createElement
( tagName: K, options?: ElementCreationOptions ): HTMLElementTagNameMap[K]; } ``` -- [https://github.com/DefinitelyTyped/DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped) مستودع لأكثر من 7000 مكتبة JavaScript، على سبيل المثال، AngularJS، bootstrap، tableau.com --- # TypeScript يأتي TypeScript مزودًا بواجهة برمجة تطبيقات لمحلل وأداة إصلاح **فائقة السهولة**: ```typescript function transform(file: string) : void { let program = ts.createProgram([file]); const sourceFile = program.getSourceFile(file); ts.forEachChild(sourceFile, node => { if (ts.isFunctionDeclaration(node)) { // do something } else if (ts.isVariableStatement(node)) { // do something else } }); } ``` --- # typescripten ## typescripten — [https://github.com/think-cell/typescripten](https://github.com/think-cell/typescripten) - التحويل البرمجي لإعلانات واجهة TypeScript إلى واجهات ++C - **بمعنى الاتصالات الاصطلاحية من النوع الآمن إلى مكتبات JavaScript عبر emscripten** **JavaScript:** ```javascript document.title = "Hello World from C++"; ``` **C++:** ```C++ using namespace tc; js::document()->title(js::string("Hello World from C++!")); ``` --- # typescripten ```C++ namespace tc::js { struct object_base { * emscripten::val m_emval; }; struct Document : virtual Node, ... { auto URL() noexcept; auto activeElement() noexcept; auto title() noexcept; void title(string v) noexcept; // ... }; inline auto Document::title() noexcept { return m_emval["title"].template as
(); } inline void Document::title(string v) noexcept { m_emval.set("title", v); } inline auto document() noexcept { return emscripten::val::global("document").template as
(); } } ``` --- # typescripten ```C++ namespace tc::js { struct object_base { emscripten::val m_emval; }; struct Document : virtual Node, ... { auto URL() noexcept; auto activeElement() noexcept; auto title() noexcept; void title(string v) noexcept; // ... }; * inline auto Document::title() noexcept { return m_emval["title"].template as
(); } * inline void Document::title(string v) noexcept { m_emval.set("title", v); } inline auto document() noexcept { return emscripten::val::global("document").template as
(); } } ``` --- # typescripten **هل ندعم جميع بنيات TypeScript؟** -- ```typescript interface A { func(a: { length: number }) : void; } ``` -- **لا.** نحتاج إلى دعم البنيات الشائعة في ملفات تعريف الواجهة. -- ```typescript interface A { func(a: TypeWithLengthProperty) : void; } ``` --- # typescripten **بنيات TypeScript المدعومة** - تطبيق الأنواع المدمجة `tc::js::any`، `tc::js::undefined`، `tc::js::null`، `tc::js::string` - الأعضاء الاختياريين، أدوات حماية الأنواع - دعم الأنواع الموحدة `A|B|C` مثل `tc::js::union_t
` - التعدادات المختلطة مثل ```typescript enum E { a, b = "that's a string", c = 1.0 } ``` - تمرير عمليات رد الاتصال وlambda للوظائف إلى JavaScript مثل وظيفة `tc::js::
` - الأنواع العامة مثل `tc::js::Array
` أو `tc::js::Record
` -- **الاستضافة الذاتية بمعني التحويل البرمجي لتعريف الواجهة لـ TypeScript API الذي تستخدمه بنفسها** --- # typescripten تستخدم typescripten نفسها الواجهات التي تم إنشاؤها إلى TypeScript API ```typescript function transform(file: string) : void { let program = ts.createProgram([file]); const sourceFile = program.getSourceFile(file); ts.forEachChild(sourceFile, node => { if (ts.isFunctionDeclaration(node)) { // do something } else if (ts.isVariableStatement(node)) { // do something else } }); } ``` --- # typescripten تستخدم typescripten نفسها الواجهات التي تم إنشاؤها إلى TypeScript API ```C++ void transform(js::string const& file) noexcept { js::Array
files(jst::create_js_object, tc::single(file)); auto const program = js::ts::createProgram(files, ...); auto const sourceFile = program->getSourceFile(file); js::ts::forEachChild(sourceFile, js::lambda( * [](js::ts::Node node) noexcept { if (js::ts::isFunctionDeclaration(node)) { // do something } else if (js::ts::isVariableStatement(node)) { // do something else } } ) ); } ``` --- # typescripten **لا يهم ترتيب الإعلان في TypeScript** ```typescript type FooBar = test.Foo | test.Bar; declare namespace test { export interface Foo { a: string; } export interface Bar { b: number; } } ``` --- # typescripten
**الأنواع الموحدة ليست مثل اتحادات ++C** - ليس لها قيمة تعداد تمييزية - بدلاً من ذلك، فإن تقاطع خصائص `A|B|C` له أعضاء _تتقاطع_ مع أعضاء A وB **و** C -- يمكن إنشاء `A|B|C` من أي قيمة تتم مشاركة جميع أعضائها بواسطة A وB ** و**C ```typescript class D { common: number = 0.0; } let u : A|B|C = new D(); ``` -- **النوع هو مجرد مجموعة من الخصائص = أنواع هيكلية** --- # typescripten **لا تدعم لغة ++C الأنواع الهيكلية**
`union_t
` يحول إلى _فئات أساسية شائعة_ من A وB **و**C -- `union_t
` يحول إلى اتحاد أوسع `union_t
` -- `union_t
` يمكن إنشاؤه من أي شيء يحوّل إلى A أو B أو C -- **لا يقصر على شيء محدد كما يبدو** -- ```typescript interface HasCommonProp { common: number; }; interface A extends HasCommonProp {} interface B extends HasCommonProp {} interface C extends HasCommonProp {} ``` --- # typescripten **تعدادات مختلطة مع تنظيم مخصص** ```typescript export enum FunnyEnum { foo = "foo", bar = 1.5 } ``` -- ```C++ enum class FunnyEnum { foo, bar }; template<> struct MarshalEnum
{ static inline auto const& Values() { static tc::dense_map
vals{ {FunnyEnum::foo, js::string("foo")}, {FunnyEnum::bar, js::any(1.5)} }; return vals; } }; ``` ??? يستخدم أسماء تعداد ثابتة، تنظيم مخصص --- # typescripten **تعدادات مختلطة مع تنظيم مخصص** ```typescript export enum FunnyEnum { foo = "foo", bar = 1.5 } ``` ```C++ enum class FunnyEnum { foo, bar }; template<> struct MarshalEnum
{ static inline auto const& Values() { static tc::dense_map
vals{ * {FunnyEnum::foo, js::string("foo")}, * {FunnyEnum::bar, js::any(1.5)} }; return vals; } }; ``` ??? يستخدم أسماء تعداد ثابتة، تنظيم مخصص --- class: middle # مثال التعليمات البرمجية #2 ??? --- # typescripten **تعد الكائنات ذات وظيفة عدد الإشارات معقدة** ```typescript class SomeButton { constructor() { const button = document.createElement(...); button.addEventListener("click", () => this.OnClick()); } function OnClick(ev: MouseEvent) : void { /* do something */ /* but in which states will this be called? */ } } ``` - يؤدي عدم وجود إتلاف محدد - عند امتلاك كائنات ذات إشارات محسوبة - إلى جعل التفكير في الحالات معقدًا --- # typescripten **بناء جملة سيء ولكن حالة بسيطة ** ```cpp struct SomeButton { SomeButton() { const button = js::document()->createElement(...); button->addEventListener("click", OnClick); } ~SomeButton() { button->remove(); // Our callback will also be destroyed! 🎉 } TC_JS_MEMBER_FUNCTION(S, OnClick, void, (js::MouseEvent ev)) { // do something } }; ``` --- # typescripten ```C++ // 1. Create RAII wrapper OnClick static emscripten::val OnClickWrapper(void* pvThis, emscripten::val const& emvalThis, emscripten::val const& emvalArgs) noexcept; jst::function
OnClick{&OnClickWrapper, this}; ``` -- ```javascript // 2. jst::function ctor calls to JS and creates JS function object *Module.CreateJsFunction = function(iFuncPtr, iThisPtr) { const fnWrapper = function() { if(iFuncPtr !== null) { return Module.tc_js_CallCpp(iFuncPtr, iThisPtr, this, arguments); } }; fnWrapper.detach = function() { iFuncPtr = null; } return fnWrapper; } // 3. JS function object held as emscripten::val ``` --- # typescripten ```C++ // 1. Create RAII wrapper OnClick static emscripten::val OnClickWrapper(void* pvThis, emscripten::val const& emvalThis, emscripten::val const& emvalArgs) noexcept; jst::function
OnClick{&OnClickWrapper, this}; ``` ```javascript // 2. jst::function ctor calls to JS and creates JS function object Module.CreateJsFunction = function(iFuncPtr, iThisPtr) { * const fnWrapper = function() { if(iFuncPtr !== null) { return Module.tc_js_CallCpp(iFuncPtr, iThisPtr, this, arguments); } }; fnWrapper.detach = function() { iFuncPtr = null; } * return fnWrapper; } // 3. JS function object held as emscripten::val ``` --- # typescripten ```C++ // 1. Create RAII wrapper OnClick static emscripten::val OnClickWrapper(void* pvThis, emscripten::val const& emvalThis, emscripten::val const& emvalArgs) noexcept; jst::function
OnClick{&OnClickWrapper, this}; ``` ```javascript // 2. jst::function ctor calls to JS and creates JS function object Module.CreateJsFunction = function(iFuncPtr, iThisPtr) { const fnWrapper = function() { if(iFuncPtr !== null) { * return Module.tc_js_CallCpp(iFuncPtr, iThisPtr, this, arguments); } }; fnWrapper.detach = function() { iFuncPtr = null; } return fnWrapper; } // 3. JS function object held as emscripten::val ``` --- # typescripten ```C++ // 1. Create RAII wrapper OnClick static emscripten::val OnClickWrapper(void* pvThis, emscripten::val const& emvalThis, emscripten::val const& emvalArgs) noexcept; jst::function
OnClick{&OnClickWrapper, this}; ``` ```javascript // 2. jst::function ctor calls to JS and creates JS function object Module.CreateJsFunction = function(iFuncPtr, iThisPtr) { const fnWrapper = function() { if(iFuncPtr !== null) { return Module.tc_js_CallCpp(iFuncPtr, iThisPtr, this, arguments); } }; fnWrapper.detach = function() { * iFuncPtr = null; } return fnWrapper; } // 3. JS function object held as emscripten::val ``` --- # typescripten ```C++ // 4. When called, JS function object passes function pointer back to generic C++ function emscripten::val Call(PointerNumber iFuncPtr, PointerNumber iArgPtr, emscripten::val emvalThis, emscripten::val emvalArgs) noexcept { // 5. Casts function pointer to correct signature and calls it } ``` -- ```C++ static emscripten::val OnClickWrapper(void* pvThis, emscripten::val const& emvalThis, emscripten::val const& emvalArgs) noexcept { // 6. Cast this pointer, unpack arguments from emvalArgs and call OnClickImpl } ``` -- ```C++ void OnClickImpl(js::MouseEvent ev) noexcept { /* ... user code */ } ``` --- # typescripten يدعم TypeScript الفئات العامة ``` js::HTMLCollectionOf
htmlcollection = js::document()->body()->getElementsByTagName(js::string("div")); ``` -- تتم ترجمة الفئات العامة إلى قوالب ++C، تتم ترجمة `interface Array
{}إلى `template
struct Array {}` -- قد يكون للفئات العامة قيود ```typescript enum Enum {} interface A
{} ``` -- يمكن التعبير عنها كمعلمة قالب غير خاص بالنوع ```C++ template
struct A {}; ``` --- # typescripten تدعم الفئات العامة العديد من أنواع القيود ```typescript class Node {} interface A
{} ``` -- يمكن التعبير عنها في صورة ```C++
::value>* = nullptr> struct A {}; ``` مرة أخرى، الدلالات غير متطابقة. --- class: middle # التعليمات البرمجية المباشرة #3 ??? أوضح كيفية إضافة مكتبة tableau قم بإضافتها إلى main.emscripten اشرح أداة الإنشاء، أظهر تصحيح الأخطاء، خرائط المصدر --- الفئة: largetext # ✅ التحويل البرمجي للغة ++C للويب — اتصال **WebAssembly وemscripten** ✅ الاتصال بـ JavaScript من C++ — **emscripten** ✅ مكالمات من النوع الآمن إلى JS —**typescripten** --- # typescripten سيتم استبدال typescripten بـ _WebAssembly Interface types_ Still in proposal phase https://github.com/WebAssembly/interface-types مقدمة أطول: https://hacks.mozilla.org/2019/08/webassembly-interface-types/ كما في ++ISO C، قد يكون من المفيد التدرب على التنفيذ ??? كان من المعتاد تنفيذ النماذج الأولية المبكرة في Rust، ولكن تمت إزالة التنفيذ مرة أخرى --- # typescripten **اختبار الأداء** 1.000.000 اتصال وظيفي، تعمل وظيفة WebAssembly إلى JavaScript JS على زيادة رقم - `extern "C" function` من WebAssembly إلى JavaScript - `EM_ASM_DOUBLE` تعليمات JS البرمجية المضمنة - اتصال _typescripten_ عبر `emscripten::val` ```C++ inline auto _impl_js_j_qMyLib_q::_tcjs_definitions::next() noexcept { return emscripten::val::global("MyLib")["next"]().template as
(); } ``` --- # typescripten **اختبار الأداء** 1.000.000 اتصال وظيفي، تعمل وظيفة WebAssembly إلى JavaScript JS على زيادة رقم
-- **تكلفة تحويل سلاسل C إلى سلاسل JavaScript** --- # typescripten **التحديات التالية:** - القيود العامة ```typescript interface HTMLCollectionOf
extends HTMLCollectionBase { item(index: number): T | null; } ``` -- - أنواع عمليات الوصول المفهرسة ```typescript interface DocumentEventMap { "click": MouseEvent; "keydown": KeyboardEvent; } addEventListener
( type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, ... ): void; ``` --- # typescripten ## راجعها في [https://github.com/think-cell/typescripten](https://github.com/think-cell/typescripten) ## يتم الترحيب بشدة بالمشاركين --- class: middle # شكرًا لك! ## الآن إلى أسئلتك! سيباستيان ثيوفيل، think-cell Software، برلين [stheophil@think-cell.com](stheophil@think-cell.com) --- # typescripten عامل `keyof` لـ يُرجع أسماء خصائص الفئات ```typescript interface K { foo: string: bar: string; } interface A
{} // T can be "foo" or "bar" ``` ربما يتم التعبير عنه بأفضل وجه بالصورة ```C++ enum class KeyOfK { foo, bar }; template
struct A {} ``` ---
×