Skip to content

Change static fields emits #43114

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

Merged
merged 41 commits into from
Jun 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
b77edd8
use emit into iife
Kingwl Mar 6, 2021
061f916
Update emit
Kingwl Mar 6, 2021
3a6f8da
Revert un-related changes
Kingwl Mar 6, 2021
77a43f7
Allow super in static context
Kingwl Mar 6, 2021
bbf99ea
Allow this and super in static property declaration
Kingwl Mar 6, 2021
9c732ee
Add more tests
Kingwl Mar 6, 2021
f70cbfd
Avoid errors
Kingwl Mar 6, 2021
3ce5cd4
Accept baseline
Kingwl Mar 6, 2021
006ec9f
Accept baseline
Kingwl Mar 7, 2021
be8ab04
Merge branch 'master' into change_static_fields_emits
Kingwl Mar 22, 2021
a5824ea
Add decorated classes test
Kingwl Mar 22, 2021
5309eb6
Merge branch 'master' into change_static_fields_emits
Kingwl Mar 25, 2021
0f807ee
Add errors
Kingwl Mar 25, 2021
23ebf10
Avoid this in emitter
Kingwl Mar 25, 2021
fbb617e
make lint happy
Kingwl Mar 25, 2021
40af136
Add class expression tests
Kingwl Mar 25, 2021
2630d07
Add computed name test
Kingwl Mar 25, 2021
f5299db
Avoid super if target below es6
Kingwl Mar 26, 2021
52b53cd
Adjust function boundary
Kingwl Mar 26, 2021
d3bb4fa
Add internal
Kingwl Mar 26, 2021
325d648
Merge branch 'master' into change_static_fields_emits
Kingwl Mar 27, 2021
f605ad7
Fix minor CR issues
Kingwl Mar 30, 2021
55908df
Merge branch 'master' into change_static_fields_emits
Kingwl Apr 13, 2021
3513104
accept baseline
Kingwl Apr 13, 2021
df59243
Update behavior
Kingwl Apr 14, 2021
39c2fcb
Avoid spaces
Kingwl Apr 14, 2021
38a44b6
Make lint happy
Kingwl Apr 14, 2021
73c9bac
Avoid function boundary utils
Kingwl Apr 14, 2021
0870370
Update baseline
Kingwl Apr 14, 2021
3de52b3
Avoid errors
Kingwl Apr 14, 2021
9fe7a7b
Merge branch 'master' into change_static_fields_emits
Kingwl Apr 14, 2021
e8cd8ea
Accept baseline
Kingwl Apr 14, 2021
4e7cd7a
Accept baseline
Kingwl Apr 14, 2021
1f8d980
Merge branch 'main' into change_static_fields_emits
Kingwl Jun 11, 2021
37f00f9
Accept baseline
Kingwl Jun 11, 2021
2e95d7d
Merge branch 'main' into change_static_fields_emits
Kingwl Jun 22, 2021
32bd0f1
Accept baseline
Kingwl Jun 22, 2021
67d1ca9
Use substitutions
Kingwl Jun 22, 2021
f43b5e7
Full coverage for super, this, merge static and private context
rbuckton Jun 25, 2021
abaf262
Merge branch 'main' into change_static_fields_emits
rbuckton Jun 25, 2021
9850dc3
Fix use-before-def in static fields
rbuckton Jun 25, 2021
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
14 changes: 7 additions & 7 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ namespace ts {
}
// We create a return control flow graph for IIFEs and constructors. For constructors
// we use the return control flow graph in strict property initialization checks.
currentReturnTarget = isIIFE || node.kind === SyntaxKind.Constructor || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression)) ? createBranchLabel() : undefined;
currentReturnTarget = isIIFE || node.kind === SyntaxKind.Constructor || node.kind === SyntaxKind.ClassStaticBlockDeclaration || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression)) ? createBranchLabel() : undefined;
currentExceptionTarget = undefined;
currentBreakTarget = undefined;
currentContinueTarget = undefined;
Expand All @@ -678,10 +678,10 @@ namespace ts {
bindChildren(node);
// Reset all reachability check related flags on node (for incremental scenarios)
node.flags &= ~NodeFlags.ReachabilityAndEmitFlags;
if (!(currentFlow.flags & FlowFlags.Unreachable) && containerFlags & ContainerFlags.IsFunctionLike && nodeIsPresent((node as FunctionLikeDeclaration).body)) {
if (!(currentFlow.flags & FlowFlags.Unreachable) && containerFlags & ContainerFlags.IsFunctionLike && nodeIsPresent((node as FunctionLikeDeclaration | ClassStaticBlockDeclaration).body)) {
node.flags |= NodeFlags.HasImplicitReturn;
if (hasExplicitReturn) node.flags |= NodeFlags.HasExplicitReturn;
(node as FunctionLikeDeclaration).endFlowNode = currentFlow;
(node as FunctionLikeDeclaration | ClassStaticBlockDeclaration).endFlowNode = currentFlow;
}
if (node.kind === SyntaxKind.SourceFile) {
node.flags |= emitFlags;
Expand All @@ -691,8 +691,8 @@ namespace ts {
if (currentReturnTarget) {
addAntecedent(currentReturnTarget, currentFlow);
currentFlow = finishFlowLabel(currentReturnTarget);
if (node.kind === SyntaxKind.Constructor || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression))) {
(node as FunctionLikeDeclaration).returnFlowNode = currentFlow;
if (node.kind === SyntaxKind.Constructor || node.kind === SyntaxKind.ClassStaticBlockDeclaration || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression))) {
(node as FunctionLikeDeclaration | ClassStaticBlockDeclaration).returnFlowNode = currentFlow;
}
}
if (!isIIFE) {
Expand Down Expand Up @@ -1944,7 +1944,7 @@ namespace ts {
}

function declareClassMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
return hasSyntacticModifier(node, ModifierFlags.Static)
return isStatic(node)
? declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes)
: declareSymbol(container.symbol.members!, container.symbol, node, symbolFlags, symbolExcludes);
}
Expand Down Expand Up @@ -2950,7 +2950,7 @@ namespace ts {
// this.foo assignment in a JavaScript class
// Bind this property to the containing class
const containingClass = thisContainer.parent;
const symbolTable = hasSyntacticModifier(thisContainer, ModifierFlags.Static) ? containingClass.symbol.exports! : containingClass.symbol.members!;
const symbolTable = isStatic(thisContainer) ? containingClass.symbol.exports! : containingClass.symbol.members!;
if (hasDynamicName(node)) {
bindDynamicallyNamedThisPropertyAssignment(node, containingClass.symbol, symbolTable);
}
Expand Down
211 changes: 136 additions & 75 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -3344,6 +3344,14 @@
"category": "Error",
"code": 2815
},
"Cannot use 'this' in a static property initializer of a decorated class.": {
"category": "Error",
"code": 2816
},
"Property '{0}' has no initializer and is not definitely assigned in a class static block.": {
"category": "Error",
"code": 2817
},

