@@ -1228,81 +1228,98 @@ function transitiveBaseKeys(node: NodeType): Set<string> {
12281228 return seen ;
12291229}
12301230
1231- function generateNodeAccessorDispatch (
1232- w : CodeWriter ,
1233- method : string ,
1234- ret : string ,
1235- hasAccessor : ( node : NodeType ) => boolean ,
1236- body : ( node : NodeType ) => string ,
1237- zero : string ,
1238- ) {
1239- const cases : { node : NodeType ; kinds : string [ ] ; } [ ] = [ ] ;
1240- for ( const node of api . nodes ( ) ) {
1241- if ( ! hasAccessor ( node ) ) continue ;
1242- const kinds = node . allKinds ( ) . map ( kind => kind . formatGoConstant ( ) ) ;
1243- if ( kinds . length !== 0 ) cases . push ( { node, kinds } ) ;
1244- }
1245-
1246- // Large sparse Kind switches become comparison trees. Dense indices let Go
1247- // emit jump tables; zero is reserved for kinds without the accessor.
1248- const useDispatchTable = cases . length >= 8 ;
1249- const table = `node${ method } Dispatch` ;
1250- if ( useDispatchTable ) {
1251- if ( cases . length > 255 ) {
1252- throw new Error ( `${ method } dispatch exceeds the uint8 index range` ) ;
1253- }
1254- w . write ( `var ${ table } = [kindFlowReduceLabelData + 1]uint8{` ) ;
1231+ // Kinds have a base when their records hold its fields, so testing for it is
1232+ // a table lookup.
1233+ function generateNodeAccessors ( w : CodeWriter ) {
1234+ for ( const { method, ret, base } of NODE_ACCESSORS ) {
1235+ const table = `kindHas${ base } ` ;
1236+ w . write ( `var ${ table } = [kindFlowReduceLabelData + 1]bool{` ) ;
12551237 w . push ( ) ;
1256- for ( const [ index , { kinds } ] of cases . entries ( ) ) {
1257- for ( const kind of kinds ) {
1258- w . write ( `${ kind } : ${ index + 1 } ,` ) ;
1259- }
1238+ for ( const node of api . nodes ( ) ) {
1239+ if ( ! transitiveBaseKeys ( node ) . has ( base ) ) continue ;
1240+ for ( const kind of node . allKinds ( ) ) w . write ( `${ kind . formatGoConstant ( ) } : true,` ) ;
12601241 }
12611242 w . pop ( ) ;
12621243 w . write ( "}" ) ;
12631244 w . write ( "" ) ;
1245+ w . write ( `func (n Node) ${ method } () ${ ret } {` ) ;
1246+ w . push ( ) ;
1247+ w . write ( `if ${ table } [n.Kind()] {` ) ;
1248+ w . push ( ) ;
1249+ w . write ( `return ${ ret } {n}` ) ;
1250+ w . pop ( ) ;
1251+ w . write ( "}" ) ;
1252+ w . write ( `return ${ ret } {}` ) ;
1253+ w . pop ( ) ;
1254+ w . write ( "}" ) ;
1255+ w . write ( "" ) ;
12641256 }
1257+ }
12651258
1266- w . write ( `func (n Node) ${ method } () ${ ret } {` ) ;
1259+ function hasMember ( node : NodeType , name : string ) : boolean {
1260+ return schemaMembers ( node ) . some ( member => member . name === name ) ;
1261+ }
1262+
1263+ function generateMemberOffsetTable ( w : CodeWriter , table : string , member : string , refType : string ) {
1264+ const storage = goStorageName ( member ) ;
1265+ w . write ( `var ${ table } = [kindFlowReduceLabelData + 1]uint8{` ) ;
12671266 w . push ( ) ;
1268- if ( useDispatchTable ) {
1269- w . write ( `switch ${ table } [n.Kind()] {` ) ;
1270- }
1271- else {
1272- w . write ( "switch n.Kind() {" ) ;
1273- }
1274- for ( const [ index , { node, kinds } ] of cases . entries ( ) ) {
1275- w . write ( `case ${ useDispatchTable ? index + 1 : kinds . join ( ", " ) } :` ) ;
1276- w . push ( ) ;
1277- w . write ( `return ${ body ( node ) } ` ) ;
1278- w . pop ( ) ;
1267+ for ( const node of api . nodes ( ) ) {
1268+ const field = nodeRecordFields ( node ) . find ( f => f . storage === storage ) ;
1269+ // Only refs can be read the same way for every kind.
1270+ if ( field ?. goType !== refType ) continue ;
1271+ for ( const kind of node . allKinds ( ) ) {
1272+ w . write ( `${ kind . formatGoConstant ( ) } : uint8(unsafe.Offsetof(${ dataName ( node ) } {}.${ storage } )),` ) ;
1273+ }
12791274 }
1280- w . write ( "default:" ) ;
1275+ w . pop ( ) ;
1276+ w . write ( "}" ) ;
1277+ w . write ( "" ) ;
1278+ }
1279+
1280+ // A member that every node having it stores as the same kind of ref is read
1281+ // through a table of its offsets by kind, zero for kinds without it.
1282+ function generateMemberOffsetAccessor ( w : CodeWriter , method : string , ret : string , member : string , refType : string , deref : string , zero : string ) {
1283+ const table = `node${ method } Offsets` ;
1284+ generateMemberOffsetTable ( w , table , member , refType ) ;
1285+ w . write ( `func (n Node) ${ method } () ${ ret } {` ) ;
12811286 w . push ( ) ;
1282- w . write ( `return ${ zero } ` ) ;
1287+ w . write ( `if off := ${ table } [n.Kind()]; off != 0 {` ) ;
1288+ w . push ( ) ;
1289+ w . write ( `return ${ deref } ((*${ refType } )(unsafe.Add(n.rec(), off)))` ) ;
12831290 w . pop ( ) ;
12841291 w . write ( "}" ) ;
1292+ w . write ( `return ${ zero } ` ) ;
12851293 w . pop ( ) ;
12861294 w . write ( "}" ) ;
12871295 w . write ( "" ) ;
12881296}
12891297
1290- function generateNodeAccessors ( w : CodeWriter ) {
1291- for ( const { method, ret, base } of NODE_ACCESSORS ) {
1292- generateNodeAccessorDispatch ( w , method , ret , node => transitiveBaseKeys ( node ) . has ( base ) , ( ) => `${ ret } {n}` , `${ ret } {}` ) ;
1293- }
1294- }
1298+ // Members read through Node by hand-written accessors, which find them by
1299+ // these tables.
1300+ const MEMBER_OFFSET_TABLES : { member : string ; refType : string ; } [ ] = [
1301+ { member : "Expression" , refType : "nodeRef" } ,
1302+ { member : "Initializer" , refType : "nodeRef" } ,
1303+ { member : "Type" , refType : "nodeRef" } ,
1304+ { member : "PostfixToken" , refType : "nodeRef" } ,
1305+ { member : "QuestionToken" , refType : "nodeRef" } ,
1306+ { member : "QuestionDotToken" , refType : "nodeRef" } ,
1307+ { member : "TypeArguments" , refType : "listRef" } ,
1308+ { member : "Arguments" , refType : "listRef" } ,
1309+ ] ;
12951310
1296- function hasMember ( node : NodeType , name : string ) : boolean {
1297- return schemaMembers ( node ) . some ( member => member . name === name ) ;
1311+ function generateMemberOffsetTables ( w : CodeWriter ) {
1312+ for ( const { member, refType } of MEMBER_OFFSET_TABLES ) {
1313+ generateMemberOffsetTable ( w , `node${ member } Offsets` , member , refType ) ;
1314+ }
12981315}
12991316
13001317function generateNameDispatch ( w : CodeWriter ) {
1301- generateNodeAccessorDispatch ( w , "Name" , "DeclarationName" , node => hasMember ( node , "name" ) , node => ` ${ viewOf ( node , "n" ) } .Name()` , "Node{}" ) ;
1318+ generateMemberOffsetAccessor ( w , "Name" , "DeclarationName" , "name" , "nodeRef" , "n.deref" , "Node{}" ) ;
13021319}
13031320
13041321function generateModifiersDispatch ( w : CodeWriter ) {
1305- generateNodeAccessorDispatch ( w , "Modifiers" , "ModifierList" , node => hasMember ( node , "modifiers" ) , node => ` ${ viewOf ( node , "n" ) } .Modifiers()` , "ModifierList{}" ) ;
1322+ generateMemberOffsetAccessor ( w , "Modifiers" , "ModifierList" , "modifiers" , "listRef" , "n.derefModifiers" , "ModifierList{}" ) ;
13061323}
13071324
13081325function generateSetModifiersDispatch ( w : CodeWriter ) {
@@ -1695,6 +1712,7 @@ function generate(): string {
16951712 w . write ( "" ) ;
16961713 generateNameDispatch ( w ) ;
16971714 generateModifiersDispatch ( w ) ;
1715+ generateMemberOffsetTables ( w ) ;
16981716 generateSetModifiersDispatch ( w ) ;
16991717
17001718 // As*() casts
0 commit comments