현재 번역은 완벽하지 않습니다. 한국어로 문서 번역에 동참해주세요.

웹어셈블리를 직접 읽고 편집하기 위해 wasm 바이너리 형식을 텍스트로 표현하는 방식을 소개합니다. 이 형식은 텍스트 편집기 및 브라우저, 개발 도구 등에서 바이너리를 설계하기 위한 중간 형식입니다. 이 장에서는 텍스트 형식의 작동 방식, 원시 구문의 규칙, 그리고 바이너리 코드를 표현하는 데 얼마나 관련있는지, 그리고 래퍼를 작성하여 wasm 방식 어떻게 자바스크립트에 표현하는지 보여줍니다.

참고: 이 문서에서는 당신이 그저 웹어셈블리를 자바스크립트에 불러오는 방법보다 훨씬 난해할 수 있습니다.(웹어셈블리를 자바스크립트 API에 사용하기 참고), 하지만 이 예시를 통하여 당신이 웹어셈블리 모듈을 작성하고, 자바스크립트 라이브러리의 성능을 향상할 수있는 예시를 찾거나, 직접 웹어셈블리 컴파일러를 작성할 수 있는 데 도움이 될 것입니다.

S-표현식

웹어셈블리에서 바이너리와 텍스트 사이에 기본적인 코드 교환 방식을 모듈이라 칭합니다. 텍스트 형식에서는, 모듈은 하나의 큰 S-표현식으로 표현됩니다. S-표현식은 트리를 아주 고전적이고 쉽게 묘사하는 텍스트 형식으로, 우리는 나무에 있는 나뭇가지라는 모듈의 구조를 코드로 작성하게 됩니다. 추상적인 문법을 가진 개발 언어와는 다르게, 웹어셈블리의 트리는 평평하면서도, 일반적으로 사용하는 소개 지침 목록으로 구성되었습니다. 

먼저, S-표현식이 어떻게 표현되는지 한번 보겠습니다. 각 트리와 노드를 괄호( ... )로 구성합니다. 이 괄호 안에서는 노드의 방식을 어떻게 기재하는지, 그리고 그리고 후에 있는 공백 구문자가 속성이나 자식 노드를 어떻게 가지는지를 구성할 수 있습니다. 아래 구문이 바로 S-표현식입니다.

(module (memory 1) (func))

모듈이라는 최상위 노드와 2개의 자식 노드를 가진 트리로 구성되었습니다. 그리고 "메모리" 노드에서는 1이라는 속성으로 "함수" 노드를 지칭하였습니다. 이제 이 표현식이 가지는 의미를 간단하게 알아보겠습니다.

간단한 모듈

가장 간단하고, 작동 가능한 wasm 모듈 작성을 시작합니다.

(module)

이 모듈은 전체적으로 비어 있지만 올바르게 작동하는 모듈입니다.

만약 이 모듈을 바이너리로 전환하면,(웹어셈블리 텍스트 형식을 wasm으로 변환 참조), 우리는 겨우 8바이트짜리 모듈 헤더를 이진 형식으로 보게 될 것입니다.

0000000: 0061 736d              ; WASM_BINARY_MAGIC
0000004: 0d00 0000              ; WASM_BINARY_VERSION

함수 표현식을 모듈에 추가하기

별로 흥미롭지 않은 모양이군요. 그렇다면 한번 실행 가능한 모듈을 작성해 보도록 하겠습니다.

모든 웹어셈블리 모듈 코드는 함수 그룹으로 이루어져 있습니다. 여기 함수에 대한 가상 코드 구조를 소개합니다.

( func <signature> <locals> <body> )
  • 명칭(signature)은 함수에서 (인자를)받고 (반환 값)반환하는 형식을 정의합니다.
  • 지역인수(locals)는 자바스크립트의 변수 같지만, 명시적으로 형식을 정의합니다.
  • 본문(body)은 저수준 정의를 일렬로 나열한 목록입니다.

따라서 함수는 다른 언어와 비슷하지만, S-표현식이라서 그런지 꽤 달라 보입니다.

명칭과 인자