"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
Expand Down
54 changes: 52 additions & 2 deletions src/compiler/factory/nodeFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -496,8 +496,11 @@ namespace ts {
createArraySliceCall,
createArrayConcatCall,
createObjectDefinePropertyCall,
createReflectGetCall,
createReflectSetCall,
createPropertyDescriptor,
createCallBinding,
createAssignmentTargetWrapper,

// Utilities
inlineExpressions,
Expand Down Expand Up @@ -998,8 +1001,10 @@ namespace ts {
case SyntaxKind.UndefinedKeyword: // `undefined` is an Identifier in the expression case.
transformFlags = TransformFlags.ContainsTypeScript;
break;
case SyntaxKind.StaticKeyword:
case SyntaxKind.SuperKeyword:
transformFlags = TransformFlags.ContainsES2015 | TransformFlags.ContainsLexicalSuper;
break;
case SyntaxKind.StaticKeyword:
transformFlags = TransformFlags.ContainsES2015;
break;
case SyntaxKind.ThisKeyword:
Expand Down Expand Up @@ -2624,7 +2629,7 @@ namespace ts {
propagateChildFlags(node.equalsGreaterThanToken) |
TransformFlags.ContainsES2015;
if (modifiersToFlags(node.modifiers) & ModifierFlags.Async) {
node.transformFlags |= TransformFlags.ContainsES2017;
node.transformFlags |= TransformFlags.ContainsES2017 | TransformFlags.ContainsLexicalThis;
}
return node;
}
Expand Down Expand Up @@ -5435,6 +5440,15 @@ namespace ts {
}

function createMethodCall(object: Expression, methodName: string | Identifier, argumentsList: readonly Expression[]) {
// Preserve the optionality of `object`.
if (isCallChain(object)) {
return createCallChain(
createPropertyAccessChain(object, /*questionDotToken*/ undefined, methodName),
/*questionDotToken*/ undefined,
/*typeArguments*/ undefined,
argumentsList
);
}
return createCallExpression(
createPropertyAccessExpression(object, methodName),
/*typeArguments*/ undefined,
Expand Down Expand Up @@ -5470,6 +5484,14 @@ namespace ts {
return createGlobalMethodCall("Object", "defineProperty", [target, asExpression(propertyName), attributes]);
}

function createReflectGetCall(target: Expression, propertyKey: Expression, receiver?: Expression): CallExpression {
return createGlobalMethodCall("Reflect", "get", receiver ? [target, propertyKey, receiver] : [target, propertyKey]);
}

function createReflectSetCall(target: Expression, propertyKey: Expression, value: Expression, receiver?: Expression): CallExpression {
return createGlobalMethodCall("Reflect", "set", receiver ? [target, propertyKey, value, receiver] : [target, propertyKey, value]);
}

function tryAddPropertyAssignment(properties: Push<PropertyAssignment>, propertyName: string, expression: Expression | undefined) {
if (expression) {
properties.push(createPropertyAssignment(propertyName, expression));
Expand Down Expand Up @@ -5645,6 +5667,34 @@ namespace ts {
return { target, thisArg };
}

function createAssignmentTargetWrapper(paramName: Identifier, expression: Expression): LeftHandSideExpression {
return createPropertyAccessExpression(
// Explicit parens required because of v8 regression (https://bugs.chromium.org/p/v8/issues/detail?id=9560)
createParenthesizedExpression(
createObjectLiteralExpression([
createSetAccessorDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,
"value",
[createParameterDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,
/*dotDotDotToken*/ undefined,
paramName,
/*questionToken*/ undefined,
/*type*/ undefined,
/*initializer*/ undefined
)],
createBlock([
createExpressionStatement(expression)
])
)
])
),
"value"
);
}

function inlineExpressions(expressions: readonly Expression[]) {
// Avoid deeply nested comma expressions as traversing them during emit can result in "Maximum call
// stack size exceeded" errors.
Expand Down
61 changes: 61 additions & 0 deletions src/compiler/factory/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,67 @@ namespace ts {
}
}

/**
* Expand the read and increment/decrement operations a pre- or post-increment or pre- or post-decrement expression.
*
* ```ts
* // input
* <expression>++
* // output (if result is not discarded)
* var <temp>;
* (<temp> = <expression>, <resultVariable> = <temp>++, <temp>)
* // output (if result is discarded)
* var <temp>;
* (<temp> = <expression>, <temp>++, <temp>)
*
* // input
* ++<expression>
* // output (if result is not discarded)
* var <temp>;
* (<temp> = <expression>, <resultVariable> = ++<temp>)
* // output (if result is discarded)
* var <temp>;
* (<temp> = <expression>, ++<temp>)
* ```
*
* It is up to the caller to supply a temporary variable for `<resultVariable>` if one is needed.
* The temporary variable `<temp>` is injected so that `++` and `--` work uniformly with `number` and `bigint`.
* The result of the expression is always the final result of incrementing or decrementing the expression, so that it can be used for storage.
*
* @param factory {@link NodeFactory} used to create the expanded representation.
* @param node The original prefix or postfix unary node.
* @param expression The expression to use as the value to increment or decrement
* @param resultVariable A temporary variable in which to store the result. Pass `undefined` if the result is discarded, or if the value of `<temp>` is the expected result.
*/
export function expandPreOrPostfixIncrementOrDecrementExpression(factory: NodeFactory, node: PrefixUnaryExpression | PostfixUnaryExpression, expression: Expression, recordTempVariable: (node: Identifier) => void, resultVariable: Identifier | undefined) {
const operator = node.operator;
Debug.assert(operator === SyntaxKind.PlusPlusToken || operator === SyntaxKind.MinusMinusToken, "Expected 'node' to be a pre- or post-increment or pre- or post-decrement expression");

const temp = factory.createTempVariable(recordTempVariable);
expression = factory.createAssignment(temp, expression);
setTextRange(expression, node.operand);

let operation: Expression = isPrefixUnaryExpression(node) ?
factory.createPrefixUnaryExpression(operator, temp) :
factory.createPostfixUnaryExpression(temp, operator);
setTextRange(operation, node);

if (resultVariable) {
operation = factory.createAssignment(resultVariable, operation);
setTextRange(operation, node);
}

expression = factory.createComma(expression, operation);
setTextRange(expression, node);

if (isPostfixUnaryExpression(node)) {
expression = factory.createComma(expression, temp);
setTextRange(expression, node);
}

return expression;
}

/**
* Gets whether an identifier should only be referred to by its internal name.
*/
Expand Down
Loading