Skip to content

Give lazy functions ability to assert list of inner functions. #69

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: spec-draft
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 91 additions & 12 deletions spec.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<title>Binary AST</title>
<pre class=metadata>
status: proposal
contributors: Shu-yu Guo, David Teller, Ecma International
contributors: Shu-yu Guo, Tooru Fujisawa, David Teller, Ecma International
</pre>
<emu-intro id="binast-intro">
<h1>Binary AST</h1>
Expand All @@ -26,7 +26,14 @@ <h1>Tree Grammar</h1>
<p>This section is derived from <a href="https://github.com/shapesecurity/shift-spec/blob/es2017/spec.idl">Shift AST Spec</a> and parts are Copyright 2014-2017 Shape Security, Inc.</p>
<p>Unlike the Shift AST spec, interface types are not used to inherit fields to control over ordering of fields. Type hierarchies that are not used to discriminate are collapsed to make space costs simple. Nor are they used to discriminate types, for which explicitly discriminated unions types are used.</p>
<emu-note>Whereas Shift AST's design principle is ease of search-and-replace of node types, binary AST's design principle is ease of verification and ease of associating different behaviors with syntactically different (but possibly lexically similar) productions.</emu-note>
<p>The grammar is presented in WebIDL with the `[TypeIndicator]` and `[NonEmpty]` extensions per Shift AST spec. The `[Lazy]` extension serves as a hint to the surface encoding that the `[Lazy]` attribute should be skippable in the byte stream in constant time. The `typedefs` of `or` types are to be read as recursive sum types. In text below, the "is a `Foo`" prose is shorthand for checking the node's `type` attribute being equal to `"Foo"`.</p>
<p>The grammar is presented in WebIDL with the `[TypeIndicator]` and `[NonEmpty]` extensions per Shift AST spec, as well as the listed extensions below. </p>
<ul>
<li>The `[Lazy]` extension serves as a hint to the surface encoding that the `[Lazy]` attribute must be skippable in the byte stream in constant time.</li>
<li>The `[Linkable]` extension serves as a hint to the surface encoding that the `[Linkable]` attribute must be random accessible in the byte stream in constant time.</li>
<li>The `NodeLink` type corresponds to the set of opaque values interpretable by the surface encoding to seek to attributes that have the `[Linkable]` extension.</li>
<li>The `typedefs` of `or` types are to be read as recursive sum types.</li>
<li>The prose "is a `Foo`" is shorthand for checking the node's `type` attribute being equal to `"Foo"`.</li>
</ul>

<pre><code class="language-webidl">
// Type aliases and enums.
Expand Down Expand Up @@ -193,6 +200,7 @@ <h1>Tree Grammar</h1>
EmptyStatement or
ExpressionStatement or
FunctionDeclaration or
LinkableFunctionDeclaration or
IfStatement or
IterationStatement or
LabelledStatement or
Expand All @@ -217,6 +225,7 @@ <h1>Tree Grammar</h1>
LiteralRegExpExpression or
ArrayExpression or
ArrowExpression or
LinkableArrowExpression or
AssignmentExpression or
BinaryExpression or
CallExpression or
Expand All @@ -225,6 +234,7 @@ <h1>Tree Grammar</h1>
ConditionalExpression or
ClassExpression or
FunctionExpression or
LinkableFunctionExpression or
IdentifierExpression or
NewExpression or
NewTargetExpression or
Expand All @@ -243,7 +253,12 @@ <h1>Tree Grammar</h1>
LiteralPropertyName)
PropertyName;

typedef (Method or Getter or Setter) MethodDefinition;
typedef (Method or
Getter or
Setter or
LinkableMethod or
LinkableGetter or
LinkableSetter) MethodDefinition;