명칭은 인자 형식을 목록으로 정의하고, 반환 형식을 정의하는 순서로 이루어져 있습니다. 여기서 중요한 점을 알려드리자면,

  • (결과)의 부재는 함수가 어느 것도 반환하지 않는 걸 뜻합니다.
  • 현재 단 하나의 반환 형식을 가질 수 있으나,  안정화된 후에(영문) 여러 개를 가질 수 있습니다.

각 인자를 정의할 때, wasm는 현재 4가지의 형식을 지원합니다.

  • i32: 32비트 정수
  • i64: 64비트 정수
  • f32: 32비트 부동소수
  • f64: 64비트 부동소수

하나의 인자를 받기 위해 (param i32) 처럼 작성 후, 반환 형식을 받기 위해 (result i32)처럼 작성하였습니다. 여기서부터 2개의 32비트 정수를 받고 64비트 부동소수를 반환하기 위해 아래와 같이 바이너리 함수를 작성하였습니다.

(func (param i32) (param i32) (result f64) ... )

명칭이 뒤에는 형식을 가진 지역인자를 나열하게 됩니다. 예를 들면 (local i32). 인자는 기본적으로 지역에 속하며, 함수 호출 시 인자에 값을 전달받아 초기화 됩니다.

지역인수와 함수인자를 가져오거나 설정하기

지역인수와 함수인자는 함수 본문에서 get_local 명령문과 set_local 명령문을 통해 가져오거나 설정할 수 있습니다.

The get_local/set_local 명령문은 숫자로 이루어진 위치로부터 요소를 가져오거나 설정합니다. 함수인자가 선언 순서상 먼저 위치하며, 그다음 지역인수 순으로 되어 있습니다. 따라서 아래와 같은 함수에서는,

(func (param i32) (param f32) (local f64)
  get_local 0
  get_local 1
  get_local 2)

본문 첫 줄에 get_local 0 명령어로 i32 함수인자를 받아내게 되며, get_local 1 명령어로 f32 함수인자를 받게 될 것입니다. 그리고 get_local 2 명렁어로 f64 지역인수를 받을 수 있습니다.

여기서 하나의 문제가 생겼군요. 숫자로 된 순서대로 받자니 좀 혼란스럽고 짜증날 수 있습니다. 그래서 텍스트로 명명된 함수인자, 지역인수, 그리고 다른 요소들을 간편하게 달러문자 ($) 로 시작하여 선언할 수 있습니다.

따라서 위에 작성한 함수 명칭을 아래와 같이 재구성할 수 있습니다.

(func (param $p1 i32) (param $p2 f32) (local $loc i32) …)

이렇게 작성하면 get_local 0 대신 get_local $p1 처럼 표현할 수 있습니다. (참고로 명명된 인자는 바이너리로 변환 시 다시 숫자로 바뀌게 됩니다.)

스택 머신

우리가 함수 본문을 작성하기 전에 한가지 얘기할 게 더 있습니다. 바로 스택 머신이죠. 브라우저가 효율적으로 컴파일할  때, wasm 실행부 안에는 스택 머신에 대한 규칙이 정의되어 있습니다. 간단하게 생각하면 모든 형식을 스택에 넣고, 이에 i32/i64/f32/f64 값을 스택에 빼는 식입니다.

예를 들면, get_local 명령어는 정의된 지역인수 값을 스택 안에 넣습니다. 그리고 i32.add 명령어로 두 개의 i32 값을 빼낸 다음(암묵적으로 스택에 넣은 2 개의 값을 가져갑니다.),  이 둘의 합(2^32 형태)을 계산한 다음, 결과로 나온 i32 값을 다시 넣게 됩니다.

함수가 호출되면, 이 함수는 빈 스택으로 시작하여 함수가 실행되는 동안 스택이 서서히 채우고 끝나면 다시 비워 버립니다. 아래 함수를 실행해 봅시다.

(func (param $p i32)
  get_local $p
  get_local $p
  i32.add)

여기선 스택이 i32 값 딱 하나만 존재합니다. 그리고 ($p + $p) 식을 i32.add 명령어로 합한 결과가 나오죠. 결국 함수가 가지는 최후의 스택은 반환되는 값 하나 뿐입니다.

