Skip to content

Commit 4d2315d

Browse files
committed
Editorial: Clean up caching JS objects
Previously, the instantiate function had some kind of complicated logic to make it so just one JS object per memory, table or exported function would be created per address in the store without having a global mapping. This patch switches to a global mapping to make the specification easier to read and modify over time. The change here was discussed in this comment thread: WebAssembly#591 (comment)
1 parent c437b7f commit 4d2315d

File tree

1 file changed

+50
-60
lines changed

1 file changed

+50
-60
lines changed

document/js-api/index.bs

+50-60
Original file line numberDiff line numberDiff line change
@@ -288,13 +288,7 @@ A {{Module}} object represents a single WebAssembly module. Each {{Module}} obje
288288
To <dfn>instantiate a WebAssembly module</dfn> from a {{Module}} |moduleObject| and imports |importObject|, perform the following steps:
289289
1. Let |module| be |moduleObject|.\[[Module]].
290290
1. If |module|.[=𝗂𝗆𝗉𝗈𝗋𝗍𝗌=] is not an empty list, and |importObject| is undefined, throw a {{TypeError}} exception.
291-
1. Let |funcs| be an empty [=list=] of callable JavaScript objects.
292-
1. Let |memories| be an empty [=list=] of {{Memory}} objects.
293-
1. Let |tables| be an empty [=list=] of {{Table}} objects.
294-
295-
Note: The |funcs|, |memories| and |tables| lists are collected and used in order to cache the JavaScript objects corresponding to WebAssembly objects. If a WebAssembly object is exported multiple times, it will appear in exports as the same object instance. Moreover, if a JavaScript wrapper of a WebAssembly object was imported, and then present on the export, a new wrapper will not need to be created. However, if an ordinary JS function is imported, it will appear as wrapped by an [=Exported Function=].
296-
297-
6. Let |imports| be an empty [=list=] of [=external value=]s.
291+
1. Let |imports| be an empty [=list=] of [=external value=]s.
298292
1. For each (|moduleName|, |componentName|, |externtype|) in [=module_imports=](|module|), do
299293
1. Let |o| be ? [=Get=](|importObject|, |moduleName|).
300294
1. If [=Type=](|o|) is not [=Object=], throw a {{TypeError}} exception.
@@ -305,11 +299,9 @@ A {{Module}} object represents a single WebAssembly module. Each {{Module}} obje
305299
1. Let |funcaddr| be the value of |v|'s \[[FunctionAddress]] internal slot.
306300

307301
Note: The signature is checked by [=instantiate_module=] invoked below.
308-
309-
2. [=Append=] |v| to |funcs|.
310302
1. Otherwise,
311303
1. [=Create a host function=] from |v| and let |funcaddr| be the result.
312-
1. Let |externfunc| be the [=external value=] [=external value|𝖿𝗎𝗇𝖼=] |funcaddr|
304+
1. Let |externfunc| be the [=external value=] [=external value|𝖿𝗎𝗇𝖼=] |funcaddr|.
313305
1. [=Append=] |externfunc| to |imports|.
314306
1. If |externtype| is of the form [=𝗀𝗅𝗈𝖻𝖺𝗅=] |globaltype|,
315307
1. If |globaltype| is [=𝗂𝟨𝟦=] or [=Type=](|v|) is not [=Number=], throw a {{LinkError}} exception.
@@ -325,72 +317,46 @@ A {{Module}} object represents a single WebAssembly module. Each {{Module}} obje
325317

326318
Note: [=instantiate_module=] invoked below will check the imported {{Memory}}'s size against the importing module's requirements.
327319

328-
2. [=Append=] |v| to |memories|.
329-
1. Let |externmem| be the [=external value=] [=external value|𝗆𝖾𝗆=] |v|.\[[Memory]].
320+
2. Let |externmem| be the [=external value=] [=external value|𝗆𝖾𝗆=] |v|.\[[Memory]].
330321
1. [=Append=] |externmem| to |imports|.
331322
1. Otherwise, |externtype| is of the form [=𝗍𝖺𝖻𝗅𝖾=] |tabletype|,
332323
1. If |v| is not a {{Table}} instance, throw a {{LinkError}} exception.
333324

334325
Note: The table's length, etc. is checked by [=instantiate_module=] invoked below.
335326