typedef (MethodDefinition or
DataProperty or
Expand Down Expand Up @@ -462,14 +477,20 @@ <h1>Tree Grammar</h1>

// `export VariableStatement`, `export Declaration`
interface Export : Node {
attribute (FunctionDeclaration or ClassDeclaration or VariableDeclaration) declaration;
attribute (FunctionDeclaration or
LinkableFunctionDeclaration or
ClassDeclaration or
VariableDeclaration) declaration;
};

// `export default HoistableDeclaration`,
// `export default ClassDeclaration`,
// `export default AssignmentExpression`
interface ExportDefault : Node {
attribute (FunctionDeclaration or ClassDeclaration or Expression) body;
attribute (FunctionDeclaration or
LinkableFunctionDeclaration or
ClassDeclaration or
Expression) body;
};

// `ExportSpecifier`, as part of an `ExportFrom`.
Expand Down Expand Up @@ -513,8 +534,12 @@ <h1>Tree Grammar</h1>
// `length` property of this method.
attribute unsigned long length;
attribute FrozenArray&lt;Directive&gt; directives;
attribute FrozenArray&lt;NodeLink&gt; directChildFunctions;
[Lazy] attribute FunctionOrMethodContents contents;
};
interface LinkableMethod : Node {
[Linkable] attribute Method method;
};

