diff --git a/index.bs b/index.bs
index 27cb54e3..a95f8317 100644
--- a/index.bs
+++ b/index.bs
@@ -810,13 +810,13 @@ The powerPreference opt
To create a context given [=realm=] |realm| and |options| (a {{GPUDevice}} or {{MLContextOptions}}), run these steps:
- 1. Let |context| be a new {{MLContext}} object with |realm|.
+ 1. Let |context| be a new {{MLContext}} in |realm|.
1. If |options| is a {{GPUDevice}} object:
1. Set |context|.{{MLContext/[[contextType]]}} to "[=context type/webgpu=]".
1. Set |context|.{{MLContext/[[powerPreference]]}} to {{MLPowerPreference/"default"}}.
1. Otherwise:
1. Set |context|.{{MLContext/[[contextType]]}} to "[=context type/default=]".
- 1. Set |context|.{{MLContext/[[lost]]}} to [=a new promise=].
+ 1. Set |context|.{{MLContext/[[lost]]}} to [=a new promise=] in |realm|.
1. If |options|["{{MLContextOptions/powerPreference}}"] [=map/exists=], then set |context|.{{MLContext/[[powerPreference]]}} to |options|["{{MLContextOptions/powerPreference}}"].
1. Otherwise, set |context|.{{MLContext/[[powerPreference]]}} to {{MLPowerPreference/"default"}}.
1. If the user agent cannot support |context|.{{MLContext/[[contextType]]}} and |context|.{{MLContext/[[powerPreference]]}}, return failure.
@@ -828,9 +828,9 @@ The powerPreference opt
The createContext(|options|) steps are:
1. Let |global| be [=this=]'s [=relevant global object=].
- 1. If |global|'s [=associated Document=] is not [=allowed to use=] the [=webnn-feature|webnn=] feature, return [=a new promise=] [=rejected=] with a "{{SecurityError}}" {{DOMException}}.
1. Let |realm| be [=this=]'s [=relevant realm=].
- 1. Let |promise| be [=a new promise=].
+ 1. If |global|'s [=associated Document=] is not [=allowed to use=] the [=webnn-feature|webnn=] feature, return [=a new promise=] in |realm| [=rejected=] with a "{{SecurityError}}" {{DOMException}}.
+ 1. Let |promise| be [=a new promise=] in |realm|.
1. Run the following steps [=in parallel=].
1. Let |context| be the result of [=creating a context=] given |realm| and |options|. If that returns failure, then [=queue an ML task=] with |global| to [=reject=] |promise| with a "{{NotSupportedError}}" {{DOMException}} and abort these steps.
1. [=Queue an ML task=] with |global| to [=resolve=] |promise| with |context|.
@@ -842,9 +842,9 @@ The powerPreference opt
The createContext(|gpuDevice|) method steps are:
1. Let |global| be [=this=]'s [=relevant global object=].
- 1. If |global|'s [=associated Document=] is not [=allowed to use=] the [=webnn-feature|webnn=] feature, return [=a new promise=] [=rejected=] with a "{{SecurityError}}" {{DOMException}}.
1. Let |realm| be [=this=]'s [=relevant realm=].
- 1. Let |promise| be [=a new promise=].
+ 1. If |global|'s [=associated Document=] is not [=allowed to use=] the [=webnn-feature|webnn=] feature, return [=a new promise=] in |realm| [=rejected=] with a "{{SecurityError}}" {{DOMException}}.
+ 1. Let |promise| be [=a new promise=] in |realm|.
1. Run the following steps [=in parallel=].
1. Let |context| be the result of [=creating a context=] given |realm| and |gpuDevice|. If that returns failure, then [=queue an ML task=] with |global| to [=reject=] |promise| with a "{{NotSupportedError}}" {{DOMException}} and abort these steps.
1. [=Queue an ML task=] with |global| to [=resolve=] |promise| with |context|.
@@ -1043,9 +1043,10 @@ Creates an {{MLTensor}} associated with this {{MLContext}}.
The createTensor(|descriptor|) method steps are:
1. Let |global| be [=this=]'s [=relevant global object=].
- 1. If [=this=] [=MLContext/is lost=], then return [=a new promise=] [=rejected=] with an "{{InvalidStateError}}" {{DOMException}}.
+ 1. Let |realm| be [=this=]'s [=relevant realm=].
+ 1. If [=this=] [=MLContext/is lost=], then return [=a new promise=] in |realm| [=rejected=] with an "{{InvalidStateError}}" {{DOMException}}.
1. Let |tensor| be the result of [=creating an MLTensor=] given [=this=], and |descriptor|.
- 1. Let |promise| be [=a new promise=].
+ 1. Let |promise| be [=a new promise=] in |realm|.
1. Enqueue the following steps to [=this=].{{MLContext/[[timeline]]}}:
1. Run these steps, but [=/abort when=] [=this=] [=MLContext/is lost=]:
1. Create |tensor|.{{MLTensor/[[data]]}} given |descriptor| and initialize all bytes to zeros.
@@ -1072,10 +1073,10 @@ Reads back the {{MLTensor/[[data]]}} of an {{MLTensor}} from the {{MLContext}}.{
1. Let |global| be [=this=]'s [=relevant global object=].
1. Let |realm| be [=this=]'s [=relevant realm=].
- 1. If |tensor|.{{MLTensor/[[context]]}} is not [=this=], then return [=a new promise=] [=rejected=] with a {{TypeError}}.
- 1. If |tensor|.{{MLTensor/[[isDestroyed]]}} is true, then return [=a new promise=] [=rejected=] with a {{TypeError}}.
- 1. If |tensor|.{{MLTensor/[[descriptor]]}}.{{MLTensorDescriptor/readable}} is false, then return [=a new promise=] [=rejected=] with a {{TypeError}}.
- 1. Let |promise| be [=a new promise=].
+ 1. If |tensor|.{{MLTensor/[[context]]}} is not [=this=], then return [=a new promise=] in |realm| [=rejected=] with a {{TypeError}}.
+ 1. If |tensor|.{{MLTensor/[[isDestroyed]]}} is true, then return [=a new promise=] in |realm| [=rejected=] with a {{TypeError}}.
+ 1. If |tensor|.{{MLTensor/[[descriptor]]}}.{{MLTensorDescriptor/readable}} is false, then return [=a new promise=] in |realm| [=rejected=] with a {{TypeError}}.
+ 1. Let |promise| be [=a new promise=] in |realm|.
1. Enqueue the following steps to |tensor|.{{MLTensor/[[context]]}}.{{MLContext/[[timeline]]}}:
1. Run these steps, but [=/abort when=] [=this=] [=MLContext/is lost=]:
1. Let |bytes| be a [=/byte sequence=] containing a copy of |tensor|.{{MLTensor/[[data]]}}.
@@ -1102,11 +1103,12 @@ Bring-your-own-buffer variant of {{MLContext/readTensor(tensor)}}. Reads back th
The readTensor(|tensor|, |outputData|) method steps are:
1. Let |global| be [=this=]'s [=relevant global object=].
- 1. If |tensor|.{{MLTensor/[[context]]}} is not [=this=], then return [=a new promise=] [=rejected=] with a {{TypeError}}.
- 1. If |tensor|.{{MLTensor/[[isDestroyed]]}} is true, then return [=a new promise=] [=rejected=] with a {{TypeError}}.
- 1. If |tensor|.{{MLTensor/[[descriptor]]}}.{{MLTensorDescriptor/readable}} is false, then return [=a new promise=] [=rejected=] with a {{TypeError}}.
- 1. If [=validating buffer with descriptor=] given |outputData| and |tensor|.{{MLTensor/[[descriptor]]}} returns false, then return [=a new promise=] [=rejected=] with a {{TypeError}}.
- 1. Let |promise| be [=a new promise=].
+ 1. Let |realm| be [=this=]'s [=relevant realm=].
+ 1. If |tensor|.{{MLTensor/[[context]]}} is not [=this=], then return [=a new promise=] in |realm| [=rejected=] with a {{TypeError}}.
+ 1. If |tensor|.{{MLTensor/[[isDestroyed]]}} is true, then return [=a new promise=] in |realm| [=rejected=] with a {{TypeError}}.
+ 1. If |tensor|.{{MLTensor/[[descriptor]]}}.{{MLTensorDescriptor/readable}} is false, then return [=a new promise=] in |realm| [=rejected=] with a {{TypeError}}.
+ 1. If [=validating buffer with descriptor=] given |outputData| and |tensor|.{{MLTensor/[[descriptor]]}} returns false, then return [=a new promise=] in |realm| [=rejected=] with a {{TypeError}}.
+ 1. Let |promise| be [=a new promise=] in |realm|.
1. Enqueue the following steps to |tensor|.{{MLTensor/[[context]]}}.{{MLContext/[[timeline]]}}:
1. Run these steps, but [=/abort when=] [=this=] [=MLContext/is lost=]:
1. Let |bytes| be a [=/byte sequence=] containing a copy of |tensor|.{{MLTensor/[[data]]}}.
@@ -1473,7 +1475,8 @@ The {{MLOperand}} objects are created by the methods of {{MLGraphBuilder}}, inte
To create an MLOperand given {{MLGraphBuilder}} |builder| and {{MLOperandDescriptor}} |desc|, run the following steps:
- 1. Let |operand| be a new {{MLOperand}}.
+ 1. Let |realm| be |builder|'s [=relevant realm=].
+ 1. Let |operand| be a new {{MLOperand}} in |realm|.
1. Set |operand|.{{MLOperand/[[builder]]}} to |builder|.
1. Set |operand|.{{MLOperand/[[descriptor]]}} to |desc|.
1. Return |operand|.
@@ -1483,8 +1486,10 @@ The {{MLOperand}} objects are created by the methods of {{MLGraphBuilder}}, inte
To copy an MLOperand given {{MLOperand}} |operand|, run the following steps:
- 1. Let |result| be a new {{MLOperand}}.
- 1. Set |result|.{{MLOperand/[[builder]]}} to |operand|.{{MLOperand/[[builder]]}}.
+ 1. Let |builder| be |operand|.{{MLOperand/[[builder]]}}.
+ 1. Let |realm| be |builder|'s [=relevant realm=].
+ 1. Let |result| be a new {{MLOperand}} in |realm|.
+ 1. Set |result|.{{MLOperand/[[builder]]}} to |builder|.
1. Set |result|.{{MLOperand/[[descriptor]]}} to |operand|.{{MLOperand/[[descriptor]]}}.
1. If |operand|.{{MLOperand/[[name]]}} [=map/exists=], then set |result|.{{MLOperand/[[name]]}} to |operand|.{{MLOperand/[[name]]}}.
1. Return |result|.
@@ -1582,7 +1587,8 @@ An {{MLTensor}} is created by its associated {{MLContext}}.
To create an MLTensor given {{MLContext}} |context| and {{MLTensorDescriptor}} |descriptor|, run the following steps:
- 1. Let |tensor| be a new {{MLTensor}}.
+ 1. Let |realm| be |context|'s [=relevant realm=].
+ 1. Let |tensor| be a new {{MLTensor}} in |realm|.
1. Set |tensor|.{{MLTensor/[[context]]}} to |context|.
1. Set |tensor|.{{MLTensor/[[descriptor]]}} to |descriptor|.
1. Set |tensor|.{{MLTensor/[[isDestroyed]]}} to false.
@@ -1771,12 +1777,13 @@ Build a composed graph up to a given output operand into a computational graph a
The build(|outputs|) method steps are:
- 1. If [=this=] [=MLGraphBuilder/can not build=], then return [=a new promise=] [=rejected=] with an "{{InvalidStateError}}" {{DOMException}}.
- 1. If |outputs| is empty, then return [=a new promise=] [=rejected=] with a {{TypeError}}.
+ 1. Let |realm| be [=this=]'s [=relevant realm=].
+ 1. If [=this=] [=MLGraphBuilder/can not build=], then return [=a new promise=] in |realm| [=rejected=] with an "{{InvalidStateError}}" {{DOMException}}.
+ 1. If |outputs| is empty, then return [=a new promise=] in |realm| [=rejected=] with a {{TypeError}}.
1. [=map/For each=] |name| → |operand| of |outputs|:
- 1. If |name| is empty, then return [=a new promise=] [=rejected=] with a {{TypeError}}.
- 1. If [=MLGraphBuilder/validating operand=] given [=this=] and |operand| returns false, then return [=a new promise=] [=rejected=] with a {{TypeError}}.
- 1. If |operand| is in [=this=]'s [=MLGraphBuilder/graph=]'s [=computational graph/inputs=] or [=computational graph/constants=], then return [=a new promise=] [=rejected=] with a {{TypeError}}.
+ 1. If |name| is empty, then return [=a new promise=] in |realm| [=rejected=] with a {{TypeError}}.
+ 1. If [=MLGraphBuilder/validating operand=] given [=this=] and |operand| returns false, then return [=a new promise=] in |realm| [=rejected=] with a {{TypeError}}.
+ 1. If |operand| is in [=this=]'s [=MLGraphBuilder/graph=]'s [=computational graph/inputs=] or [=computational graph/constants=], then return [=a new promise=] in |realm| [=rejected=] with a {{TypeError}}.
1. Let |operands| be a new empty [=/set=].
1. Let |operators| be a new empty [=/set=].
1. Let |inputs| be a new empty [=/set=].
@@ -1789,8 +1796,7 @@ Build a composed graph up to a given output operand into a computational graph a
1. [=list/For each=] |input| of |operand|.{{MLOperand/[[operator]]}}'s [=operator/inputs=]:
1. [=queue/Enqueue=] |input| to |queue|.
1. Let |global| be [=this=]'s [=relevant global object=].
- 1. Let |realm| be [=this=]'s [=relevant realm=].
- 1. Let |graph| be a new {{MLGraph}} with |realm|.
+ 1. Let |graph| be a new {{MLGraph}} in |realm|.
1. Set |graph|.{{MLGraph/[[context]]}} to [=this=].{{MLGraphBuilder/[[context]]}}.
1. Set |graph|.{{MLGraph/[[isDestroyed]]}} to false.
1. [=set/For each=] |operand| in |inputs|:
@@ -1798,7 +1804,7 @@ Build a composed graph up to a given output operand into a computational graph a
1. [=map/For each=] |name| → |operand| of |outputs|:
1. Set |graph|.{{MLGraph/[[outputDescriptors]]}}[|name|] to |operand|.{{MLOperand/[[descriptor]]}}.
1. Set [=this=].{{MLGraphBuilder/[[hasBuilt]]}} to true.
- 1. Let |promise| be [=a new promise=].
+ 1. Let |promise| be [=a new promise=] in |realm|.
1. Run the following steps [=in parallel=]:
1. Run these steps, but [=/abort when=] |graph|.{{MLGraph/[[context]]}} [=MLContext/is lost=]:
1. Let |graphImpl| be the result of converting [=this=]'s [=MLGraphBuilder/graph=] with |operands|, |operators|, |inputs|, and |outputs|'s [=map/values=] into an [=implementation-defined=] format which can be interpreted by the underlying platform.
diff --git a/tools/lint.mjs b/tools/lint.mjs
index fdab7800..85e4ba8f 100755
--- a/tools/lint.mjs
+++ b/tools/lint.mjs
@@ -83,8 +83,9 @@ const root = parse(file, {
});
log('simplifying DOM...');
-// Remove script and style elements from consideration
-for (const element of root.querySelectorAll('script, style')) {
+// Remove script and style elements from consideration. Remove generated indexes
+// too, since they can lead to duplicate false-positive matches for lint rules.
+for (const element of root.querySelectorAll('script, style, .index')) {
element.remove();
}
@@ -350,4 +351,19 @@ for (const match of source.matchAll(/\|(\w+)\|\.{{(\w+)\/.*?}}/g)) {
});
}
+// TODO: Generate this from the IDL itself.
+const dictionaryTypes = ['MLOperandDescriptor', 'MLContextLostInfo'];
+
+// Ensure JS objects are created with explicit realm
+for (const match of text.matchAll(/ a new promise\b(?! in realm)/g)) {
+ error(`Promise creation must specify realm: ${format(match)}`);
+}
+for (const match of text.matchAll(/ be a new ([A-Z]\w+)\b(?! in realm)/g)) {
+ const type = match[1];
+ // Dictionaries are just maps, so they don't need a realm.
+ if (dictionaryTypes.includes(type))
+ continue;
+ error(`Object creation must specify realm: ${format(match)}`);
+}
+
globalThis.process.exit(exitCode);