336-
2. [=Append=] |v| to |tables|.
337-
1. Let |tableaddr| be |v|.\[[Table]]
327+
2. Let |tableaddr| be |v|.\[[Table]]
338328
1. Let |externtable| be the [=external value=] [=external value|𝗍𝖺𝖻𝗅𝖾=] |tableaddr|.
339329
1. [=Append=] |externtable| to |imports|.
340-
1. For each element |func| of |v|.\[[Values]],
341-
1. If |func| is not null, append |func| to |funcs|.
342-
1. Let (|store|, |instance|) be [=instantiate_module=](|store|, |module|, |imports|), up through step 16 of [=the instantiation algorithm=], but omitting execution of the start function.
343-
1. If |instance| is [=error=], throw a {{LinkError}} exception, or other <a href="#errors">appropriate exception type</a>.
344-
1. For each |tableaddr| in |instance|.𝗍𝖺𝖻𝗅𝖾𝖺𝖽𝖽𝗋𝗌,
345-
1. If there is no element in |tables| whose |table|.\[[Table]] is |tableaddr|:
346-
1. Let |table| be [=create a table object|a new table object=] from |tableaddr|.
347-
1. [=Append=] |table| to |tables|.
348-
1. Otherwise:
349-
1. Let |table| be the element in |tables| where |table|.\[[Table]] is |tableaddr|.
350-
1. For each index |i| from 0 to the length of |table|.\[[Values]] &minus; 1, do
351-
1. Let |funcaddr| be [=read_table=](|store|, |tableaddr|, |i|).
352-
1. Assert: |funcaddr| is not [=error=] because this read is in bounds.
353-
1. If |funcaddr| is not empty,
354-
1. If |funcs| contains any |func| where |func|.\[[ExportedAddress]] is |funcaddr|,
355-
1. Let |func| be that function object.
356-
1. Otherwise:
357-
1. Let |func| be a [=a new Exported Function=] created from |funcaddr|.
358-
1. [=Append=] |func| to |funcs|.
359-
1. Set the |table|.\[[Values]][|i|] to |func|.
360-
361-
Note: The table and function objects created by the above steps are only observable for tables that are either imported or exported.
362-
363-
1. Let |startfuncaddr| be the start function of |instance|.
364-
1. Let (|store|, |ret|) be [=invoke_func=](|store|, |startfuncaddr|, empty).
365-
1. If |ret| is [=error=], throw an exception. This exception should be a WebAssembly {{RuntimeError}} exception, unless otherwise indicated by <a href="#errors">the WebAssembly error mapping</a>.
366-
1. Assert: |ret| is empty.
330+
1. Let (|store|, |instance|) be [=instantiate_module=](|store|, |module|, |imports|).
331+
1. If |instance| is [=error=], throw an appropriate exception type:
332+
* A {{LinkError}} exception for most cases which occur during linking.
333+
* If the error came when running the start function, throw a {{RuntimeError}} for most errors which occur from WebAssembly, or the error object propagated from inner ECMAScript code.
334+
* Another error type if appropriate, for example an out-of-memory exception, as documented in <a href="#errors">the WebAssembly error mapping</a>.
367335
1. Let |exportsObject| be ! [=ObjectCreate=](null).
368336
1. For each pair (|name|, |externtype|) in [=module_exports=](|module|),
369337
1. Let |externval| be [=get_export=](|instance|, |name|).
338+
1. Assert: |externval| is not [=error=].
370339
1. If |externtype| is of the form [=𝖿𝗎𝗇𝖼=] |functype|,
371-
1. If |funcs| contains an entry |func| where 𝖿𝗎𝗇𝖼 |func|.\[[FunctionAddress]] is |externval|, let |value| be |func|
372-
1. Otherwise,
373-
1. Let |func| be the result of [=a new Exported Function=] from |externval|.
374-
1. [=Append=] |func| to |funcs|.
375-
1. Let |value| be |func|.
340+
1. Then, |externval| is of the form [=external value|𝖿𝗎𝗇𝖼=] |funcaddr|.
341+
1. Let |func| be the result of [=a new Exported Function=] from |funcaddr|.
342+
1. Let |value| be |func|.
376343
1. If |externtype| is of the form [=𝗀𝗅𝗈𝖻𝖺𝗅=] |globaltype|,
377344
1. If |globaltype|.<em>[=global type|valtype=]</em>) is [=𝗂𝟨𝟦=], throw a {{LinkError}} exception.
378345
1. Assert: |globaltype|.<em>[=global type|mut=]</em> is [=global type|𝖼𝗈𝗇𝗌𝗍=], as verified by WebAssembly validation.
379346
1. Let |value| be [=ToJSValue=]([=read_global=](|store|, |externval|)).
380347
1. If |externtype| is of the form [=𝗆𝖾𝗆=] |memtype|,
381-
1. If there is an element |memory| in |memories| such that 𝗆𝖾𝗆 |memory|.\[[Memory]] is |externval|, then let |value| be |memory|
382-
1. Otherwise:
383-
1. Let |memory| be [=create a memory object|a new Memory object=] from |externval|.
384-
1. [=Append=] |memory| to |memories|.
385-
1. Let |value| be |memory|.
348+
1. Then, |externval| is of the form [=external value|𝗆𝖾𝗆=] |memaddr|.
349+
1. Let |memory| be [=create a memory object|a new Memory object=] from |memaddr|.
350+
1. Let |value| be |memory|.
386351
1. Otherwise, |externtype| is of the form [=𝗍𝖺𝖻𝗅𝖾=] |tabletype|,
387-
1. Let |value| be the unique |table| in |tables| such that 𝗍𝖺𝖻𝗅𝖾 |table|.\[[Table]] is |externval|.
352+
1. Then, |externval| is of the form [=external value|𝗍𝖺𝖻𝗅𝖾=] |tableaddr|.
353+
1. Let |table| be [=create a Table object|a new Table object=] from |tableaddr|.
354+
1. Let |value| be |table|.
388355
1. Let |status| be ! [=CreateDataProperty=](|exportsObject|, |name|, |value|).
389356
1. Assert: |status| is true.
390357