웹어셈블리 내 스택 머신의 유효성 보증은 명확합니다. 만약 (result f32) 식을 정의하면, 스택은 반드시 끝에 단 하나의  f32 값만 남아야 합니다. 만약 결과 형식이 정의되어 있지 않다면, 스택은 비어 있어야 하죠.

첫번째 함수 본문

As mentioned before, the function body is simply a list of instructions that are followed as the function is called. Putting this together with what we have already learned, we can finally define a module containing our own simple function:

(module
  (func (param $lhs i32) (param $rhs i32) (result i32)
    get_local $lhs
    get_local $rhs
    i32.add))

This function gets two parameters, adds them together, and returns the result.

There are a lot more things that can be put inside function bodies, but we will start off simple for now, and you’ll see a lot more examples as you go along. For a full list of the available opcodes, consult the webassembly.org Semantics reference.

함수 호출하기

Our function won’t do very much on its own — now we need to call it. How do we do that? Like in an ES2015 module, wasm functions must be explicitly exported by an export statement inside the module.

Like locals, functions are identified by an index by default, but for convenience, they can be named. Let's start by doing this — first, we'll add a name preceded by a dollar sign, just after the func keyword:

(func $add … )

Now we need to add an export declaration — this looks like so:

(export "add" (func $add))

Here, add is the name the function will be identified by in JavaScript whereas $add picks out which WebAssembly function inside the Module is being exported.

So our final module (for now) looks like this:

(module
  (func $add (param $lhs i32) (param $rhs i32) (result i32)
    get_local $lhs
    get_local $rhs
    i32.add)
  (export "add" (func $add))
)

If you want to follow along with the example, save the above our module into a file called add.wat, then convert it into a binary file called add.wasm using wabt (see Converting WebAssembly text format to wasm for details).

Next, we’ll load our binary into a typed array called addCode (as described in Fetching WebAssembly Bytecode), compile and instantiate it, and execute our add function in JavaScript (we can now find add() in the exports property of the instance):

fetchAndInstantiate('add.wasm').then(function(instance) {
   console.log(instance.exports.add(1, 2));  // "3"
});

// fetchAndInstantiate() found in wasm-utils.js
function fetchAndInstantiate(url, importObject) {
  return fetch(url).then(response =>
    response.arrayBuffer()
  ).then(bytes =>
    WebAssembly.instantiate(bytes, importObject)
  ).then(results =>
    results.instance
  );
}

참고: You can find this example in GitHub as add.html (see it live also). Also see WebAssembly.instantiate() for more details about the instantiate function, and wasm-utils.js for the fetchAndInstantiate() source code.

기본 사항 둘러보기

Now we’ve covered the real basics, let’s move on to look at some more advanced features.

같은 모듈 내에 있는 다른 함수 호출하기

The call instruction calls a single function, given its index or name. For example, the following module contains two functions — one just returns the value 42, the other returns the result of calling the first plus one:

(module
  (func $getAnswer (result i32)
    i32.const 42)
  (func (export "getAnswerPlus1") (result i32)
    call $getAnswer
    i32.const 1
    i32.add))

참고: i32.const just defines a 32-bit integer and pushes it onto the stack. You could swap out the i32 for any of the other available types, and change the value of the const to whatever you like (here we’ve set the value to 42).

In this example you’ll notice an (export "getAnswerPlus1") section, declared just after the func statement in the second function — this is a shorthand way of declaring that we want to export this function, and defining the name we want to export it as.

This is functionally equivalent to including a separate function statement outside the function, elsewhere in the module in the same manner as we did before, e.g.:

(export "getAnswerPlus1" (func $functionName))

The JavaScript code to call our above module looks like so:

fetchAndInstantiate('call.wasm').then(function(instance) {
  console.log(instance.exports.getAnswerPlus1());  // "43"
});

참고: You can find this example on GitHub as call.html (see it live also). Again, see wasm-utils.js for the fetchAndInstantiate() source.

자바스크립트에 함수 가져오기

We have already seen JavaScript calling WebAssembly functions, but what about WebAssembly calling JavaScript functions? WebAssembly doesn’t actually have any built-in knowledge of JavaScript, but it does have a general way to import functions that can accept either JavaScript or wasm functions. Let’s look at an example:

(module
  (import "console" "log" (func $log (param i32)))
  (func (export "logIt")
    i32.const 13
    call $log))

WebAssembly has a two-level namespace so the import statement here is saying that we’re asking to import the log function from the console module. You can also see that the exported logIt function calls the imported function using the call instruction we introduced above.

Imported functions are just like normal functions: they have a signature that WebAssembly validation checks statically, and they are given an index and can be named and called.

JavaScript functions have no notion of signature, so any JavaScript function can be passed, regardless of the import’s declared signature. Once a module declares an import, the caller of WebAssembly.instantiate() must pass in an import object that has the corresponding properties.

For the above, we need an object (let's call it importObject) such that importObject.console.log is a JavaScript function.

This would look like the following:

var importObject = {
  console: {
    log: function(arg) {
      console.log(arg);
    }
  }
};

fetchAndInstantiate('logger.wasm', importObject).then(function(instance) {
  instance.exports.logIt();
});

참고: You can find this example on GitHub as logger.html (see it live also).

웹어셈블리 메모리

The above example is a pretty terrible logging function: it only prints a single integer!  What if we wanted to log a text string? To deal with strings and other more complex data types, WebAssembly provides memory. According to WebAssembly, memory is just a large array of bytes that can grow over time. WebAssembly contains instructions like i32.load and i32.store for reading and writing from linear memory.

From JavaScript’s point of view, it’s as though memory is all inside one big (resizable) ArrayBuffer. That’s literally all that asm.js has to play with (except that it isn't resizable; see the asm.js Programming model).

So a string is just a sequence of bytes somewhere inside this linear memory. Let's assume that we’ve written a suitable string of bytes to memory; how do we pass that string out to JavaScript?

The key is that JavaScript can create WebAssembly linear memory instances via the WebAssembly.Memory() interface, and access an existing memory instance (currently you can only have one per module instance) using the associated instance methods. Memory instances have a buffer getter, which returns an ArrayBuffer that points at the whole linear memory.

Memory instances can also grow, for example via the Memory.grow() method in JavaScript. When growth occurs, since ArrayBuffers can’t change size, the current ArrayBuffer is detached and a new ArrayBuffer is created to point to the newer, bigger memory. This means all we need to do to pass a string to JavaScript is to pass out the offset of the string in linear memory along with some way to indicate the length.

While there are many different ways to encode a string’s length in the string itself (for example, C strings); for simplicity here we just pass both offset and length as parameters:

(import "console" "log" (func $log (param i32) (param i32)))

On the JavaScript side, we can use the TextDecoder API to easily decode our bytes into a JavaScript string.  (We specify utf8 here, but many other encodings are supported.)

consoleLogString(offset, length) {
  var bytes = new Uint8Array(memory.buffer, offset, length);
  var string = new TextDecoder('utf8').decode(bytes);
  console.log(string);
}

The last missing piece of the puzzle is where consoleLogString gets access to the WebAssembly memory. WebAssembly gives us a lot of flexibility here: we can either create a Memory object in JavaScript and have the WebAssembly module import the memory, or we can have the WebAssembly module create the memory and export it to JavaScript.

For simplicity, let's create it in JavaScript then import it into WebAssembly.  Our import statement is written as follows:

(import "js" "mem" (memory 1))

The 1 indicates that the imported memory must have at least 1 page of memory (WebAssembly defines a page to be 64KB.)

So let's see a complete module that prints the string “Hi”.  In a normal compiled C program, you’d call a function to allocate some memory for the string, but since we’re just writing our own assembly here and we own the entire linear memory, we can just write the string contents into global memory using a data section.  Data sections allow a string of bytes to be written at a given offset at instantiation time and are similar to the .data sections in native executable formats.

Our final wasm module looks like this:

(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))

참고: Above, note the double semi-colon syntax (;;) for allowing comments in WebAssembly files.

Now from JavaScript we can create a Memory with 1 page and pass it in. This results in "Hi" being printed to the console:

var memory = new WebAssembly.Memory({initial:1});

var importObj = { console: { log: consoleLogString }, js: { mem: memory } };

fetchAndInstantiate('logger2.wasm', importObj).then(function(instance) {
  instance.exports.writeHi();
});

참고: You can find the full source on GitHub as logger2.html (also see it live).

웹어셈블리 테이블

To finish this tour of the WebAssembly text format, let’s look at the most intricate, and often confusing, part of WebAssembly: tables. Tables are basically resizable arrays of references that can be accessed by index from WebAssembly code.

To see why tables are needed, we need to first observe that the call instruction we saw earlier (see Calling functions from other functions in the same module) takes a static function index and thus can only ever call one function — but what if the callee is a runtime value?

  • In JavaScript we see this all the time: functions are first-class values.
  • In C/C++, we see this with function pointers.
  • In C++, we see this with virtual functions.

WebAssembly needed a type of call instruction to achieve this, so we gave it call_indirect, which takes a dynamic function operand. The problem is that the only types we have to give operands in WebAssembly are (currently) i32/i64/f32/f64.

WebAssembly could add an anyfunc type ("any" because the type could hold functions of any signature), but unfortunately this anyfunc type couldn’t be stored in linear memory for security reasons. Linear memory exposes the raw contents of stored values as bytes and this would allow wasm content to arbitrarily observe and corrupt raw function addresses, which is something that cannot be allowed on the web.

The solution was to store function references in a table and pass around table indices instead, which are just i32 values. call_indirect’s operand can therefore simply be an i32 index value.

 wasm에서 테이블 정의

So how do we place wasm functions in our table? Just like data sections can be used to initialize regions of linear memory with bytes, elem sections can be used to initialize regions of tables with functions:

(module
  (table 2 anyfunc)
  (elem (i32.const 0) $f1 $f2)
  (func $f1 (result i32)
    i32.const 42)
  (func $f2 (result i32)
    i32.const 13)
  ...
)
  • In (table 2 anyfunc), the 2 is the initial size of the table (meaning it will store two references) and anyfunc declares that the element type of these references is "a function with any signature". In the current iteration of WebAssembly, this is the only allowed element type, but in the future, more element types will be added.
  • The functions (func) sections are just like any other declared wasm functions. These are the functions we are going to refer to in our table (for example’s sake, each one just returns a constant value). Note that the order the sections are declared in doesn’t matter here — you can declare your functions anywhere and still refer to them in your elem section.
  • The elem section can list any subset of the functions in a module, in any order, allowing duplicates. This is a list of the functions that are to be referenced by the table, in the order they are to be referenced.
  • The (i32.const 0) value inside the elem section is an offset — this needs to be declared at the start of the section, and specifies at what index in the table function references start to be populated. Here we’ve specified 0, and a size of 2 (see above), so we can fill in two references at indexes 0 and 1. If we wanted to start writing our references at offset 1, we’d have to write (i32.const 1), and the table size would have to be 3.

참고: Uninitialized elements are given a default throw-on-call value.

In JavaScript, the equivalent calls to create such a table instance would look something like this:

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);
};

