Skip to content

Commit 4f4b578

Browse files
committed
[GR-31574] Implement class static initialization blocks proposal.
PullRequest: js/2138
2 parents 173140a + a24c3b9 commit 4f4b578

File tree

14 files changed

+151
-79
lines changed

14 files changed

+151
-79
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ The main focus is on user-observable behavior of the engine.
1212
* Added support for `dayPeriod` and `fractionalSecondDigits` options of `Intl.DateTimeFormat`.
1313
* Changed foreign hash map access using `map[key]` syntax to convert the key to a string or symbol.
1414
* Implemented `Object.hasOwn` ([Accessible Object.hasOwnProperty](https://github.com/tc39/proposal-accessible-object-hasownproperty) proposal). It is available in ECMAScript 2022 (`--js.ecmascript-version=2022`).
15+
* Implemented [class static initialization blocks](https://github.com/tc39/proposal-class-static-block) proposal. It is available in ECMAScript 2022 (`--js.ecmascript-version=2022`).
1516
* Added experimental Polyglot `Context` option `--js.esm-eval-returns-exports`. When enabled, `eval()` of an ES module will return a Polyglot `Value` containing the ES module exported namespace object. The option is disabled by default.
1617

1718
## Version 21.2.0

graal-js/src/com.oracle.js.parser/src/com/oracle/js/parser/Parser.java

+57-8
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,13 @@ private boolean isES2021() {
855855
return env.ecmaScriptVersion >= 12;
856856
}
857857

858+
/**
859+
* ES2022 or newer.
860+
*/
861+
private boolean isES2022() {
862+
return env.ecmaScriptVersion >= 13;
863+
}
864+
858865
private boolean isClassFields() {
859866
return ES2020_CLASS_FIELDS && env.classFields;
860867
}
@@ -1602,7 +1609,7 @@ private ClassNode classTail(int classLineNumber, long classToken, IdentNode clas
16021609
ArrayList<PropertyNode> classElements = new ArrayList<>();
16031610
Map<String, Integer> privateNameToAccessorIndexMap = new HashMap<>();
16041611
int instanceFieldCount = 0;
1605-
int staticFieldCount = 0;
1612+
int staticElementCount = 0;
16061613
boolean hasPrivateMethods = false;
16071614
boolean hasPrivateInstanceMethods = false;
16081615
for (;;) {
@@ -1618,7 +1625,17 @@ private ClassNode classTail(int classLineNumber, long classToken, IdentNode clas
16181625
TokenType nextToken = lookahead();
16191626
if (nextToken != LPAREN && nextToken != ASSIGN && nextToken != SEMICOLON && nextToken != RBRACE) {
16201627
isStatic = true;
1628+
int staticLine = line;
1629+
long staticToken = token;
16211630
next();
1631+
1632+
if (type == LBRACE && isES2022()) {
1633+
// static initialization block
1634+
PropertyNode staticInit = staticInitializer(staticLine, staticToken);
1635+
staticElementCount++;
1636+
classElements.add(staticInit);
1637+
continue;
1638+
}
16221639
} // else method/field named 'static'
16231640
}
16241641
long classElementToken = token;
@@ -1642,7 +1659,7 @@ private ClassNode classTail(int classLineNumber, long classToken, IdentNode clas
16421659
if (!generator && !async && isClassFieldDefinition(nameTokenType)) {
16431660
classElement = fieldDefinition(classElementName, isStatic, classElementToken, computed);
16441661
if (isStatic) {
1645-
staticFieldCount++;
1662+
staticElementCount++;
16461663
} else {
16471664
instanceFieldCount++;
16481665
}
@@ -1733,7 +1750,7 @@ private ClassNode classTail(int classLineNumber, long classToken, IdentNode clas
17331750

17341751
classScope.close();
17351752
return new ClassNode(classToken, classFinish, className, classHeritage, constructor, classElements, classScope,
1736-
instanceFieldCount, staticFieldCount, hasPrivateMethods, hasPrivateInstanceMethods);
1753+
instanceFieldCount, staticElementCount, hasPrivateMethods, hasPrivateInstanceMethods);
17371754
} finally {
17381755
lc.pop(classNode);
17391756
}
@@ -1986,6 +2003,32 @@ private Pair<FunctionNode, Boolean> fieldInitializer(int lineNumber, long fieldT
19862003
return Pair.create(createFunctionNode(function, fieldToken, null, lineNumber, bodyBlock), isAnonymousFunctionDefinition);
19872004
}
19882005

2006+
/**
2007+
* Parse <code>{ ClassStaticBlockBody }</code>.
2008+
*/
2009+
private PropertyNode staticInitializer(int lineNumber, long staticToken) {
2010+
assert type == LBRACE;
2011+
int functionFlags = FunctionNode.IS_METHOD | FunctionNode.IS_CLASS_FIELD_INITIALIZER | FunctionNode.IS_ANONYMOUS;
2012+
ParserContextFunctionNode function = createParserContextFunctionNode(null, staticToken, functionFlags, lineNumber, Collections.emptyList(), 0);
2013+
function.setInternalName(INITIALIZER_FUNCTION_NAME);
2014+
lc.push(function);
2015+
Block bodyBlock;
2016+
try {
2017+
bodyBlock = functionBody(function);
2018+
} finally {
2019+
lc.pop(function);
2020+
}
2021+
2022+
// It is a Syntax Error if ContainsArguments of Initializer is true.
2023+
assert function.getFlag(FunctionNode.USES_ARGUMENTS) == 0;
2024+
2025+
// currently required for all nested functions.
2026+
lc.setCurrentFunctionFlag(FunctionNode.HAS_CLOSURES);
2027+
2028+
FunctionNode functionNode = createFunctionNode(function, staticToken, null, lineNumber, bodyBlock);
2029+
return new PropertyNode(staticToken, finish, null, functionNode, null, null, true, false, false, false, false, false);
2030+
}
2031+
19892032
private boolean isPropertyName(long currentToken) {
19902033
TokenType currentType = Token.descType(currentToken);
19912034
if (ES6_COMPUTED_PROPERTY_NAME && currentType == LBRACKET && isES6()) {
@@ -3130,7 +3173,8 @@ private void breakStatement(boolean yield, boolean await) {
31303173
*/
31313174
private void returnStatement(boolean yield, boolean await) {
31323175
// check for return outside function
3133-
if (lc.getCurrentFunction().isScriptOrModule()) {
3176+
ParserContextFunctionNode currentFunction = lc.getCurrentFunction();
3177+
if (currentFunction.isScriptOrModule() || currentFunction.isClassStaticBlock()) {
31343178
throw error(AbstractParser.message("invalid.return"));
31353179
}
31363180

@@ -3232,15 +3276,20 @@ private Expression awaitExpression(boolean yield) {
32323276
// Capture await token.
32333277
long awaitToken = token;
32343278

3279+
ParserContextFunctionNode currentFunction = lc.getCurrentFunction();
3280+
if (currentFunction.isClassStaticBlock()) {
3281+
throw error(AbstractParser.message("unexpected.token", type.getNameOrType()));
3282+
}
3283+
32353284
recordYieldOrAwait();
32363285

32373286
nextOrEOL();
32383287

32393288
Expression expression = unaryExpression(yield, true);
32403289

3241-
if (isModule && lc.getCurrentFunction().isModule()) {
3290+
if (isModule && currentFunction.isModule()) {
32423291
// Top-level await module: mark the body of the module as async.
3243-
lc.getCurrentFunction().setFlag(FunctionNode.IS_ASYNC);
3292+
currentFunction.setFlag(FunctionNode.IS_ASYNC);
32443293
}
32453294
// Construct and add AWAIT node.
32463295
return new UnaryNode(Token.recast(awaitToken, AWAIT), expression);
@@ -4236,7 +4285,7 @@ private PropertyFunction propertySetterFunction(long getSetToken, int functionLi
42364285
lc.push(parameterBlock);
42374286
try {
42384287
if (!env.syntaxExtensions || type != RPAREN) {
4239-
formalParameter(yield, await);
4288+
formalParameter(false, false);
42404289
} // else Nashorn allows no-argument setters
42414290
expect(RPAREN);
42424291

@@ -5359,7 +5408,7 @@ public void accept(IdentNode identNode) {
53595408
*/
53605409
private Block functionBody(final ParserContextFunctionNode functionNode) {
53615410
final boolean yield = functionNode.isGenerator();
5362-
final boolean await = functionNode.isAsync() || (isTopLevelAwait() && isModule && functionNode.isModule());
5411+
final boolean await = functionNode.isAsync() || (isTopLevelAwait() && isModule && functionNode.isModule()) || functionNode.isClassStaticBlock();
53635412
final long bodyToken = token;
53645413
final int bodyFinish;
53655414
final boolean parseBody;

graal-js/src/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextFunctionNode.java

+4
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,10 @@ public void setInternalName(String internalName) {
547547
this.internalName = internalName;
548548
}
549549

550+
public boolean isClassStaticBlock() {
551+
return getFlag(FunctionNode.IS_CLASS_FIELD_INITIALIZER) != 0;
552+
}
553+
550554
public boolean isCoverArrowHead() {
551555
return coverArrowHead;
552556
}

graal-js/src/com.oracle.js.parser/src/com/oracle/js/parser/ir/ClassNode.java

+13-13
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public class ClassNode extends LexicalContextExpression implements LexicalContex
5757
private final List<PropertyNode> classElements;
5858
private final Scope scope;
5959
private final int instanceFieldCount;
60-
private final int staticFieldCount;
60+
private final int staticElementCount;
6161
private final boolean hasPrivateMethods;
6262
private final boolean hasPrivateInstanceMethods;
6363

@@ -70,19 +70,19 @@ public class ClassNode extends LexicalContextExpression implements LexicalContex
7070
* @param finish finish
7171
*/
7272
public ClassNode(final long token, final int finish, final IdentNode ident, final Expression classHeritage, final PropertyNode constructor, final List<PropertyNode> classElements,
73-
final Scope scope, final int instanceFieldCount, final int staticFieldCount, final boolean hasPrivateMethods, final boolean hasPrivateInstanceMethods) {
73+
final Scope scope, final int instanceFieldCount, final int staticElementCount, final boolean hasPrivateMethods, final boolean hasPrivateInstanceMethods) {
7474
super(token, finish);
7575
this.ident = ident;
7676
this.classHeritage = classHeritage;
7777
this.constructor = constructor;
7878
this.classElements = classElements;
7979
this.scope = scope;
8080
this.instanceFieldCount = instanceFieldCount;
81-
this.staticFieldCount = staticFieldCount;
81+
this.staticElementCount = staticElementCount;
8282
this.hasPrivateMethods = hasPrivateMethods;
8383
this.hasPrivateInstanceMethods = hasPrivateInstanceMethods;
84-
assert instanceFieldCount == fieldCount(classElements, false);
85-
assert staticFieldCount == fieldCount(classElements, true);
84+
assert instanceFieldCount == elementCount(classElements, false);
85+
assert staticElementCount == elementCount(classElements, true);
8686
}
8787

8888
private ClassNode(final ClassNode classNode, final IdentNode ident, final Expression classHeritage, final PropertyNode constructor, final List<PropertyNode> classElements) {
@@ -92,16 +92,16 @@ private ClassNode(final ClassNode classNode, final IdentNode ident, final Expres
9292
this.constructor = constructor;
9393
this.classElements = classElements;
9494
this.scope = classNode.scope;
95-
this.instanceFieldCount = fieldCount(classElements, false);
96-
this.staticFieldCount = fieldCount(classElements, true);
95+
this.instanceFieldCount = elementCount(classElements, false);
96+
this.staticElementCount = elementCount(classElements, true);
9797
this.hasPrivateMethods = classNode.hasPrivateMethods;
9898
this.hasPrivateInstanceMethods = classNode.hasPrivateInstanceMethods;
9999
}
100100

101-
private static int fieldCount(List<PropertyNode> classElements, boolean isStatic) {
101+
private static int elementCount(List<PropertyNode> classElements, boolean isStatic) {
102102
int count = 0;
103103
for (PropertyNode classElement : classElements) {
104-
if (classElement.isClassField() && classElement.isStatic() == isStatic) {
104+
if (classElement.isStatic() == isStatic && (classElement.isClassField() || classElement.isClassStaticBlock())) {
105105
count++;
106106
}
107107
}
@@ -195,12 +195,12 @@ public int getInstanceFieldCount() {
195195
return instanceFieldCount;
196196
}
197197

198-
public boolean hasStaticFields() {
199-
return staticFieldCount != 0;
198+
public boolean hasStaticElements() {
199+
return staticElementCount != 0;
200200
}
201201

202-
public int getStaticFieldCount() {
203-
return staticFieldCount;
202+
public int getStaticElementCount() {
203+
return staticElementCount;
204204
}
205205

206206
public boolean hasPrivateMethods() {

graal-js/src/com.oracle.js.parser/src/com/oracle/js/parser/ir/FunctionNode.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
230230
/** Does this function contain a {@code fn.apply(_, arguments)} call? */
231231
public static final int HAS_APPLY_ARGUMENTS_CALL = 1 << 29;
232232

233-
/** Is this function a class field initializer? */
233+
/** Is this function a class field/static initializer? */
234234
public static final int IS_CLASS_FIELD_INITIALIZER = 1 << 30;
235235

236236
/**

graal-js/src/com.oracle.js.parser/src/com/oracle/js/parser/ir/PropertyNode.java

+21-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2010, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2010, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -133,7 +133,7 @@ public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
133133
if (visitor.enterPropertyNode(this)) {
134134
//@formatter:off
135135
return visitor.leavePropertyNode(
136-
setKey((Expression) key.accept(visitor)).
136+
setKey(key == null ? null : (Expression) key.accept(visitor)).
137137
setValue(value == null ? null : (Expression) value.accept(visitor)).
138138
setGetter(getter == null ? null : (FunctionNode) getter.accept(visitor)).
139139
setSetter(setter == null ? null : (FunctionNode) setter.accept(visitor)));
@@ -150,33 +150,38 @@ public <R> R accept(TranslatorNodeVisitor<? extends LexicalContext, R> visitor)
150150

151151
@Override
152152
public void toString(final StringBuilder sb, final boolean printType) {
153+
if (isStatic) {
154+
sb.append("static ");
155+
}
156+
153157
if (value != null) {
154-
if (isStatic) {
155-
sb.append("static ");
156-
}
157-
if (value instanceof FunctionNode && ((FunctionNode) value).isMethod()) {
158+
if (isClassStaticBlock()) {
159+
sb.append("{}");
160+
} else if (value instanceof FunctionNode && ((FunctionNode) value).isMethod()) {
161+
if (((FunctionNode) value).isAsync()) {
162+
sb.append("async ");
163+
}
164+
if (((FunctionNode) value).isGenerator()) {
165+
sb.append('*');
166+
}
158167
toStringKey(sb, printType);
159168
((FunctionNode) value).toStringTail(sb, printType);
160169
} else {
161170
toStringKey(sb, printType);
162171
sb.append(": ");
163172
value.toString(sb, printType);
164173
}
174+
} else if (isClassField()) {
175+
toStringKey(sb, printType);
165176
}
166177

167178
if (getter != null) {
168-
if (isStatic) {
169-
sb.append("static ");
170-
}
171179
sb.append("get ");
172180
toStringKey(sb, printType);
173181
getter.toStringTail(sb, printType);
174182
}
175183

176184
if (setter != null) {
177-
if (isStatic) {
178-
sb.append("static ");
179-
}
180185
sb.append("set ");
181186
toStringKey(sb, printType);
182187
setter.toStringTail(sb, printType);
@@ -315,4 +320,8 @@ public String getPrivateName() {
315320
public boolean isAccessor() {
316321
return getter != null || setter != null;
317322
}
323+
324+
public boolean isClassStaticBlock() {
325+
return isStatic() && getKey() == null;
326+
}
318327
}

graal-js/src/com.oracle.truffle.js.parser/src/com/oracle/truffle/js/parser/GraalJSTranslator.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -3013,7 +3013,10 @@ private ObjectLiteralMemberNode enterObjectPropertyNode(PropertyNode property, b
30133013
JSWriteFrameSlotNode writePrivateNode = (JSWriteFrameSlotNode) privateVar.createWriteNode(null);
30143014
return factory.createPrivateMethodMember(property.isStatic(), value, writePrivateNode);
30153015
}
3016+
} else if (isClass && property.isClassStaticBlock()) {
3017+
return factory.createStaticBlockMember(value);
30163018
} else {
3019+
assert property.getKey() != null;
30173020
return factory.createDataMember(property.getKeyName(), property.isStatic(), enumerable, value, property.isClassField());
30183021
}
30193022
}
@@ -3387,7 +3390,7 @@ public JavaScriptNode enterClassNode(ClassNode classNode) {
33873390

33883391
JavaScriptNode classDefinition = factory.createClassDefinition(context, (JSFunctionExpressionNode) classFunction, classHeritage,
33893392
members.toArray(ObjectLiteralMemberNode.EMPTY), writeClassBinding, className,
3390-
classNode.getInstanceFieldCount(), classNode.getStaticFieldCount(), classNode.hasPrivateInstanceMethods(), currentFunction().getBlockScopeSlot());
3393+
classNode.getInstanceFieldCount(), classNode.getStaticElementCount(), classNode.hasPrivateInstanceMethods(), currentFunction().getBlockScopeSlot());
33913394

33923395
if (classNode.hasPrivateMethods()) {
33933396
// internal constructor binding used for private brand checks.

graal-js/src/com.oracle.truffle.js.test.external/src/com/oracle/truffle/js/test/external/test262/Test262Runnable.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ public class Test262Runnable extends TestRunnable {
182182
"class-fields-private",
183183
"class-fields-public",
184184
"class-methods-private",
185+
"class-static-block",
185186
"class-static-fields-private",
186187
"class-static-fields-public",
187188
"class-static-methods-private",
@@ -242,7 +243,6 @@ public class Test262Runnable extends TestRunnable {
242243
"arbitrary-module-namespace-names",
243244
"array-find-from-last",
244245
"class-fields-private-in",
245-
"class-static-block",
246246
"resizable-arraybuffer",
247247
"tail-call-optimization",
248248
}));
@@ -255,6 +255,7 @@ public class Test262Runnable extends TestRunnable {
255255
"class-fields-private",
256256
"class-fields-public",
257257
"class-methods-private",
258+
"class-static-block",
258259
"class-static-fields-private",
259260
"class-static-fields-public",
260261
"class-static-methods-private",

graal-js/src/com.oracle.truffle.js.test.external/src/com/oracle/truffle/js/test/external/testv8/TestV8Runnable.java

+1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ public class TestV8Runnable extends TestRunnable {
100100
}));
101101
private static final Set<String> ES2022_FLAGS = new HashSet<>(Arrays.asList(new String[]{
102102
"--harmony-atomics-waitasync",
103+
"--harmony-class-static-blocks",
103104
"--harmony-object-has-own",
104105
"--harmony-regexp-match-indices",
105106
"--harmony-top-level-await"

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/nodes/NodeFactory.java

+4
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,10 @@ public ObjectLiteralMemberNode createSpreadObjectMember(boolean isStatic, JavaSc
815815
return ObjectLiteralNode.newSpreadObjectMember(isStatic, value);
816816
}
817817

818+
public ObjectLiteralMemberNode createStaticBlockMember(JavaScriptNode value) {
819+
return ObjectLiteralNode.newStaticBlockMember(value);
820+
}
821+
818822
public JavaScriptNode createClassDefinition(JSContext context, JSFunctionExpressionNode constructorFunction, JavaScriptNode classHeritage, ObjectLiteralMemberNode[] members,
819823
JSWriteFrameSlotNode writeClassBinding, String className, int instanceFieldCount, int staticFieldCount, boolean hasPrivateInstanceMethods, FrameSlot blockScopeSlot) {
820824
return ClassDefinitionNode.create(context, constructorFunction, classHeritage, members,

0 commit comments

Comments
 (0)