391358
Note: the validity and uniqueness checks performed during [=WebAssembly module validation=] ensure that each property name is valid and no properties are defined twice.
392-
393-
33. Perform ! [=SetIntegrityLevel=](|exportsObject|, `"frozen"`).
359+
1. Perform ! [=SetIntegrityLevel=](|exportsObject|, `"frozen"`).
394360
1. Let |instanceObject| be a new {{Instance}} object whose internal \[[Instance]] slot is set to |instance| and the \[[Exports]] slot to |exportsObject|.
395361
1. Return |instanceObject|.
396362
</div>
@@ -472,8 +438,6 @@ interface Module {
472438
1. Let |module| be |moduleObject|.\[[Module]].
473439
1. Let |exports| be an empty [=list=].
474440
1. For each (|name|, |type|) in [=module_exports=](|module|)
475-
1. Let |nameString| be String value consisting of the [=UTF16Encoding=] of each code point of |name|.
476-
1. Assert: |name| is not failure (|module| is [=valid=]).
477441
1. Let |kind| be the [=string value of the extern type=] |type|.
478442
1. Let |obj| be a new {{ModuleExportDescriptor}} dictionary with {{ModuleExportDescriptor/name}} |name| and {{ModuleExportDescriptor/kind}} |kind|.
479443
1. [=Append=] |obj| to the end of |exports|.
@@ -551,22 +515,31 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each
551515

552516
* \[[Memory]] : a [=memory address=]
553517
* \[[BufferObject]] : an {{ArrayBuffer}} whose [=Data Block=] is [=identified with=] the above memory address
518+
519+
Each [=agent=] has an associated [=ordered map=] known as the <dfn>Memory object cache</dfn> mapping [=memory address=]es to {{Memory}} objects.
520+
521+
Note: This mapping is used to ensure that, for a given [=agent=], there exists at most one {{Memory}} object for a particular [=memory address=].
554522
</div>
555523

556524
<div algorithm>
557525
To <dfn>create a memory object</dfn> from a [=memory address=] |memaddr|, perform the following steps:
558526

527+
1. Let |map| be the current [=agent=]'s associated [=Memory object cache=].
528+
1. If |map|[|memaddr|] [=map/exists=],
529+
1. Return |map|[|memaddr|].
559530
1. Let |block| be a Data Block which is [=identified with=] the underlying memory of |memaddr|
560531
1. Let |buffer| be a new {{ArrayBuffer}} whose \[[ArrayBufferData]] is |block| and \[[ArrayBufferByteLength]] is set to the length of |block|.
561-
1. Return a new {{Memory}} instance with \[[Memory]] set to |memaddr| and \[[BufferObject]] set to |buffer|.
532+
1. Let |memory| be a new {{Memory}} instance with \[[Memory]] set to |memaddr| and \[[BufferObject]] set to |buffer|.
533+
1. [=map/Set=] |map|[|memaddr|] to |memory|.
534+
1. Return |memory|.
562535

563536
Issue: Any attempts to [=DetachArrayBuffer|detach=] |buffer|, other than the detachment performed by {{Memory/grow(delta)}}, will throw a {{TypeError}} exception. Specifying this behavior <a href="https://github.com/tc39/ecma262/issues/1024">requires changes to the ECMAScript specification</a>.
564537
</div>
565538

566539
<div algorithm>
567540
The <dfn constructor for="Memory">Memory(descriptor)</dfn> constructor, when invoked, performs the following steps:
568-
1. If |descriptor|\["initial"] is [=present=], let |initial| be |descriptor|\["initial"]; otherwise, let |initial| be 0.
569-
1. If |descriptor|\["maximum"] is [=present=], let |maximum| be |descriptor|\["maximum"]; otherwise, let |maximum| be empty.
541+
1. If |descriptor|["initial"] is [=present=], let |initial| be |descriptor|["initial"]; otherwise, let |initial| be 0.
542+
1. If |descriptor|["maximum"] is [=present=], let |maximum| be |descriptor|["maximum"]; otherwise, let |maximum| be empty.
570543
1. Let |memtype| be { min |initial|, max |maximum| }
571544
1. Let |store| be the current agent's [=associated store=].
572545
1. Let (|store|, |memaddr|) be [=alloc_mem=](|store|, |memtype|). If allocation fails, throw a {{RangeError}} exception.
@@ -621,12 +594,21 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each
621594

622595
* \[[Table]] : a [=table address=]
623596
* \[[Values]] : a List whose elements are either null or [=Exported Function=]s.
597+
598+
Each [=agent=] has an associated [=ordered map=] known as the <dfn>Table object cache</dfn> mapping [=table address=]es to {{Table}} objects.
599+
600+
Note: This mapping is used to ensure that, for a given [=agent=], there exists at most one {{Table}} object for a particular [=table address=].
624601
</div>
625602

626603
<div algorithm>
627604
To <dfn>create a table object</dfn> from a [=table address=] |tableaddr|, perform the following steps:
605+
1. Let |map| be the current [=agent=]'s associated [=Table object cache=].
606+
1. If |map|[|tableaddr|] [=map/exists=],
607+
1. Return |map|[|tableaddr|].
628608
1. Let |values| be a list whose length is [=size_table=](|store|, |tableaddr|) where each element is null.
629-
1. Return a new {{Table}} instance with \[[Table]] set to |tableaddr| and \[[Values]] set to |values|.
609+
1. Let |table| be a new {{Table}} instance with \[[Table]] set to |tableaddr| and \[[Values]] set to |values|.
610+
1. [=map/Set=] |map|[|tableaddr|] to |table|.
611+
1. Return |table|.
630612
</div>
631613

632614
<div algorithm>
@@ -690,6 +672,10 @@ A WebAssembly function is made available in JavaScript as an <dfn>Exported Funct
690672
Exported Functions are [=Built-in Function Objects=] which are not constructors, and which have a \[[FunctionAddress]] internal slot.
691673
This slot holds a [=function address=] relative to the current agent's [=associated store=].
692674

675+
Each [=agent=] has an associated [=ordered map=] known as the <dfn>Exported Function cache</dfn> mapping [=function address=]es to [=Exported Function=] objects.
676+
677+
Note: This mapping is used to ensure that, for a given [=agent=], there exists at most one [=Exported Function=] object for a particular [=function address=].
678+
693679
<div algorithm>
694680
The <dfn>name of the WebAssembly function</dfn> |funcaddr| is found by performing the following steps:
695681

@@ -709,6 +695,9 @@ This slot holds a [=function address=] relative to the current agent's [=associa
709695
<div algorithm>
710696
To create <dfn>a new Exported Function</dfn> from a WebAssembly [=function address=] |funcaddr|, perform the following steps:
711697

698+
1. Let |map| be the current [=agent=]'s associated [=Exported Function cache=].
699+
1. If |map|[|funcaddr|] [=map/exists=],
700+
1. Return |map|[|funcaddr|].
712701
1. Let |steps| be "[=call an Exported Function|call the Exported Function=] |funcaddr| with arguments."
713702
1. Let |realm| be the [=current Realm=].
714703
1. Let |function| be [=CreateBuiltinFunction=](|realm|, |steps|, [=%FunctionPrototype%=], &laquo; \[[FunctionAddress]]).
@@ -720,6 +709,7 @@ This slot holds a [=function address=] relative to the current agent's [=associa
720709
1. Perform ! [=DefinePropertyOrThrow=](|function|, `"length"`, PropertyDescriptor {\[[Value]]: |arity|, \[[Writable]]: false, \[[Enumerable]]: false, \[[Configurable]]: true}).
721710
1. Let |name| be the [=name of the WebAssembly function=] |funcaddr|.
722711
1. Perform ! [=SetFunctionName=](|function|, |name|).
712+
1. [=map/Set=] |map|[|funcaddr|] to |function|.
723713
1. Return |function|.
724714
</div>
725715

0 commit comments

Comments
 (0)