@@ -78,6 +78,12 @@ impl<'a> PeepholeOptimizations {
7878 prop : & mut MethodDefinition < ' a > ,
7979 ctx : Ctx < ' a , ' _ > ,
8080 ) {
81+ let property_key_parent: ClassPropertyKeyParent = prop. into ( ) ;
82+ if let PropertyKey :: StringLiteral ( str) = & prop. key {
83+ if property_key_parent. should_keep_as_computed_property ( & str. value ) {
84+ return ;
85+ }
86+ }
8187 self . try_compress_property_key ( & mut prop. key , & mut prop. computed , ctx) ;
8288 }
8389
@@ -86,6 +92,12 @@ impl<'a> PeepholeOptimizations {
8692 prop : & mut PropertyDefinition < ' a > ,
8793 ctx : Ctx < ' a , ' _ > ,
8894 ) {
95+ let property_key_parent: ClassPropertyKeyParent = prop. into ( ) ;
96+ if let PropertyKey :: StringLiteral ( str) = & prop. key {
97+ if property_key_parent. should_keep_as_computed_property ( & str. value ) {
98+ return ;
99+ }
100+ }
89101 self . try_compress_property_key ( & mut prop. key , & mut prop. computed , ctx) ;
90102 }
91103
@@ -94,6 +106,12 @@ impl<'a> PeepholeOptimizations {
94106 prop : & mut AccessorProperty < ' a > ,
95107 ctx : Ctx < ' a , ' _ > ,
96108 ) {
109+ let property_key_parent: ClassPropertyKeyParent = prop. into ( ) ;
110+ if let PropertyKey :: StringLiteral ( str) = & prop. key {
111+ if property_key_parent. should_keep_as_computed_property ( & str. value ) {
112+ return ;
113+ }
114+ }
97115 self . try_compress_property_key ( & mut prop. key , & mut prop. computed , ctx) ;
98116 }
99117
@@ -847,9 +865,7 @@ impl<'a> PeepholeOptimizations {
847865 } ;
848866 let PropertyKey :: StringLiteral ( s) = key else { return } ;
849867 let value = s. value . as_str ( ) ;
850- // Uncaught SyntaxError: Classes may not have a field named 'constructor'
851- // Uncaught SyntaxError: Class constructor may not be a private method
852- if matches ! ( value, "__proto__" | "prototype" | "constructor" | "#constructor" ) {
868+ if value == "__proto__" {
853869 return ;
854870 }
855871 if is_identifier_name ( value) {
@@ -1107,6 +1123,68 @@ impl<'a> LatePeepholeOptimizations {
11071123 }
11081124}
11091125
1126+ struct ClassPropertyKeyParent {
1127+ pub ty : ClassPropertyKeyParentType ,
1128+ /// Whether the property is static.
1129+ pub r#static : bool ,
1130+ }
1131+
1132+ impl ClassPropertyKeyParent {
1133+ /// Whether the key should be kept as a computed property to avoid early errors.
1134+ ///
1135+ /// <https://tc39.es/ecma262/2024/multipage/ecmascript-language-functions-and-classes.html#sec-static-semantics-classelementkind>
1136+ /// <https://tc39.es/ecma262/2024/multipage/ecmascript-language-functions-and-classes.html#sec-class-definitions-static-semantics-early-errors>
1137+ /// <https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-class-definitions-static-semantics-early-errors>
1138+ fn should_keep_as_computed_property ( & self , key : & str ) -> bool {
1139+ match key {
1140+ "prototype" => self . r#static ,
1141+ "constructor" => match self . ty {
1142+ // Uncaught SyntaxError: Class constructor may not be an accessor
1143+ ClassPropertyKeyParentType :: MethodDefinition => !self . r#static ,
1144+ // Uncaught SyntaxError: Classes may not have a field named 'constructor'
1145+ // Uncaught SyntaxError: Class constructor may not be a private method
1146+ ClassPropertyKeyParentType :: AccessorProperty
1147+ | ClassPropertyKeyParentType :: PropertyDefinition => true ,
1148+ } ,
1149+ "#constructor" => true ,
1150+ _ => false ,
1151+ }
1152+ }
1153+ }
1154+
1155+ enum ClassPropertyKeyParentType {
1156+ PropertyDefinition ,
1157+ AccessorProperty ,
1158+ MethodDefinition ,
1159+ }
1160+
1161+ impl From < & PropertyDefinition < ' _ > > for ClassPropertyKeyParent {
1162+ fn from ( prop : & PropertyDefinition < ' _ > ) -> Self {
1163+ Self { ty : ClassPropertyKeyParentType :: PropertyDefinition , r#static : prop. r#static }
1164+ }
1165+ }
1166+
1167+ impl From < & AccessorProperty < ' _ > > for ClassPropertyKeyParent {
1168+ fn from ( accessor : & AccessorProperty < ' _ > ) -> Self {
1169+ Self { ty : ClassPropertyKeyParentType :: AccessorProperty , r#static : accessor. r#static }
1170+ }
1171+ }
1172+
1173+ impl From < & MethodDefinition < ' _ > > for ClassPropertyKeyParent {
1174+ fn from ( method : & MethodDefinition < ' _ > ) -> Self {
1175+ Self { ty : ClassPropertyKeyParentType :: MethodDefinition , r#static : method. r#static }
1176+ }
1177+ }
1178+
1179+ impl < T > From < & mut T > for ClassPropertyKeyParent
1180+ where
1181+ ClassPropertyKeyParent : for < ' a > std:: convert:: From < & ' a T > ,
1182+ {
1183+ fn from ( prop : & mut T ) -> Self {
1184+ ( & * prop) . into ( )
1185+ }
1186+ }
1187+
11101188/// Port from <https://github.com/google/closure-compiler/blob/v20240609/test/com/google/javascript/jscomp/PeepholeSubstituteAlternateSyntaxTest.java>
11111189#[ cfg( test) ]
11121190mod test {
@@ -1607,10 +1685,34 @@ mod test {
16071685 ) ;
16081686
16091687 test ( "class C { ['-1']() {} }" , "class C { '-1'() {} }" ) ;
1610- test_same ( "class C { ['prototype']() {} }" ) ;
16111688 test_same ( "class C { ['__proto__']() {} }" ) ;
1612- test_same ( "class C { ['constructor']() {} }" ) ;
1613- test_same ( "class C { ['#constructor']() {} }" ) ;
1689+
1690+ // <https://tc39.es/ecma262/2024/multipage/ecmascript-language-functions-and-classes.html#sec-static-semantics-classelementkind>
1691+ // <https://tc39.es/ecma262/2024/multipage/ecmascript-language-functions-and-classes.html#sec-class-definitions-static-semantics-early-errors>
1692+ // <https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-class-definitions-static-semantics-early-errors>
1693+ test_same ( "class C { static ['prototype']() {} }" ) ; // class C { static prototype() {} } is an early error
1694+ test_same ( "class C { static ['prototype'] = 0 }" ) ; // class C { prototype = 0 } is an early error
1695+ test_same ( "class C { static accessor ['prototype'] = 0 }" ) ; // class C { accessor prototype = 0 } is an early error
1696+ test ( "class C { ['prototype']() {} }" , "class C { prototype() {} }" ) ;
1697+ test ( "class C { ['prototype'] = 0 }" , "class C { prototype = 0 }" ) ;
1698+ test ( "class C { accessor ['prototype'] = 0 }" , "class C { accessor prototype = 0 }" ) ;
1699+ test_same ( "class C { ['constructor'] = 0 }" ) ; // class C { constructor = 0 } is an early error
1700+ test_same ( "class C { accessor ['constructor'] = 0 }" ) ; // class C { accessor constructor = 0 } is an early error
1701+ test_same ( "class C { static ['constructor'] = 0 }" ) ; // class C { static constructor = 0 } is an early error
1702+ test_same ( "class C { static accessor ['constructor'] = 0 }" ) ; // class C { static accessor constructor = 0 } is an early error
1703+ test_same ( "class C { ['constructor']() {} }" ) ; // computed `constructor` is not treated as a constructor
1704+ test_same ( "class C { *['constructor']() {} }" ) ; // class C { *constructor() {} } is an early error
1705+ test_same ( "class C { async ['constructor']() {} }" ) ; // class C { async constructor() {} } is an early error
1706+ test_same ( "class C { async *['constructor']() {} }" ) ; // class C { async *constructor() {} } is an early error
1707+ test_same ( "class C { get ['constructor']() {} }" ) ; // class C { get constructor() {} } is an early error
1708+ test_same ( "class C { set ['constructor'](v) {} }" ) ; // class C { set constructor(v) {} } is an early error
1709+ test ( "class C { static ['constructor']() {} }" , "class C { static constructor() {} }" ) ;
1710+ test_same ( "class C { ['#constructor'] = 0 }" ) ; // class C { #constructor = 0 } is an early error
1711+ test_same ( "class C { accessor ['#constructor'] = 0 }" ) ; // class C { accessor #constructor = 0 } is an early error
1712+ test_same ( "class C { ['#constructor']() {} }" ) ; // class C { #constructor() {} } is an early error
1713+ test_same ( "class C { static ['#constructor'] = 0 }" ) ; // class C { static #constructor = 0 } is an early error
1714+ test_same ( "class C { static accessor ['#constructor'] = 0 }" ) ; // class C { static accessor #constructor = 0 } is an early error
1715+ test_same ( "class C { static ['#constructor']() {} }" ) ; // class C { static #constructor() {} } is an early error
16141716 }
16151717
16161718 #[ test]
0 commit comments