aboutsummaryrefslogtreecommitdiff
path: root/files/ru/webassembly/understanding_the_text_format/index.html
diff options
context:
space:
mode:
Diffstat (limited to 'files/ru/webassembly/understanding_the_text_format/index.html')
-rw-r--r--files/ru/webassembly/understanding_the_text_format/index.html537
1 files changed, 537 insertions, 0 deletions
diff --git a/files/ru/webassembly/understanding_the_text_format/index.html b/files/ru/webassembly/understanding_the_text_format/index.html
new file mode 100644
index 0000000000..9acdd0af80
--- /dev/null
+++ b/files/ru/webassembly/understanding_the_text_format/index.html
@@ -0,0 +1,537 @@
+---
+title: Описание текстового формата WebAssembly
+slug: WebAssembly/Understanding_the_text_format
+translation_of: WebAssembly/Understanding_the_text_format
+---
+<div>{{WebAssemblySidebar}}</div>
+
+<p class="summary">Чтобы люди могли читать и редактировать код WebAssembly, существует текстовое представление двоичного формата wasm. Это промежуточная форма, предназначенная для отображения в текстовых редакторах, средствах разработки браузеров и т. д. В этой статье объясняется, как работает этот текстовый формат с точки зрения синтаксиса, как он связан с байт-кодом, который он представляет и оболочками объектов wasm в JavaScript.</p>
+
+<div class="note">
+<p><strong>Примечание</strong>: Ознакомление с данной статьей может оказаться излишним, если вы веб-разработчик, который просто хочет загрузить модуль wasm на страницу и использовать его в своем коде (см. <a href="/ru/docs/WebAssembly/Using_the_JavaScript_API">Использование WebAssembly JavaScript API</a>). Эта статья будет наиболее полезной, если вы хотите написать несколько модулей wasm для оптимизации производительности вашей библиотеки JavaScript или создать свой собственный компилятор WebAssembly.</p>
+</div>
+
+<h2 id="S-выражения">S-выражения</h2>
+
+<p>Как в двоичном, так и в текстовом форматах основным блоком кода в WebAssembly является модуль. В текстовом формате модуль представлен как одно большое S-выражение. S-выражения - это очень старый и очень простой текстовый формат для представления деревьев. И поэтому мы можем думать о модуле как о дереве узлов, которые описывают структуру модуля и его код. В отличие от абстрактного синтаксического дерева в языке программирования, дерево WebAssembly довольно плоское и состоит в основном из списков инструкций.</p>
+
+<p>Во-первых, давайте посмотрим, как выглядит S-выражение. Каждый узел дерева входит в пару круглых скобок - <code>( ... )</code>. Первая метка в скобках сообщает вам, какой это тип узла, за ним следует разделенный пробелами список атрибутов или дочерних узлов. Давайте рассмотрим, что  означает следующее S-выражение WebAssembly:</p>
+
+<pre>(module (memory 1) (func))</pre>
+
+<p>Это выражение представляет дерево с корневым узлом «module» и двумя дочерними узлами - узлом «memory» с атрибутом «1» и узлом «func». Мы вскоре увидим, что на самом деле означают эти узлы.</p>
+
+<h3 id="Самый_простой_модуль">Самый простой модуль</h3>
+
+<p>Давайте начнем с самого простого модуля wasm.</p>
+
+<pre>(module)</pre>
+
+<p>Этот модуль полностью пуст, но является допустимым.</p>
+
+<p>Если мы сейчас преобразуем наш модуль в двоичный формат (см. <a href="/en-US/docs/WebAssembly/Text_format_to_wasm">Перевод текстового формата WebAssembly в wasm</a>), мы увидим только 8-байтовый заголовок модуля, описанный в <a href="http://webassembly.org/docs/binary-encoding/#high-level-structure">двоичном формате</a>:</p>
+
+<pre>0000000: 0061 736d ; WASM_BINARY_MAGIC
+0000004: 0100 0000 ; WASM_BINARY_VERSION</pre>
+
+<h3 id="Добавление_функциональности_в_ваш_модуль">Добавление функциональности в ваш модуль</h3>
+
+<p>Хорошо, это не очень интересно, давайте добавим немного исполняемого кода в этот модуль.</p>
+
+<p>Весь код в модуле сгруппирован в функции, которые имеют следующую структуру псевдокода:</p>
+
+<pre>( func &lt;signature&gt; &lt;locals&gt; &lt;body&gt; )</pre>
+
+<ul>
+ <li><strong>signature</strong> объявляет, что функция принимает (параметры) и возвращает (возвращаемые значения).</li>
+ <li><strong>locals</strong> похожи на переменные в JavaScript, но с определенными явными типами.</li>
+ <li><strong>body</strong> - это просто линейный список низкоуровневых инструкций.</li>
+</ul>
+
+<p>Несмотря на то, что это S-выражение, оно очень напоминает функцию в других языках.</p>
+
+<h2 id="Сигнатуры_и_параметры">Сигнатуры и параметры</h2>
+
+<p>Сигнатура - это последовательность объявлений типов параметров, за которыми следует список объявлений возвращаемых типов. Здесь стоит отметить, что:</p>
+
+<ul>
+ <li>
+ <p class="syntaxbox">    Отсутствие возвращаемого типа <code>(result)</code> означает, что функция ничего не возвращает.   </p>
+ </li>
+ <li>
+ <p class="syntaxbox">В текущей версии WebAssembly может быть не более 1 возвращаемого типа, но <a href="https://webassembly.org/docs/future-features#multiple-return">позже это значение будет изменено</a> на любое число.</p>
+ </li>
+</ul>
+
+<p>    Каждый параметр имеет явно объявленный тип; у wasm в настоящее время есть четыре доступных типа:</p>
+
+<ul>
+ <li>    <code>i32</code>: 32-разрядное целое число</li>
+ <li>    <code>i64</code>: 64-разрядное целое число</li>
+ <li>    <code>f32</code>: 32-разрядное число с плавающей точкой</li>
+ <li>    <code>f64</code>: 64-разрядное число с плавающей точкой</li>
+</ul>
+
+<p>Один параметр можно записать как <code>(param i32)</code>, а тип возвращаемого значения как <code>(result i32)</code>. Двоичная функция, которая принимает два 32-разрядных целых числа и возвращает 64-разрядное число с плавающей запятой, будет записана следующим образом:</p>
+
+<pre>(func (param i32) (param i32) (result f64) ... )</pre>
+
+<p>После сигнатуры перечисляются локальные переменные с указанием типа, например <code>(local i32)</code>. Параметры в сигнатуре приравниваются к локальным переменным, которые инициализируются значением соответствующего аргумента, переданного вызывающей стороной.</p>
+
+<h2 id="Получение_и_установка_локальных_переменных_и_параметров_функции">Получение и установка локальных переменных и параметров функции</h2>
+
+<p>И параметры и локальные переменные могут быть прочитаны и записаны в теле функции с помощью инструкций <code>get_local</code> и <code>set_local</code>.</p>
+
+<p>Инструкции <code>get_local</code> и <code>set_local</code> ссылаются по индексу на параметр, который должен быть получен или  установлен: сначала считаются параметры, а затем локальные переменные в порядке их объявления. Объясним это на примере следующей функции:</p>
+
+<pre>(func (param i32) (param f32) (local f64)
+ get_local 0
+ get_local 1
+ get_local 2)</pre>
+
+<p>Инструкция <code>get_local 0</code> получит параметр i32, <code>get_local 1</code> получит параметр f32, а get_local 2 получит локальную переменную local f64.</p>
+
+<p>Использование числовых индексов для ссылки на элементы может сбивать с толку и раздражать, поэтому текстовый формат позволяет присваивать имена параметрам, локальным переменным и большинству других элементов. Для этого нужно просто добавить имя с префиксом символа доллара (<code>$</code>) непосредственно перед объявлением типа.</p>
+
+<p>Таким образом, можно переписать нашу сигнатуру так:</p>
+
+<pre>(func (param $p1 i32) (param $p2 f32) (local $loc f64) …)</pre>
+
+<p>После чего можно было бы написать инструкцию получения <code>get_local $p1</code> вместо <code>get_local 0</code> и т.д. (Обратите внимание, что, когда этот текст преобразуется в двоичный файл, двоичный файл будет содержать только индексы.)</p>
+
+<h2 id="Стековые_машины">Стековые машины</h2>
+
+<p>Прежде чем мы сможем написать тело функции, мы должны поговорить еще о <strong>стековых машинах</strong>. Хотя браузер компилирует wasm-код во что-то более эффективное, выполнение его определяется в терминах стековой машины, где основная идея заключается в том, что каждый тип инструкции получает или помещает определенное количество значений <code>i32</code> / <code>i64</code> / <code>f32</code> / <code>f64</code> в стек или из стека.</p>
+
+<p>Например, инструкция <code>get_local</code> предназначена для помещения значения локальной переменной, которое она считала, в стек. А инструкция <code>i32.add</code> получает два значения <code>i32</code> (неявно получает два предыдущих значения, помещенных в стек), вычисляет их сумму и помещает назад в стек результат вычисления <code>i32</code>.</p>
+
+<p>Когда вызывается функция, для нее выделяется пустой стек, который постепенно заполняется и очищается при выполнении инструкций в теле функции. Так, например, после выполнения следующей функции:</p>
+
+<pre>(func (param $p i32)
+ get_local $p
+ get_local $p
+ i32.add)</pre>
+
+<p>Стек будет содержать ровно одно значение <code>i32</code> - результат выполнения выражения ($p + $p), которое обработалось инструкцией <code>i32.add</code>. Возвращаемое значение функции - это  последнее значение, оставленное в стеке.</p>
+
+<p>Правила валидации WebAssembly гарантируют, выполнение следующего: если вы объявляете тип возвращаемого значения функции как <code>(result f32)</code>, то стек должен содержать ровно одно значение типа <code>f32</code> в конце. Если тип результата отсутствует, стек должен быть пустым.</p>
+
+<h2 id="Тело_функции">Тело функции</h2>
+
+<p>Как упоминалось ранее, тело функции - это просто список инструкций, которые выполняются при вызове функции. Объединяя это с тем, что мы уже изучили, мы можем наконец определить модуль, содержащий простую функцию:</p>
+
+<pre>(module
+ (func (param $lhs i32) (param $rhs i32) (result i32)
+ get_local $lhs
+ get_local $rhs
+ i32.add))</pre>
+
+<p>Эта функция получает два параметра, складывает их вместе и возвращает результат.</p>
+
+<p>Есть еще много инструкций, которые можно поместить в тело функции. Сейчас мы начнем с простых, а далее вы увидите гораздо больше примеров по мере продвижения. Полный список доступных инструкций смотрите в справочнике по <a href="http://webassembly.org/docs/semantics/">семантике webassembly.org</a>.</p>
+
+<h3 id="Вызов_функции">Вызов функции</h3>
+
+<p>Определение нашей функции само по себе почти ничего не делает - теперь нам нужно её вызвать. Как мы это сделаем? Как и в модуле ES2015, функции wasm должны быть явно экспортированы инструкцией <code>export</code> внутри модуля.</p>
+
+<p>Как и локальные переменные, функции идентифицируются индексом по умолчанию, но для удобства им можно присвоить имя. Давайте это сделаем: сначала добавим имя, которому предшествует знак доллара, сразу после ключевого слова <code>func</code>:</p>
+
+<pre>(func $add … )</pre>
+
+<p>Теперь нам нужно добавить объявление экспорта:</p>
+
+<pre>(export "add" (func $add))</pre>
+
+<p>Здесь <code>add</code> - это имя, по которому функция будет идентифицироваться в коде JavaScript, а <code>$add</code> определяет, какая функция внутри модуля WebAssembly будет экспортироваться.</p>
+
+<p>Итак, наш последний вариант модуля (на данный момент) выглядит так:</p>
+
+<pre>(module
+ (func $add (param $lhs i32) (param $rhs i32) (result i32)
+ get_local $lhs
+ get_local $rhs
+ i32.add)
+ (export "add" (func $add))
+)</pre>
+
+<p>Если вы хотите собственноручно скомпилировать пример, сохраните ранее написанный модуль в файле с именем <code>add.wat</code>, а затем преобразуйте его в двоичный файл с именем <code>add.wasm</code>, используя wabt (подробности смотрите в разделе <a href="/en-US/docs/WebAssembly/Text_format_to_wasm">Перевод текстового формата WebAssembly в wasm</a>).</p>
+
+<p>Затем мы загрузим наш двоичный файл, скомпилируем, создадим его экземпляр и выполним нашу функцию <code>add</code> в коде JavaScript (теперь нам доступна функция <code>add()</code> в свойстве <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Instance/exports">exports</a></code> экземпляра модуля):</p>
+
+<pre class="brush: js">WebAssembly.instantiateStreaming(fetch('add.wasm'))
+.then(obj =&gt; {
+   console.log(obj.instance.exports.add(1, 2));  // "3"
+});</pre>
+
+<div class="note">
+<p><strong>Примечание</strong>: Вы можете найти этот пример на GitHub в файле <a href="https://github.com/mdn/webassembly-examples/blob/master/understanding-text-format/add.html">add.html</a> (смотрите также это <a href="https://mdn.github.io/webassembly-examples/understanding-text-format/add.html">вживую</a>). Также смотрите {{jsxref("WebAssembly.instantiateStreaming()")}} для получения более подробной информации о функции создания экземпляра модуля.</p>
+</div>
+
+<h2 id="Изучение_основ">Изучение основ</h2>
+
+<p>Теперь, когда мы рассмотрели простейшие примеры, давайте перейдем к рассмотрению некоторых более сложных возможностей.</p>
+
+<h3 id="Вызов_функций_из_других_функций_в_том_же_модуле">Вызов функций из других функций в том же модуле</h3>
+
+<p>Для вызова функци по индексу или имени используется инструкция <code>call</code>. Например, следующий модуль содержит две функции - первая просто возвращает значение <code>42</code>, вторая возвращает сумму результата вызова первой функции и единицы:</p>
+
+<pre>(module
+ (func $getAnswer (result i32)
+ i32.const 42)
+ (func (export "getAnswerPlus1") (result i32)
+ call $getAnswer
+ i32.const 1
+ i32.add))</pre>
+
+<div class="note">
+<p><strong>Примечание</strong>: Инструкция <code>i32.const</code> создает 32-разрядное целое число и помещает его в стек. Вы можете поменять <code>i32</code> на любой другой доступный тип данных и изменить значение на любое другое (здесь мы установили значение <code>42</code>).</p>
+</div>
+
+<p>В этом примере обратите внимание на секцию объявления экспорта <code>(export “getAnswerPlus1”)</code>, которая находится сразу после объявления второй функции <code>func</code>. Это сокращенный способ объявления, совмещенный с именем функции, которую мы хотим экспортировать.</p>
+
+<p>Функционально это эквивалентно включению отдельного объявления экспорта функции без функции, в любом месте модуля, например:</p>
+
+<pre>(export "getAnswerPlus1" (func $functionName))</pre>
+
+<p>Код JavaScript для вызова экспортируемой функции из нашего модуля выглядит так:</p>
+
+<pre class="brush: js">WebAssembly.instantiateStreaming(fetch('call.wasm'))
+.then(obj =&gt; {
+   console.log(obj.instance.exports.getAnswerPlus1());  // "43"
+});</pre>
+
+<div class="note">
+<p><strong>Примечание</strong>: Вы можете найти этот пример на GitHub как <a href="https://github.com/mdn/webassembly-examples/blob/master/understanding-text-format/call.html">call.html</a> (смотрите также <a href="https://mdn.github.io/webassembly-examples/understanding-text-format/call.html">вживую</a>). Еще посмотрите <a href="https://github.com/mdn/webassembly-examples/blob/master/wasm-utils.js">wasm-utils.js</a> для метода <code>fetchAndInstantiate()</code>.</p>
+</div>
+
+<h3 id="Импорт_функций_из_JavaScript">Импорт функций из JavaScript</h3>
+
+<p>Мы уже видели JavaScript, вызывающий экспортируемые функции модуля WebAssembly, но как насчет WebAssembly модуля, вызывающего функции JavaScript? WebAssembly не имеет каких либо знаний о внешнем коде JavaScript, но у него есть  способ импорта, который может принимать функции из JavaScript или wasm. Давайте посмотрим на пример:</p>
+
+<pre>(module
+ (import "console" "log" (func $log (param i32)))
+ (func (export "logIt")
+ i32.const 13
+ call $log))</pre>
+
+<p>В инструкции импорта в модуль WebAssembly определено двухуровневое пространство имен, в котором мы указали импортировать функцию <code>log</code> из модуля <code>console</code>. Вы также можете видеть, что экспортируемая функция <code>logIt</code> вызывает импортированную функцию, используя инструкцию <code>call</code>, о которой мы говорили ранее.</p>
+
+<p>Импортируемые функции аналогичны обычным функциям: они имеют сигнатуру, которую WebAssembly проверяет статически, им присваивается индекс (в место которого можно  присвоить имя) и их можно вызвать обычным способом.</p>
+
+<p>Функции JavaScript не имеют понятия сигнатуры, поэтому любую функцию JavaScript можно передать независимо от объявленной сигнатуры импорта. Если модуль объявляет импорт, вызывающая сторона (например метод {{jsxref("WebAssembly.instantiate()")}}) должна передать объект импорта, который должен иметь соответствующее свойство.</p>
+
+<p>Для иллюстрации вышесказанного нам нужен объект (назовем его <code>importObject</code>), в котором  конечное свойство <code>importObject.console.log</code> должно содержать функцию JavaScript.</p>
+
+<p>Код будет выглядеть следующим образом:</p>
+
+<pre class="brush: js">var importObject = {
+ console: {
+ log: function(arg) {
+ console.log(arg);
+ }
+ }
+};
+
+WebAssembly.instantiateStreaming(fetch('logger.wasm'), importObject)
+.then(obj =&gt; {
+  obj.instance.exports.logIt();
+});</pre>
+
+<div class="note">
+<p><strong>Примечание</strong>: Этот пример можно найти на GitHub в файле <a href="https://github.com/mdn/webassembly-examples/blob/master/understanding-text-format/logger.html">logger.html</a> (смотрите также <a href="https://mdn.github.io/webassembly-examples/understanding-text-format/logger.html">вживую</a>).</p>
+</div>
+
+<h3 id="Определение_глобальных_переменных_WebAssembly">Определение глобальных переменных WebAssembly</h3>
+
+<p>WebAssembly имеет возможность создавать экземпляры глобальных переменных. Они доступны как в коде JavaScript, так и через импорт / экспорт для одиного и более экземпляров {{jsxref("WebAssembly.Module")}}. Это очень полезная возможность в плане динамического связывания нескольких модулей.</p>
+
+<p>В текстовом формате WebAssembly это выглядит примерно так (смотрите файл <a href="https://mdn.github.io/webassembly-examples/js-api-examples/global.html">global.html</a> в нашем репозитории на GitHub; смотрите также <a href="https://mdn.github.io/webassembly-examples/js-api-examples/global.html">вживую</a>):</p>
+
+<pre>(module
+ (global $g (import "js" "global") (mut i32))
+ (func (export "getGlobal") (result i32)
+ (get_global $g))
+ (func (export "incGlobal")
+ (set_global $g
+ (i32.add (get_global $g) (i32.const 1))))
+)</pre>
+
+<p>Это похоже на то, что мы делали раньше, за исключением того, что мы указываем глобальную переменную с помощью ключевого слова <code>global</code>. Также мы указываем ключевое слово <code>mut</code> вместе с типом данных значения (если хотим, чтобы глобальная переменная была изменяемой).</p>
+
+<p>Чтобы создать эквивалентный код с помощью JavaScript, вы должны использовать конструктор {{jsxref("WebAssembly.Global()")}}:</p>
+
+<pre class="brush: js">const global = new WebAssembly.Global({value:'i32', mutable:true}, 0);</pre>
+
+<h3 id="Память_WebAssembly">Память WebAssembly</h3>
+
+<p>Приведенный выше пример - довольно ужасная функция ведения журнала: она печатает только одно целое число! Что если мы хотим записать текстовую строку? Для работы со строками и другими более сложными типами данных WebAssembly предоставляет <strong>линейную память</strong>. Согласно технологии WebAssembly, линейная память - это просто большой массив байтов, который со временем может увеличиваться. WebAssembly код содержит ряд инструкций, наподобие <code>i32.load</code> и <code>i32.store</code> для чтения и записи значений из <a href="http://webassembly.org/docs/semantics/#linear-memory">линейной памяти</a>.</p>
+
+<p>Со стороны JavaScript, линейная память как будто находится внутри одного большого (расширяющегося) объекта {{domxref("ArrayBuffer")}}.</p>
+
+<p>Таким образом, строка - это просто последовательность байтов где-то внутри этой линейной памяти. Давайте предположим, что мы записали нужную строку байтов в память; как мы передадим эту строку в JavaScript?<br>
+ Ключевым моментом является то, что JavaScript может создавать экземпляры(объекты) линейной памяти WebAssembly через конструктор {{jsxref("WebAssembly.Memory()")}} и получать доступ к существующему экземпляру памяти (в настоящее время вы можете иметь только один экземпляр памяти на экземпляр модуля), используя соответствующие методы экземпляра модуля. Экземпляр памяти имеет свойство <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory/buffer">buffer</a></code>, которое возвращает объект <code>ArrayBuffer</code>, предоставляя всю линейную память модуля.</p>
+
+<p>Объекты памяти могут расширятся с помощью метода <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory/grow">Memory.grow()</a></code> из JavaScript. Когда происходит расширение, текущий объект <code>ArrayBuffer</code> не может изменить размер и он отсоединяется. Вместо него создается новый объект <code>ArrayBuffer</code>, указывающий на новую, увеличенную память. Пользуясь этими возможностями можно передать строку в  JavaScript, её начальный индекс и её длину в линейной памяти.</p>
+
+<p>Хотя есть много разных способов кодировать длину строки в самой строке (например, как в строках в C); для простоты здесь мы просто передаем смещение и длину в качестве параметров:</p>
+
+<pre>(import "console" "log" (func $log (param i32) (param i32)))</pre>
+
+<p>На стороне JavaScript, мы можем использовать <a href="/en-US/docs/Web/API/TextDecoder">TextDecoder API</a>, чтобы легко декодировать наши байты в строку JavaScript. (Мы указываем кодировку utf8, хотя поддерживаются и другие кодировки.)</p>
+
+<pre class="brush: js">function consoleLogString(offset, length) {
+ var bytes = new Uint8Array(memory.buffer, offset, length);
+ var string = new TextDecoder('utf8').decode(bytes);
+ console.log(string);
+}</pre>
+
+<p>Последний недостающий фрагмент головоломки - это место, где функция <code>consoleLogString</code> получает доступ к памяти (<code>memory</code>) WebAssembly. WebAssembly дает нам здесь много гибкости: либо мы можем создать объект <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory">Memory</a></code> в коде JavaScript и импортировать его в модуль WebAssembly, или мы можем создать его в модуле WebAssembly и затем экспортировать в  JavaScript.</p>
+
+<p>Для простоты, давайте создадим объект памяти в JavaScript и импортируем его в  WebAssembly модуль. Напишем следующее объявление импорта <code>(import</code>):</p>
+
+<pre>(import "js" "mem" (memory 1))</pre>
+
+<p>Число <code>1</code> указывает, что импортируемая память должна иметь по крайней мере 1 страницу памяти (WebAssembly определяет страницу как фиксированный блок памяти в 64КБ.)</p>
+
+<p>Давайте взглянем на наш последний вариант модуля, который выводит слово “Hi”. В обычной C программе, мы бы вызывали функцию для выделения памяти для строки. Но так как мы пишем собственную сборку и у нас есть собственная импортируемая память, то мы просто пишем содержание строки в линейную память, используя секцию <code>data</code>. Data-секция во время создания записывает строку байт, начиная с указанного отступа. И она действует также как и <code>.data</code> секция в “родных” форматах для исполнения.</p>
+
+<p>Наш последний вариант модуля выглядит так:</p>
+
+<pre>(module
+ (import "console" "log" (func $log (param i32 i32)))
+ (import "js" "mem" (memory 1))
+ (data (i32.const 0) "Hi")
+ (func (export "writeHi")
+ i32.const 0 ;; pass offset 0 to log
+ i32.const 2 ;; pass length 2 to log
+ call $log))</pre>
+
+<div class="note">
+<p><strong>Примечание</strong>: Обратите внимание, что двойная точка с запятой (<code>;;</code>) позволяет оставлять комментарии в файлах WebAssembly.</p>
+</div>
+
+<p>Теперь из JavaScript мы можем создать и передать объект памяти размером в 1 страницу. Результатом работы этого кода будет вывод “Hi” в консоль:</p>
+
+<pre class="brush: js">var memory = new WebAssembly.Memory({initial:1});
+
+var importObject = { console: { log: consoleLogString }, js: { mem: memory } };
+
+WebAssembly.instantiateStreaming(fetch('logger2.wasm'), importObject)
+.then(obj =&gt; {
+  obj.instance.exports.writeHi();
+});</pre>
+
+<div class="note">
+<p><strong>Примечание</strong>: Вы можете найти полный исходный код на GitHub в файле <a href="https://github.com/mdn/webassembly-examples/blob/master/understanding-text-format/logger2.html">logger2.html</a> (также смотрите это <a href="https://mdn.github.io/webassembly-examples/understanding-text-format/logger2.html">вживую</a>).</p>
+</div>
+
+<h3 id="Таблицы_WebAssembly">Таблицы WebAssembly</h3>
+
+<p>Чтобы завершить обзор текстового формата WebAssembly, давайте рассмотрим самую сложную и запутанную часть WebAssembly - <strong>таблицы</strong>. Таблицы - это массивы ссылок изменяемого размера, доступ к которым можно получить по индексу из кода WebAssembly.</p>
+
+<p>Чтобы понять, зачем нужны таблицы, нам нужно сначала обратить внимание, что инструкция <code>call</code>, которую мы видели ранее (см. {{anch("Вызов функций из других функций в том же модуле")}}), принимает статический индекс функции и может вызывать только определенную функцию. Но что, если вызываемый элемент будет значением, установленным во время выполнения?</p>
+
+<ul>
+ <li>    В JavaScript это делается постоянно: функции являются ссылочными значениями.</li>
+ <li>    В C/C++ это делается с помощью указателей на функции.</li>
+ <li>    В C++ это делается с помощью виртуальных функций.</li>
+</ul>
+
+<p>Для того чтобы сделать это в WebAssembly нужен был отдельный тип инструкции вызова.  Поэтому мы создали инструкцию <code>call_indirect</code>, которая принимает операнд динамической функции. Проблема в том, что типы данных, которые мы должны использовать в операндах в WebAssembly, в настоящее время такие: <code>i32</code> / <code>i64</code> / <code>f32</code> / <code>f64</code>.</p>
+
+<p>Для WebAssembly можно было бы создать тип инструкции вызова  <code>anyfunc</code> («любой», потому что эта инструкция смогла вызвать функции любой сигнатуры), но, к сожалению, операнд этого типа не может быть сохранен в линейной памяти по соображениям безопасности. Линейная память представляет содержимое хранимых значений в виде незащищенных байтов, и это позволяет содержимому wasm произвольно читать и изменять незащищенные адреса функций, что недопустимо для веб.</p>
+
+<p>Решением стало следующее. Хранить ссылки на функции в таблице и передавать вместо них индексы таблицы, которые являются просто значениями <code>i32</code>. Поэтому операндом инструкции <code>call_indirect</code> может выступить простое значение индекса <code>i32</code>.</p>
+
+<h4 id="Определение_таблицы_в_wasm">Определение таблицы в wasm</h4>
+
+<p>Так как же разместить функции wasm в нашей таблице? Подобно тому, как секции <code>data</code> могут использоваться для инициализации областей линейной памяти байтами, секции <code>elem</code>  могут использоваться для инициализации областей таблиц с функциями:</p>
+
+<pre>(module
+ (table 2 anyfunc)
+ (elem (i32.const 0) $f1 $f2)
+ (func $f1 (result i32)
+ i32.const 42)
+ (func $f2 (result i32)
+ i32.const 13)
+ ...
+)</pre>
+
+<ul>
+ <li>
+ <p>В <code>(table 2 anyfunc)</code>, 2 - это начальный размер таблицы (это означает, что она будет хранить две ссылки), а объявление <code>anyfunc</code> означает, что типом элемента этих ссылок является «функция с любой сигнатурой». В текущей версии WebAssembly, это единственный допустимый тип атрибута, но в будущем будет добавлено больше.</p>
+ </li>
+ <li>
+ <p>Секции функций <code>(func) </code>- это обычные объявления функций модуля wasm. Это те функции, на которые мы будем ссылаться в нашей таблице (каждая из них просто возвращает постоянное значение). Обратите внимание, что порядок объявления секций не имеет значения - вы можете объявить свои функции где угодно и по-прежнему ссылаться на них в секции <code>elem</code>.</p>
+ </li>
+ <li>
+ <p>Секция <code>elem</code> - это список функций, на которые ссылается таблица, в том порядке, в котором они указаны. Здесь можно перечислить любое количество функций, включая их дубликаты.</p>
+ </li>
+ <li>
+ <p>Значение <code>(i32.const 0)</code> внутри секции <code>elem</code> является смещением - его необходимо объявить в начале секции и указать, по какому индексу в таблице ссылок начинают заполняться ссылки на функции. Здесь мы указали 0, а размер таблицы указали как 2 (см. выше), поэтому мы можем заполнить две ссылки на индексы 0 и 1. Если бы мы захотели записать  наши ссылки со смещением в 1, то нам нужно было бы написать <code>(i32.const 1)</code>, а размер таблицы должен был быть равен 3.</p>
+ </li>
+</ul>
+
+<div class="note">
+<p><strong>Примечание</strong>: Неинициализированным элементам присваивается значение вызова по умолчанию.</p>
+</div>
+
+<p>В JavaScript эквивалентный код для создания такого экземпляра таблицы ссылок будет выглядеть примерно так:</p>
+
+<pre class="brush: js">function() {
+ // table section
+ var tbl = new WebAssembly.Table({initial:2, element:"anyfunc"});
+
+ // function sections:
+ var f1 = function() { … }
+ var f2 = function() { … }
+
+ // elem section
+ tbl.set(0, f1);
+ tbl.set(1, f2);
+};</pre>
+
+<h4 id="Использование_таблицы">Использование таблицы</h4>
+
+<p>Мы определили таблицу, которую нам нужно как-то использовать. Для этого добавим следующую секцию кода:</p>
+
+<pre>(type $return_i32 (func (result i32))) ;; if this was f32, type checking would fail
+(func (export "callByIndex") (param $i i32) (result i32)
+ get_local $i
+ call_indirect (type $return_i32))</pre>
+
+<ul>
+ <li>Секция <code>(type $return_i32 (func (result i32)))</code> определяет тип с заданным именем <code>$return_i32</code>. Этот тип используется при выполнении проверки сигнатуры функции в таблице функций. Здесь мы указываем, что ссылки должны быть функциями, возвращаемое значение которых должно быть с типом <code>i32</code>.</li>
+ <li>Далее мы определяем экспортируемую функцию с именем <code>callByIndex</code>. Для единственного  параметра функции задан тип <code>i32</code>, которому присвоено имя <code>$i</code>.</li>
+ <li>Внутри функции мы помещаем одно значение в стек - любое значение, переданное в качестве параметра <code>$i</code> экспортируемой функции.</li>
+ <li>Наконец, мы используем инструкцию <code>call_indirect</code> для вызова функции из таблицы - она ​​неявно получает значение <code>$i</code> из стека. Конечным результатом будет вызов функции из таблицы с индексом, указанным в <code>$i</code>.</li>
+</ul>
+
+<p>Вы также можете объявить параметр <code>call_indirect</code> явно во время вызова инструкции, а не до него (неявным получением из стека), например так:</p>
+
+<pre>(call_indirect (type $return_i32) (get_local $i))</pre>
+
+<p>На языке высокого уровня, таком как JavaScript эти же действия вы можете представить в виде манипуляций с массивом (или, скорее, с объектом), содержащим функции. Псевдокод будет выглядеть примерно так: <code>tbl[i]()</code>.</p>
+
+<p>Итак, вернемся к проверке типов. Так как в коде WebAssembly проверяются типы, а атрибут <code>anyfunc</code> означает “сигнатура любой функции", мы должны предоставить предполагаемую сигнатуру в месте вызова, поэтому мы включаем тип с именем <code>$return_i32</code>, чтобы сообщить программе, что ожидается функция, возвращающая значение с типом <code>i32</code>. Если вызываемая функция не имеет соответствующей сигнатуры (скажем, вместо нее возвращается <code>f32</code>), выбросится исключение {{jsxref("WebAssembly.RuntimeError")}}.</p>
+
+<p>Так как инструкция <code>call_indirect</code> связывается с таблицей, с которой мы вызываем функцию? Ответ заключается в том, что на данный момент для каждого экземпляра модуля разрешена только одна таблица. Поэтому инструкция <code>call_indirect</code> выполняет неявный вызов именно из этой таблицы. В будущем, когда будет разрешено использование нескольких таблиц, нам нужно будет указать идентификатор таблицы, например так:</p>
+
+<pre>call_indirect $my_spicy_table (type $i32_to_void)</pre>
+
+<p>Весь модуль в целом выглядит следующим образом и может быть найден в нашем примере файла <a href="https://github.com/mdn/webassembly-examples/blob/master/understanding-text-format/wasm-table.wat">wasm-table.wat</a>:</p>
+
+<pre>(module
+ (table 2 anyfunc)
+ (func $f1 (result i32)
+ i32.const 42)
+ (func $f2 (result i32)
+ i32.const 13)
+ (elem (i32.const 0) $f1 $f2)
+ (type $return_i32 (func (result i32)))
+ (func (export "callByIndex") (param $i i32) (result i32)
+ get_local $i
+ call_indirect (type $return_i32))
+)</pre>
+
+<p>Загрузка модуля и использование экспортируемой функции в коде JavaScript будет выглядеть так:</p>
+
+<pre class="brush: js">WebAssembly.instantiateStreaming(fetch('wasm-table.wasm'))
+.then(obj =&gt; {
+  console.log(obj.instance.exports.callByIndex(0)); // returns 42
+  console.log(obj.instance.exports.callByIndex(1)); // returns 13
+  console.log(obj.instance.exports.callByIndex(2)); // returns an error, because there is no index position 2 in the table
+});</pre>
+
+<div class="note">
+<p><strong>Примечание</strong>: Этот пример можно найти на GitHub в файле <a href="https://github.com/mdn/webassembly-examples/blob/master/understanding-text-format/wasm-table.html">wasm-table.html</a> (смотрите это также <a href="https://mdn.github.io/webassembly-examples/understanding-text-format/wasm-table.html">вживую</a>)</p>
+</div>
+
+<div class="note">
+<p><strong>Примечание</strong>: Как и в случае с памятью, таблицы также можно создавать из кода JavaScript (см. <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table">WebAssembly.Table()</a></code>).</p>
+</div>
+
+<h3 id="Изменяющиеся_таблицы_и_динамическое_связывание">Изменяющиеся таблицы и динамическое связывание</h3>
+
+<p>Поскольку JavaScript имеет полный доступ к ссылкам на функции, объект таблицы может быть изменен из кода JavaScript с помощью методов <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table/grow">grow()</a></code>, <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table/get">get()</a></code> и <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table/set">set()</a></code>. Когда WebAssembly получит <a href="http://webassembly.org/docs/gc/">ссылочные типы</a>, код WebAssembly сможет изменять таблицы самостоятельно с помощью инструкций <code>get_elem</code> / <code>set_elem</code>.</p>
+
+<p>Поскольку таблицы являются изменяемыми, их можно использовать для реализации сложных схем <a href="http://webassembly.org/docs/dynamic-linking">динамического связывания</a> во время загрузки и во время выполнения. Когда программа динамически связана, несколько экземпляров могут совместно использовать линейную память и таблицу ссылок. Это похоже на поведение в обычном приложении где несколько скомпилированных <code>.dll</code> совместно используют адресное пространство одного процесса.</p>
+
+<p>Чтобы увидеть это в действии, мы создадим один объект импорта, содержащий объект памяти и объект таблицы. Далее мы передадим этот объект импорта при создании нескольких модулей с помощью метода <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/instantiate">instantiate()</a></code>.</p>
+
+<p>Наши примеры файлов <code>.wat</code> выглядят так:</p>
+
+<p><code>shared0.wat</code>:</p>
+
+<pre>(module
+ (import "js" "memory" (memory 1))
+ (import "js" "table" (table 1 anyfunc))
+ (elem (i32.const 0) $shared0func)
+ (func $shared0func (result i32)
+ i32.const 0
+ i32.load)
+)</pre>
+
+<p><code>shared1.wat</code>:</p>
+
+<pre>(module
+ (import "js" "memory" (memory 1))
+ (import "js" "table" (table 1 anyfunc))
+ (type $void_to_i32 (func (result i32)))
+ (func (export "doIt") (result i32)
+ i32.const 0
+ i32.const 42
+ i32.store ;; store 42 at address 0
+ i32.const 0
+ call_indirect (type $void_to_i32))
+)</pre>
+
+<p>Они работают следующим образом:</p>
+
+<ol>
+ <li>Функция <code>shared0func</code> определена в <code>shared0.wat</code> и сохраняется в нашей импортированной таблице.</li>
+ <li>Эта функция создает константу, содержащую значение <code>0</code>, затем инструкция <code>i32.load</code> получает значение из импортированной памяти по предоставленному константой индексу. Предоставленный  индекс равен <code>0</code>. Как и другие подобные инструкции, <code>i32.load</code> неявно получает предоставленное значение из стека. Итак, <code>shared0func</code> загружает и возвращает значение, хранящееся в индексе памяти <code>0</code>.</li>
+ <li>В <code>shared1.wat</code> мы экспортируем функцию с именем <code>doIt</code> - эта функция размещает в стеке  две константы, содержащие значения <code>0</code> и <code>42</code>. Затем она вызывает инструкцию <code>i32.store</code> для сохранения предоставленного значения по предоставленному индексу в импортированной памяти. Опять же, инструкция неявно получает эти значения из стека. Поэтому в результате <code>doIt</code> сохраняет значение <code>42</code> в индексе памяти <code>0</code>.</li>
+ <li>В последней части функции создается константа со значением <code>0</code>, затем вызывается  функция с этим индексом (<code>0</code>) из таблицы. Это будет функция <code>shared0func</code> модуля <code>shared0.wat</code>, которая ранее была размещена там с помощью секции <code>elem</code>.</li>
+ <li>При вызове shared0func загружает число <code>42</code>, которые мы сохранили в памяти, с помощью ранее указанной инструкции <code>i32.store</code> в модуле <code>shared1.wat</code>.</li>
+</ol>
+
+<div class="note">
+<p><strong>Примечание</strong>: Вышеприведенные выражения неявно извлекают значения из стека, но вместо этого вы можете объявить их явно в вызовах инструкций, например:</p>
+
+<pre>(i32.store (i32.const 0) (i32.const 42))
+(call_indirect (type $void_to_i32) (i32.const 0))</pre>
+</div>
+
+<p>После преобразования текста в модули мы используем файлы <code>shared0.wasm</code> и <code>shared1.wasm</code> в JavaScript с помощью следующего кода:</p>
+
+<pre class="brush: js">var importObj = {
+ js: {
+ memory : new WebAssembly.Memory({ initial: 1 }),
+ table : new WebAssembly.Table({ initial: 1, element: "anyfunc" })
+ }
+};
+
+Promise.all([
+  WebAssembly.instantiateStreaming(fetch('shared0.wasm'), importObj),
+  WebAssembly.instantiateStreaming(fetch('shared1.wasm'), importObj)
+]).then(function(results) {
+  console.log(results[1].instance.exports.doIt());  // prints 42
+});</pre>
+
+<p>Каждый из компилируемых модулей может импортировать общие объекты памяти и таблицы. Таким образом, они могут совместно использовать одну и ту же линейную память и таблицу ссылок.</p>
+
+<div class="note">
+<p><strong>Примечание</strong>: Этот пример можно найти на GitHub в файле <a href="https://github.com/mdn/webassembly-examples/blob/master/understanding-text-format/shared-address-space.html">shared-address-space.html</a> (смотрите это также <a href="https://mdn.github.io/webassembly-examples/understanding-text-format/shared-address-space.html">вживую</a>).</p>
+</div>
+
+<h2 id="Резюме">Резюме</h2>
+
+<p>На этом мы завершаем обзор основных компонентов текстового формата WebAssembly и того, как они отображены в WebAssembly JS API.</p>
+
+<h2 id="Смотрите_также">Смотрите также</h2>
+
+<ul>
+ <li><a href="http://webassembly.org/docs/semantics">Семантика WebAssembly</a> для информации по всем возможным инструкциям.</li>
+ <li><a href="https://github.com/WebAssembly/spec/blob/master/interpreter/README.md#s-expression-syntax">Грамматика текстового формата</a>, который реализован в интерпретаторе спецификации.</li>
+</ul>