테이블 사용하기

Moving on, now we’ve defined the table we need to use it somehow. Let's use this section of code to do so:

(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 $return_i32)
  • The (type $return_i32 (func (param i32))) block specifies a type, with a reference name. This type is used when performing type checking of the table function reference calls later on. Here we are saying that the references need to be functions that return an i32 as a result.
  • Next, we define a function that will be exported with the name callByIndex. This will take one i32 as a parameter, which is given the argument name $i.
  • Inside the function, we add one value to the stack — whatever value is passed in as the parameter $i.
  • Finally, we use call_indirect to call a function from the table — it implicitly pops the value of $i off the stack. The net result of this is that the callByIndex function invokes the $i’th function in the table.

You could also declare the call_indirect parameter explicitly during the command call instead of before it, like this:

(call_indirect $return_i32 (get_local $i))

In a higher level, more expressive language like JavaScript, you could imagine doing the same thing with an array (or probably more likely, object) containing functions. The pseudo code would look something like tbl[i]().

So, back to the typechecking. Since WebAssembly is typechecked, and anyfunc means "any function signature", we have to supply the presumed signature of the callee at the callsite, hence we include the $return_i32 type, to tell the program a function returning an i32 is expected. If the callee doesn’t have a matching signature (say an f32 is returned instead), a WebAssembly.RuntimeError is thrown.

