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