-
Notifications
You must be signed in to change notification settings - Fork 904
Implement ES2017 (async) transforms #2853
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
Changes from all commits
6f7ff97
2b0c69a
e5dcdba
0e042bb
c843586
722d2bc
3aa19f3
323adeb
9bdb2e2
45a7771
ae03692
be175d5
c3d1b6b
d76ae7d
912f1f6
3ce161d
159e36b
3353611
9a537d5
d51c6ac
ba421b4
e0ebc88
3e1546d
0c3404b
c5124d4
33739ac
f73dddb
f96bdea
663dc5a
f9ae2d5
7e0a203
0e0604e
7aef41c
b1a083a
aa65589
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package ast | ||
|
|
||
| type FunctionFlags uint32 | ||
|
|
||
| const ( | ||
| FunctionFlagsNormal FunctionFlags = 0 | ||
| FunctionFlagsGenerator FunctionFlags = 1 << 0 | ||
| FunctionFlagsAsync FunctionFlags = 1 << 1 | ||
| FunctionFlagsInvalid FunctionFlags = 1 << 2 | ||
| FunctionFlagsAsyncGenerator FunctionFlags = FunctionFlagsAsync | FunctionFlagsGenerator | ||
| ) | ||
|
|
||
| func GetFunctionFlags(node *Node) FunctionFlags { | ||
| if node == nil { | ||
| return FunctionFlagsInvalid | ||
| } | ||
| data := node.BodyData() | ||
| if data == nil { | ||
| return FunctionFlagsInvalid | ||
| } | ||
| flags := FunctionFlagsNormal | ||
| switch node.Kind { | ||
| case KindFunctionDeclaration, KindFunctionExpression, KindMethodDeclaration: | ||
| if data.AsteriskToken != nil { | ||
| flags |= FunctionFlagsGenerator | ||
| } | ||
| fallthrough | ||
| case KindArrowFunction: | ||
| if HasSyntacticModifier(node, ModifierFlagsAsync) { | ||
| flags |= FunctionFlagsAsync | ||
| } | ||
| } | ||
| if data.Body == nil { | ||
| flags |= FunctionFlagsInvalid | ||
| } | ||
| return flags | ||
| } |
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -280,6 +280,24 @@ func (f *NodeFactory) InlineExpressions(expressions []*ast.Expression) *ast.Expr | |
| // Utilities | ||
| // | ||
|
|
||
| func (f *NodeFactory) CreateExpressionFromEntityName(node *ast.Node) *ast.Expression { | ||
| if ast.IsQualifiedName(node) { | ||
| left := f.CreateExpressionFromEntityName(node.AsQualifiedName().Left) | ||
| right := node.AsQualifiedName().Right.Clone(f.AsNodeFactory()) | ||
| right.Loc = node.AsQualifiedName().Right.Loc | ||
| // TODO(rbuckton): Does this need to be parented? | ||
| right.Parent = node.AsQualifiedName().Right.Parent | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This parent setting was TODO'd with a "does this need to be done" before it was moved. My question: does it? Most AST construction in the transformers doesn't need
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm going to say no. The original Strada code was flipped the other way with the opposite TODO: const left = createExpressionFromEntityName(factory, node.left);
// TODO(rbuckton): Does this need to be parented?
const right = setParent(setTextRange(factory.cloneNode(node.right), node.right), node.right.parent);
return setTextRange(factory.createPropertyAccessExpression(left, right), node);I'll delete it here once and for all.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Er, sorry, not flipped, it also set parent.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just tested, and while this first one works when unparented, the one below (plain clone) does not and causes: --- old.unicodeEscapesInJsxtags.js
+++ new.unicodeEscapesInJsxtags.js
@@= skipped -38, +38 lines =@@
React.createElement("a", null);
React.createElement("a-b", null);
React.createElement("a-c", null);
-React.createElement(Comp\u0061, { x: 12 });
+React.createElement(Compa, { x: 12 });
React.createElement(x.\u0076ideo, null);
React.createElement("a", null);
React.createElement("a-b", null);
React.createElement("a-c", null);
-React.createElement(Comp\u{0061}, { x: 12 });
+React.createElement(Compa, { x: 12 });
React.createElement("video", { "data-video": true });
React.createElement("video", { \u0073rc: "" });
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I restored the comments for now.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, the parent pointers being set causes it to go back to the source file for text vs using the parsed text stored in the identifier. Kinda just highlights information we're not properly storing in the AST.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah. This to me means something is wrong, but there's not a great way to ban all parent accesses during transform... |
||
| propAccess := f.NewPropertyAccessExpression(left, nil, right, ast.NodeFlagsNone) | ||
| propAccess.Loc = node.Loc | ||
| return propAccess | ||
| } | ||
| res := node.Clone(f.AsNodeFactory()) | ||
| res.Loc = node.Loc | ||
| // TODO(rbuckton): Does this need to be parented? | ||
| res.Parent = node.Parent | ||
| return res | ||
| } | ||
|
|
||
| func (f *NodeFactory) NewTypeCheck(value *ast.Node, tag string) *ast.Node { | ||
| if tag == "null" { | ||
| return f.NewStrictEqualityExpression(value, f.NewKeywordExpression(ast.KindNullKeyword)) | ||
|
|
@@ -650,6 +668,64 @@ func (f *NodeFactory) NewRestHelper(value *ast.Expression, elements []*ast.Node, | |
|
|
||
| // !!! ES2017 Helpers | ||
|
|
||
| // Allocates a new Call expression to the `__awaiter` helper. | ||
| func (f *NodeFactory) NewAwaiterHelper( | ||
| hasLexicalThis bool, | ||
| argumentsExpression *ast.Expression, | ||
| parameters *ast.NodeList, | ||
| body *ast.BlockNode, | ||
| ) *ast.Expression { | ||
| f.emitContext.RequestEmitHelper(awaiterHelper) | ||
|
|
||
| var params *ast.NodeList | ||
| if parameters != nil { | ||
| params = parameters | ||
| } else { | ||
| params = f.NewNodeList([]*ast.Node{}) | ||
| } | ||
|
|
||
| generatorFunc := f.NewFunctionExpression( | ||
| nil, /*modifiers*/ | ||
| f.NewToken(ast.KindAsteriskToken), | ||
| nil, /*name*/ | ||
| nil, /*typeParameters*/ | ||
| params, | ||
| nil, /*returnType*/ | ||
| nil, /*fullSignature*/ | ||
| body, | ||
| ) | ||
|
|
||
| // Mark this node as originally an async function body | ||
| f.emitContext.AddEmitFlags(generatorFunc, EFAsyncFunctionBody|EFReuseTempVariableScope) | ||
|
|
||
| var thisArg *ast.Expression | ||
| if hasLexicalThis { | ||
| thisArg = f.NewKeywordExpression(ast.KindThisKeyword) | ||
| } else { | ||
| thisArg = f.NewVoidZeroExpression() | ||
| } | ||
|
|
||
| var argsArg *ast.Expression | ||
| if argumentsExpression != nil { | ||
| argsArg = argumentsExpression | ||
| } else { | ||
| argsArg = f.NewVoidZeroExpression() | ||
| } | ||
|
|
||
| return f.NewCallExpression( | ||
| f.NewUnscopedHelperName("__awaiter"), | ||
| nil, /*questionDotToken*/ | ||
| nil, /*typeArguments*/ | ||
| f.NewNodeList([]*ast.Expression{ | ||
| thisArg, | ||
| argsArg, | ||
| f.NewVoidZeroExpression(), | ||
|
jakebailey marked this conversation as resolved.
|
||
| generatorFunc, | ||
| }), | ||
| ast.NodeFlagsNone, | ||
| ) | ||
| } | ||
|
|
||
| // ES2015 Helpers | ||
|
|
||
| func (f *NodeFactory) NewPropKeyHelper(expr *ast.Expression) *ast.Expression { | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -176,6 +176,9 @@ func NewPrinter(options PrinterOptions, handlers PrintHandlers, emitContext *Emi | |||||||||
| printer.nameGenerator.Context = printer.emitContext | ||||||||||
| printer.nameGenerator.GetTextOfNode = func(node *ast.Node) string { return printer.getTextOfNode(node, false) } | ||||||||||
| printer.nameGenerator.IsFileLevelUniqueNameInCurrentFile = printer.isFileLevelUniqueNameInCurrentFile | ||||||||||
| printer.makeFileLevelOptimisticUniqueName = func(name string) string { | ||||||||||
| return printer.nameGenerator.MakeFileLevelOptimisticUniqueName(name) | ||||||||||
| } | ||||||||||
|
Comment on lines
+179
to
+181
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this not just
Suggested change
? But moreover, why is it not just a method on
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's used like: p.writeLines(helper.TextCallback(p.makeFileLevelOptimisticUniqueName))So will be rebound to |
||||||||||
| printer.containerPos = -1 | ||||||||||
| printer.containerEnd = -1 | ||||||||||
| printer.declarationListContainerEnd = -1 | ||||||||||
|
|
@@ -1502,7 +1505,14 @@ func canEmitSimpleArrowHead(parentNode *ast.Node, parameters *ast.ParameterList) | |||||||||
| parent := parentNode.AsArrowFunction() | ||||||||||
| parameter := parameters.Nodes[0].AsParameterDeclaration() | ||||||||||
|
|
||||||||||
| return parameter.Pos() == greatestEnd(parent.Pos(), parent.Modifiers()) && // may not have parsed tokens between modifiers/start of parent and parameter | ||||||||||
| // Only use modifiers for position check if they actually contain nodes. | ||||||||||
| // After transformation (e.g., async removal), modifiers may be an empty list with stale source positions. | ||||||||||
| modifiers := parent.Modifiers() | ||||||||||
| if modifiers != nil && len(modifiers.Nodes) == 0 { | ||||||||||
| modifiers = nil | ||||||||||
| } | ||||||||||
|
|
||||||||||
| return parameter.Pos() == greatestEnd(parent.Pos(), modifiers) && // may not have parsed tokens between modifiers/start of parent and parameter | ||||||||||
| parent.TypeParameters == nil && // parent may not have type parameters | ||||||||||
| parent.Type == nil && // parent may not have return type annotation | ||||||||||
| !parameters.HasTrailingComma() && // parameters may not have a trailing comma | ||||||||||
|
|
@@ -2715,6 +2725,7 @@ func (p *Printer) getBinaryExpressionPrecedence(node *ast.BinaryExpression) (lef | |||||||||
| case ast.OperatorPrecedenceAssignment: | ||||||||||
| // assignment is right-associative | ||||||||||
| leftPrec = ast.OperatorPrecedenceLeftHandSide | ||||||||||
| rightPrec = ast.OperatorPrecedenceYield | ||||||||||
|
jakebailey marked this conversation as resolved.
|
||||||||||
| case ast.OperatorPrecedenceLogicalOR: | ||||||||||
| rightPrec = ast.OperatorPrecedenceLogicalAND | ||||||||||
| case ast.OperatorPrecedenceLogicalAND: | ||||||||||
|
|
||||||||||
Uh oh!
There was an error while loading. Please reload this page.