So what links the call_indirect to the table we are calling? The answer is that there is only one table allowed right now per module instance, and that is what call_indirect is implicitly calling. In the future, when multiple tables are allowed, we would also need to specify a table identifier of some kind, along the lines of

call_indirect $my_spicy_table $i32_to_void

The full module all together looks like this, and can be found in our wasm-table.wat example file:

(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 $return_i32)
)

We load it into a webpage using the following JavaScript:

fetchAndInstantiate('wasm-table.wasm').then(function(instance) {
  console.log(instance.exports.callByIndex(0)); // returns 42
  console.log(instance.exports.callByIndex(1)); // returns 13
  console.log(instance.exports.callByIndex(2));
  // returns an error, because there is no index position 2 in the table
});

참고: You can find this example on GitHub as wasm-table.html (see it live also).

참고: Just like Memory, Tables can also be created from JavaScript (see WebAssembly.Table()) as well as imported to/from another wasm module.

변이 테이블과 동적 링크

Because JavaScript has full access to function references, the Table object can be mutated from JavaScript by the grow(), get() and set() methods. When WebAssembly gets reference types, WebAssembly code will be able to mutate tables itself with get_elem/set_elem instructions.

Because tables are mutable, they can be used to implement sophisticated load-time and run-time dynamic linking schemes. When a program is dynamically linked, multiple instances share the same memory and table. This is symmetric to a native application where multiple compiled .dlls share a single process’s address space.

To see this in action, we’ll create a single import object containing a Memory object and a Table object, and pass this same import object to multiple instantiate() calls.

Our .wat examples look like so:

shared0.wat:

(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)
)

shared1.wat:

(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 $void_to_i32)
)

These work as follows:

  1. The function shared0func is defined in shared0.wat, and stored in our imported table.
  2. This function creates a constant containing the value 0, and then uses the i32.load command to load the value contained in the provided memory index. The index provided is 0 — again, it implicitly pops the previous value off the stack. So shared0func loads and returns the value stored at memory index 0.
  3. In shared1.wat, we export a function called doIt — this fucntion creates two constants containing the values 0 and 42, then calls i32.store to store a provided value at a provided index of the imported memory. Again, it implicitly pops these values off the stack, so the result is that it stores the value 42 in memory index 0,
  4. In the last part of the function, we create a constant with value 0, then call the function at this index 0 of the table, which is shared0func, stored there earlier by the elem block in shared0.wat.
  5. When called, shared0func loads the 42 we stored in memory using the i32.store command in shared1.wat.

참고: The above expressions again pop values from the stack implicitly, but you could declare these explicitly inside the command calls instead, for example:

(i32.store (i32.const 0) (i32.const 42))
(call_indirect $void_to_i32 (i32.const 0))

After converting to assembly, we then use shared0.wasm and shared1.wasm in JavaScript via the following code:

var importObj = {
  js: {
    memory : new WebAssembly.Memory({ initial: 1 }),
    table : new WebAssembly.Table({ initial: 1, element: "anyfunc" })
  }
};

Promise.all([
  fetchAndInstantiate('shared0.wasm', importObj),
  fetchAndInstantiate('shared1.wasm', importObj)
]).then(function(results) {
  console.log(results[1].exports.doIt());  // prints 42
});

Each of the modules that is being compiled can import the same memory and table objects and thus share the same linear memory and table "address space".

참고: You can find this example on GitHub as shared-address-space.html (see it live also).

요약

이 문서에서 웹에셈블리 텍스트 형식의 고수준의 주요 요소를 둘러보고, 어떻게 웹어셈블리 JS API에서 반영되는지 알아봅니다.

같이 보기

  • 중요한 점은 함수 본문을 작성할 때 포괄적인 메뉴얼을 제공했을 뿐, 모든 방벙이 소개되어 있지 않습니다. 웹어셈블리의 의미(영문) 문서를 참고하여 모든 소개를 볼 수 있습니다.
  • 텍스트 형식 문법(영문) 을 참고하여 인터프리터 명세가 구현된 문서를 볼 수 있습니다.

문서 태그 및 공헌자

 이 페이지의 공헌자: composite
 최종 변경: composite,