// `get PropertyName ( ) { FunctionBody }`
interface EagerGetter : Node {
Expand All @@ -525,8 +550,12 @@ <h1>Tree Grammar</h1>
interface LazyGetter : Node {
attribute PropertyName name;
attribute FrozenArray&lt;Directive&gt; directives;
attribute FrozenArray&lt;NodeLink&gt; directChildFunctions;
[Lazy] attribute GetterContents contents;
};
interface LinkableGetter : Node {
[Linkable] Getter getter;
};

interface GetterContents : Node {
attribute boolean isThisCaptured;
Expand All @@ -547,8 +576,12 @@ <h1>Tree Grammar</h1>
// `length` property of this setter function.
attribute unsigned long length;
attribute FrozenArray&lt;Directive&gt; directives;
attribute FrozenArray&lt;NodeLink&gt; directChildFunctions;
[Lazy] attribute SetterContents contents;
};
interface LinkableSetter : Node {
[Linkable] attribute Setter setter;
};

interface SetterContents : Node {
attribute boolean isThisCaptured;
Expand Down Expand Up @@ -635,6 +668,7 @@ <h1>Tree Grammar</h1>
// `length` property of this arrow function.
attribute unsigned long length;
attribute FrozenArray&lt;Directive&gt; directives;
attribute FrozenArray&lt;NodeLink&gt; directChildFunctions;
[Lazy] attribute ArrowExpressionContentsWithFunctionBody contents;
};
interface EagerArrowExpressionWithExpression : Node {
Expand All @@ -649,8 +683,12 @@ <h1>Tree Grammar</h1>
attribute boolean isAsync;
// `length` property of this arrow function.
attribute unsigned long length;
attribute FrozenArray&lt;NodeLink&gt; directChildFunctions;
[Lazy] attribute ArrowExpressionContentsWithExpression contents;
};
interface LinkableArrowExpression : Node {
[Linkable] attribute ArrowExpression arrow;
};

interface ArrowExpressionContentsWithFunctionBody : Node {
attribute AssertedParameterScope parameterScope;
Expand Down Expand Up @@ -743,8 +781,12 @@ <h1>Tree Grammar</h1>
// `length` property of this function.
attribute unsigned long length;
attribute FrozenArray&lt;Directive&gt; directives;
attribute FrozenArray&lt;NodeLink&gt; directChildFunctions;
[Lazy] attribute FunctionExpressionContents contents;
};
interface LinkableFunctionExpression : Node {
[Linkable] attribute FunctionExpression fun;
};

interface FunctionExpressionContents : Node {
attribute boolean isFunctionNameCaptured;
Expand Down Expand Up @@ -1000,16 +1042,19 @@ <h1>Tree Grammar</h1>
attribute FrozenArray&lt;Directive&gt; directives;
attribute FunctionOrMethodContents contents;
};

interface LazyFunctionDeclaration : Node {
attribute boolean isAsync;
attribute boolean isGenerator;
attribute BindingIdentifier name;
// `length` property of this function.
attribute unsigned long length;
attribute FrozenArray&lt;Directive&gt; directives;
attribute FrozenArray&lt;NodeLink&gt; directChildFunctions;
[Lazy] attribute FunctionOrMethodContents contents;
};
interface LinkableFunctionDeclaration : Node {
[Linkable] attribute FunctionDeclaration fun;
}

interface FunctionOrMethodContents : Node {
attribute boolean isThisCaptured;
Expand Down Expand Up @@ -1096,7 +1141,7 @@ <h1>StatementListEcmaify ( _stmts_ )</h1>
1. Let _emptyStmt_ be |StatementListItem| : |EmptyStatement|.
1. Set _list_ to |StatementList| : _emptyStmt_.
1. For each _stmt_ in _stmts_, do
1. If _stmt_ is a `FunctionDeclaration`, then
1. If _stmt_ is a `FunctionDeclaration` or a `LinkableFunctionDeclaration`, then
1. Set _n_ to |HoistableDeclaration|: ? Ecmaify(_stmt_).
1. Set _n_ to be |Declaration| : _n_.
1. Else if _stmt_ an `ExpressionStatement` and _stmt_`.expression` is a `LiteralStringExpression`:
Expand Down Expand Up @@ -1307,7 +1352,7 @@ <h1>PrimaryExpressionEcmaify ( _e_ )</h1>
<emu-alg>
1. Assert: _e_ is an `Expression`, an `AssignmentTarget`, or an `AssignmentTargetWithInitializer`.
1. If _e_ is a `ThisExpression`, then return ? Ecmaify(_e_).
1. Else if _pn_ is an `IdentifierExpression`, a `Literal`, an `ArrayExpression`, an `ObjectExpression`, a `FunctionExpression`, a `ClassExpression`, a `LiteralRegExpExpression`, a `TemplateExpression`, an `AssignmentTargetIdentifier`, an `ArrayAssignmentExpression`, or an `ObjectAssignmentExpression`, then return |PrimaryExpression| : ? Ecmaify(_e_).
1. Else if _pn_ is an `IdentifierExpression`, a `Literal`, an `ArrayExpression`, an `ObjectExpression`, a `FunctionExpression`, a `LinkableFunctionExpression`, a `ClassExpression`, a `LiteralRegExpExpression`, a `TemplateExpression`, an `AssignmentTargetIdentifier`, an `ArrayAssignmentExpression`, or an `ObjectAssignmentExpression`, then return |PrimaryExpression| : ? Ecmaify(_e_).
1. Else,
1. Let _parenthesized_ be |ParenthesizedExpression| : <emu-t>(</emu-t> ? ExpressionEcmaify(_e_) <emu-t>)</emu-t>.
1. Return |PrimaryExpression| : _parenthesized_.
Expand Down Expand Up @@ -1501,7 +1546,7 @@ <h1>AssignmentExpressionEcmaify ( _e_ )</h1>
<emu-alg>
1. Assert: _e_ is an `Expression`, an `AssignmentTarget`, or an `AssignmentTargetWithInitializer`.
1. If _e_ is an `AssignmentExpression`, a `CompoundAssignmentExpression`, or an `AssignmentTargetWithInitializer`, then return ? Ecmaify(_e_).
1. Else if _e_ is a `YieldExpression` or an `ArrowExpression`, then return |AssignmentExpression| : ? Ecmaify(_e_).
1. Else if _e_ is a `YieldExpression`, an `ArrowExpression`, or a `LinkableArrowExpression`, then return |AssignmentExpression| : ? Ecmaify(_e_).
1. Else,
1. Let _n_ be ? ConditionalExpressionEcmaify(_e_).
1. Return |AssignmentExpression| : _n_.
Expand Down Expand Up @@ -2004,8 +2049,10 @@ <h1>EcmaifyLabelledStatement ( _labelled_ )</h1>
<emu-alg>
1. Assert _labelled_ is a `LabelledStatement`.
1. Let _body_ be an empty Parse Node.
1. If _labelled_`.body` is a `FunctionDeclaration`, then
1. If _labelled_`.body.isAsync` is true or _labelled_`.body.isGenerator` is *true*, then throw a *SyntaxError* exception.
1. If _labelled_`.body` is a `FunctionDeclaration` or a `LinkableFunctionDeclaration`, then
1. Let _funNode_ be _labelled_`.body`.
1. If _funNode_ is a `LinkableFunctionDeclaration`, then set _funNode_ to _funNode_`.fun`.
1. If _funNode_`.isAsync` is true or _funNode_`.isGenerator` is *true*, then throw a *SyntaxError* exception.
1. Set _body_ to ? Ecmaify(_labelled_`.body`).
1. Else set _body_ to ? StatementEcmaify(_labelled_`.body`).
1. Let _item_ be |LabelledItem| : _body_.
Expand Down Expand Up @@ -2785,6 +2832,7 @@ <h1>Ecmaify ( _node_ )</h1>
1. Else if _node_ is a `EmptyStatement`, then return ? EcmaifyEmptyStatement(_node_).
1. Else if _node_ is a `ExpressionStatement`, then return ? EcmaifyExpressionStatement(_node_).
1. Else if _node_ is a `FunctionDeclaration`, then return ? EcmaifyFunctionDeclaration(_node_).
1. Else if _node_ is a `LinkableFunctionDeclaration`, then return ? EcmaifyFunctionDeclaration(_node_`.fun`).
1. Else if _node_ is a `IfStatement`, then return ? EcmaifyIfStatement(_node_).
1. Else if _node_ is a `DoWhileStatement`, then return ? EcmaifyDoWhileStatement(_node_).
1. Else if _node_ is a `ForInStatement`, then return ? EcmaifyForInStatement(_node_).
Expand All @@ -2808,6 +2856,7 @@ <h1>Ecmaify ( _node_ )</h1>
1. Else if _node_ is a `LiteralRegExpExpression`, then return ? EcmaifyLiteralRegExpExpression(_node_).
1. Else if _node_ is a `ArrayExpression`, then return ? EcmaifyArrayExpression(_node_).
1. Else if _node_ is a `ArrowExpression`, then return ? EcmaifyArrowExpression(_node_).
1. Else if _node_ is a `LinkableArrowExpression`, then return ? EcmaifyArrowExpression(_node_`.arrow`).
1. Else if _node_ is a `AssignmentExpression`, then return ? EcmaifyAssignmentExpression(_node_).
1. Else if _node_ is a `BinaryExpression`, then return ? EcmaifyBinaryExpression(_node_).
1. Else if _node_ is a `CallExpression`, then return ? EcmaifyCallExpression(_node_).
Expand All @@ -2816,6 +2865,7 @@ <h1>Ecmaify ( _node_ )</h1>
1. Else if _node_ is a `ConditionalExpression`, then return ? EcmaifyConditionalExpression(_node_).
1. Else if _node_ is a `ClassExpression`, then return ? EcmaifyClassExpression(_node_).
1. Else if _node_ is a `FunctionExpression`, then return ? EcmaifyFunctionExpression(_node_).
1. Else if _node_ is a `LinkableFunctionExpression`, then return ? EcmaifyFunctionExpression(_node_`.fun`).
1. Else if _node_ is a `IdentifierExpression`, then return ? EcmaifyIdentifierExpression(_node_).
1. Else if _node_ is a `NewExpression`, then return ? EcmaifyNewExpression(_node_).
1. Else if _node_ is a `NewTargetExpression`, then return ? EcmaifyNewTargetExpression(_node_).
Expand All @@ -2832,8 +2882,11 @@ <h1>Ecmaify ( _node_ )</h1>
1. Else if _node_ is a `LiteralPropertyName`, then return ? EcmaifyLiteralPropertyName(_node_).
1. Else if _node_ is a `LiteralPropertyName`, then return ? EcmaifyLiteralPropertyName(_node_).
1. Else if _node_ is a `Method`, then return ? EcmaifyMethod(_node_).
1. Else if _node_ is a `LinkableMethod`, then return ? EcmaifyMethod(_node_`.method`).
1. Else if _node_ is a `Getter`, then return ? EcmaifyGetter(_node_).
1. Else if _node_ is a `LinkableGetter`, then return ? EcmaifyGetter(_node_`.getter`).
1. Else if _node_ is a `Setter`, then return ? EcmaifySetter(_node_).
1. Else if _node_ is a `LinkableSetter`, then return ? EcmaifySetter(_node_`.setter`).
1. Else if _node_ is a `DataProperty`, then return ? EcmaifyDataProperty(_node_).
1. Else if _node_ is a `ShorthandProperty`, then return ? EcmaifyShorthandProperty(_node_).
1. Else if _node_ is a `ExportAllFrom`, then return ? EcmaifyExportAllFrom(_node_).
Expand Down Expand Up @@ -3029,7 +3082,7 @@ <h1>CheckRestParameterName ( _expectedParams_, _restParameterName_ )</h1>
</emu-alg>
</emu-clause>

<emu-clause id="sec-checkboundnames" aoid="CheckBOundNames">
<emu-clause id="sec-checkboundnames" aoid="CheckBoundNames">
<h1>CheckBoundNames ( _expectedBound_, _actualBound_ )</h1>
<emu-alg>
1. Let _unseen_ be a new empty List.
Expand All @@ -3042,6 +3095,31 @@ <h1>CheckBoundNames ( _expectedBound_, _actualBound_ )</h1>
</emu-alg>
</emu-clause>

<emu-clause id="sec-checkdirectchildfunctions" aoid="CheckDirectChildFunctions">
<h1>CheckDirectChildFunctions ( _funcNode_ )</h1>
<emu-alg>
1. Assert: _funcNode_ is a `LazyFunctionDeclaration`, a `LazyFunctionExpression`, a `LazyMethod`, a `LazyGetter`, a `LazySetter`, or a `LazyArrowExpression`.
1. NOTE: All asserted direct child functions (i.e. not nested within another inner function) in _funcNode_`.directChildFunctions` must be found. It is not required that all direct child functions be in _funcNode_`.directChildFunctions`.
1. Let _directChildFunctions_ be a new empty List.
1. For each _link_ in _funcNode_`.directChildFunctions`, do
1. Let _linkedNode_ be the node linked to by _link_.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't we assert that the linkedNode is direct inner function of funcNode?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like assert that it is not nested inside another inner function? That's supposed to be taken care of by the very step: if innerFunctions isn't empty, then we throw.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it mean that such link is allowed per file format?
and even if such link appears in the file, we should check if each entry has corresponding functions and throw SyntaxError for it if there's not?

Copy link

@efaust efaust Dec 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll certainly know when delazifying if there's a Linkable* that doesn't exist, and we can throw then, before exer executing anything.

Is there something in particular that has you worried?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking about the order of the error.
like, if there's 2 possible errors, which one should be observed?

maybe I should revisit this (and some other errors) once the surface encoding format is fixed.

1. NOTE: The above step is up to the surface encoding.
1. If _linkedNode_ is a `LinkableFunctionDeclaration`, then set _linkedNode_ to _linkedNode_`.fun`.
1. Else if _linkedNode_ is a `LinkableFunctionExpression`, then set _linkedNode_ to _linkedNode_`.fun`.
1. Else if _linkedNode_ is a `LinkableMethod`, then set _linkedNode_ to _linkedNode_`.method`.
1. Else if _linkedNode_ is a `LinkableGetter`, then set _linkedNode_ to _linkedNode_`.getter`.
1. Else if _linkedNode_ is a `LinkableSetter`, then set _linkedNode_ to _linkedNode_`.setter`.
1. Else,
1. Assert: _linkedNode_ is a `LinkableArrowExpression`.
1. Set _linkedNode_ to _linkedNode_`.arrow`.
1. Add _linkedNode_ as the last element of _directChildFunctions_.
1. For each `FunctionDeclaration`, `FunctionExpression`, `Method`, `Getter`, `Setter`, or `ArrowExpression` _candidateChildFunction_ contained in _funcNode_, do
1. If the the path from _funcNode_ to _candidateChildFunction_ contains no other `FunctionDeclaration`, `FunctionExpression`, `Method`, `Getter`, `Setter`, or `ArrowExpression`, then
1. If _candidateChildFunction_ is in _directChildFunctions_, then remove _candidateChildFunction_ from _directChildFunctions_.
1. If _directChildFunctions_ is not empty, then throw a *SyntaxError* exception.
</emu-alg>
</emu-clause>

<emu-clause id="sec-checkassertedscope" aoid="CheckAssertedScope">
<h1>CheckAssertedScope ( _scope_ , _parseTree_ )</h1>
<emu-alg>
Expand Down Expand Up @@ -3609,6 +3687,7 @@ <h1>Runtime Semantics: Delazify</h1>
1. Let _delazifiedBody_ be ? EcmaifyFunctionBody(_funcNode_).
1. Perform ? ValidateAndUpdateFunctionObject(_funcNode_, _functionObject_, _delazifiedBody_, _delazifiedParams_).
1. If _funcNode_ is not a `LazyGetter`, then perform ? CheckAssertedScope(_contents_`.parameterScope`, _delazifiedParams_).
1. Perform ? CheckDirectChildFunctions(_funcNode_).
1. Perform ? CheckAssertedScope(_contents_`.bodyScope`, _delazifiedBody_).
1. If _funcNode_ is a `LazyArrowExpression`, then
1. Perform ? CheckThisCapture(_contents_`.parameterScope`, _delazifiedParams_).
Expand Down