5.12.3.2.1.1.3.4.3. 缓存内联函数体
函数 start_method 为这个 method-declaration 的声明符构建了节点,随后如果方法有缺省参数,这些参数将被 parser 的 unparsed_functions_queues 域所记录。而如果接下来的符号是“ { ”,表示适用 function-definition 规则,定义了内联函数。
cp_parser_save_member_function_body (continue)
14656 /* Remember it, if there default args to post process. */
14657 cp_parser_save_default_args (parser, fn);
14658
14659 /* Create a token cache. */
14660 cache = cp_token_cache_new ();
14661 /* Save away the tokens that make up the body of the
14662 function. */
14663 cp_parser_cache_group (parser, cache, CPP_CLOSE_BRACE, /*depth=*/ 0);
14664 /* Handle function try blocks. */
14665 while (cp_lexer_next_token_is_keyword (parser->lexer, RID_CATCH))
14666 cp_parser_cache_group (parser, cache, CPP_CLOSE_BRACE, /*depth=*/ 0);
14667
14668 /* Save away the inline definition; we will process it when the
14669 class is complete. */
14670 DECL_PENDING_INLINE_INFO (fn) = cache;
14671 DECL_PENDING_INLINE_P (fn) = 1;
14672
14673 /* We need to know that this was defined in the class, so that
14674 friend templates are handled correctly. */
14675 DECL_INITIALIZED_IN_CLASS_P (fn) = 1;
14676
14677 /* We're done with the inline definition. */
14678 finish_method (fn);
14679
14680 /* Add FN to the queue of functions to be parsed later. */
14681 TREE_VALUE (parser->unparsed_functions_queues)
14682 = tree_cons (NULL_TREE, fn,
14683 TREE_VALUE (parser->unparsed_functions_queues));
14684
14685 return fn;
14686 }
在调用处,内联函数要被展开,因此其定义需要被解析器记住。解析器使用类型为 cp_token_block 的块来缓存函数的定义。每个块的大小是 512 字节。
100 typedef struct cp_token_block GTY (()) in parser.c
101 {
102 /* The tokens. */
103 cp_token tokens[CP_TOKEN_BLOCK_NUM_TOKENS];
104 /* The number of tokens in this block. */
105 size_t num_tokens;
106 /* The next token block in the chain. */
107 struct cp_token_block *next;
108 /* The previous block in the chain. */
109 struct cp_token_block *prev;
110 } cp_token_block;
112 typedef struct cp_token_cache GTY (())
113 {
114 /* The first block in the cache. NULL if there are no tokens in the
115 cache. */
116 cp_token_block *first;
117 /* The last block in the cache. NULL If there are no tokens in the
118 cache. */
119 cp_token_block *last;
120 } cp_token_cache;
对于每个内联函数,都伴随一个 cp_token_cache ,它把保存定义的多个块串接起来。
131 static cp_token_cache *
132 cp_token_cache_new (void) in parser.c
133 {
134 return ggc_alloc_cleared (sizeof (cp_token_cache));
135 }
函数 cp_parser_cache_group 是一个递归函数,因为一个函数可以包含任意对括号或大括号,这种情形由递归函数处理更为简单。注意到 end 或者是 CPP_CLOSE_PAREN ,或者是 CPP_CLOSE_BRACE ,而函数“消化”的第一个符号一定是“ { ”。
15349 static void
15350 cp_parser_cache_group (cp_parser *parser, in parser.c
15351 cp_token_cache *cache,
15352 enum cpp_ttype end,
15353 unsigned depth)
15354 {
15355 while (true)
15356 {
15357 cp_token *token;
15358
15359 /* Abort a parenthesized expression if we encounter a brace. */
15360 if ((end == CPP_CLOSE_PAREN || depth == 0)
15361 && cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
15362 return ;
15363 /* If we've reached the end of the file, stop. */
15364 if (cp_lexer_next_token_is (parser->lexer, CPP_EOF))
15365 return ;
15366 /* Consume the next token. */
15367 token = cp_lexer_consume_token (parser->lexer);
15368 /* Add this token to the tokens we are saving. */
15369 cp_token_cache_push_token (cache, token);
15370 /* See if it starts a new group. */
15371 if (token->type == CPP_OPEN_BRACE)
15372 {
15373 cp_parser_cache_group (parser, cache, CPP_CLOSE_BRACE, depth + 1);
15374 if (depth == 0)
15375 return ;
15376 }
15377 else if (token->type == CPP_OPEN_PAREN)
15378 cp_parser_cache_group (parser, cache, CPP_CLOSE_PAREN, depth + 1);
15379 else if (token->type == end)
15380 return ;
15381 }
15382 }
正常情况下,匹配的闭括号或闭大括号满足 15379 行的条件,使得函数体面退出。但是 15360 行的条件,并不像注释所说的那样,括号间的分号而不是大括号将使得条件成立。例如内联函数:
void m1() {
for (int j = 0 ; j < 10; j++)
{
…
}
}
开括号所触发的缓存仅是红色部分的符号,余下至闭括号的符号被保存在函数体起始的开大括号所开启的缓存里。
139 static void
140 cp_token_cache_push_token (cp_token_cache *cache, in parser.c
141 cp_token *token)
142 {
143 cp_token_block *b = cache->last;
144
145 /* See if we need to allocate a new token block. */
146 if (!b || b->num_tokens == CP_TOKEN_BLOCK_NUM_TOKENS)
147 {
148 b = ggc_alloc_cleared (sizeof (cp_token_block));
149 b->prev = cache->last;
150 if (cache->last)
151 {
152 cache->last->next = b;
153 cache->last = b;
154 }
155 else
156 cache->first = cache->last = b;
157 }
158 /* Add this token to the current token block. */
159 b->tokens[b->num_tokens++] = *token;
160 }
5.12.3.2.1.1.3.4.4. 退出方法的作用域
在看到该方法的声明符的时候,前端加入方法的作用域对象,并使得其成为当前作用域。现在我们跨过该方法定义的闭大括号,应该退入上一级的作用域。
11111 tree
11112 finish_method (tree decl) in decl.c
11113 {
11114 tree fndecl = decl;
11115 tree old_initial;
11116
11117 tree link;
11118
11119 if (decl == void_type_node)
11120 return decl;
11121
11122 old_initial = DECL_INITIAL (fndecl);
11123
11124 /* Undo the level for the parms (from start_method).
11125 This is like poplevel, but it causes nothing to be
11126 saved. Saving information here confuses symbol-table
11127 output routines. Besides, this information will
11128 be correctly output when this method is actually
11129 compiled. */
11130
11131 /* Clear out the meanings of the local variables of this level;
11132 also record in each decl which block it belongs to. */
11133
11134 for (link = current_binding_level ->names; link; link = TREE_CHAIN (link))
11135 {
11136 if (DECL_NAME (link) != NULL_TREE)
11137 pop_binding (DECL_NAME (link), link);
11138 my_friendly_assert (TREE_CODE (link) != FUNCTION_DECL, 163);
11139 DECL_CONTEXT (link) = NULL_TREE;
11140 }
11141
11142 poplevel (0, 0, 0);
11143
11144 DECL_INITIAL (fndecl) = old_initial;
11145
11146 /* We used to check if the context of FNDECL was different from
11147 current_class_type as another way to get inside here. This didn't work
11148 for String.cc in libg++. */
11149 if (DECL_FRIEND_P (fndecl))
11150 {
11151 CLASSTYPE_INLINE_FRIENDS (current_class_type )
11152 = tree_cons (NULL_TREE, fndecl, CLASSTYPE_INLINE_FRIENDS (current_class_type ));
11153 decl = void_type_node;
11154 }
11155
11156 return decl;
11157 }
我们的构造函数其函数体是空的,那么, finish_method 及其调用的函数仅是调用了 leave_scope 。看到作用域对象断开同作用域链的连接,被链入 free_binding_level 。
( 点此打开 )
图 72 :“ Lock ”构造函数的 FUNCTION_DECL —退出作用域
5.12.3.2.1.1.3.4.5. 把构造函数插入类
回到 cp_parser_member_declaration ,接下来的 finish_member_declaration 完成对方法的处理。下面因为构造函数是声明在 struct 里,在 pushclass 的 5482 行, current_access_specifier 被设置为 access_public_node 。
2089 void
2090 finish_member_declaration (tree decl) in semantics.c
2091 {
2092 if (decl == error_mark_node || decl == NULL_TREE)
2093 return ;
2094
2095 if (decl == void_type_node)
2096 /* The COMPONENT was a friend, not a member, and so there's
2097 nothing for us to do. */
2098 return ;
2099
2100 /* We should see only one DECL at a time. */
2101 my_friendly_assert (TREE_CHAIN (decl) == NULL_TREE, 0);
2102
2103 /* Set up access control for DECL. */
2104 TREE_PRIVATE (decl)
2105 = (current_access_specifier == access_private_node);
2106 TREE_PROTECTED (decl)
2107 = (current_access_specifier == access_protected_node);
2108 if (TREE_CODE (decl) == TEMPLATE_DECL)
2109 {
2110 TREE_PRIVATE (DECL_TEMPLATE_RESULT (decl)) = TREE_PRIVATE (decl);
2111 TREE_PROTECTED (DECL_TEMPLATE_RESULT (decl)) = TREE_PROTECTED (decl);
2112 }
2113
2114 /* Mark the DECL as a member of the current class. */
2115 DECL_CONTEXT (decl) = current_class_type ;
2116
2117 /* [dcl.link]
2118
2119 A C language linkage is ignored for the names of class members
2120 and the member function type of class member functions. */
2121 if (DECL_LANG_SPECIFIC (decl) && DECL_LANGUAGE (decl) == lang_c)
2122 SET_DECL_LANGUAGE (decl, lang_cplusplus);
2123
2124 /* Put functions on the TYPE_METHODS list and everything else on the
2125 TYPE_FIELDS list. Note that these are built up in reverse order.
2126 We reverse them (to obtain declaration order) in finish_struct. */
2127 if (TREE_CODE (decl) == FUNCTION_DECL
2128 || DECL_FUNCTION_TEMPLATE_P (decl))
2129 {
2130 /* We also need to add this function to the
2131 CLASSTYPE_METHOD_VEC. */
2132 add_method (current_class_type , decl, /*error_p=*/ 0);
2133
2134 TREE_CHAIN (decl) = TYPE_METHODS (current_class_type );
2135 TYPE_METHODS (current_class_type ) = decl;
2136
2137 maybe_add_class_template_decl_list (current_class_type , decl,
2138 /*friend_p=*/ 0);
2139 }
2140 /* Enter the DECL into the scope of the class. */
2141 else if ((TREE_CODE (decl) == USING_DECL && TREE_TYPE (decl))
2142 || pushdecl_class_level (decl))
2143 {
…
2172 }
2173 }
现在可以向类的节点加入这个方法了。在前端中,类保持一个向量( vector )来保存在其中声明的所有方法。对于构造函数及析构函数,它们是常用的方法,因此它们放在固定的位置( 0 对应构造函数, 1 对应析构函数)。
721 void
722 add_method (tree type, tree method, int error_p) in class.c
723 {
724 int using;
725 int len;
726 int slot;
727 tree method_vec;
728 int template_conv_p;
729
730 if (method == error_mark_node)
731 return ;
732
733 using = (DECL_CONTEXT (method) != type);
734 template_conv_p = (TREE_CODE (method) == TEMPLATE_DECL
735 && DECL_TEMPLATE_CONV_FN_P (method));
736
737 if (!CLASSTYPE_METHOD_VEC (type))
738 /* Make a new method vector. We start with 8 entries. We must
739 allocate at least two (for constructors and destructors), and
740 we're going to end up with an assignment operator at some point
741 as well.
742
743 We could use a TREE_LIST for now, and convert it to a TREE_VEC
744 in finish_struct, but we would probably waste more memory
745 making the links in the list than we would by over-allocating
746 the size of the vector here. Furthermore, we would complicate
747 all the code that expects this to be a vector. */
748 CLASSTYPE_METHOD_VEC (type) = make_tree_vec (8);
749
750 method_vec = CLASSTYPE_METHOD_VEC (type);
751 len = TREE_VEC_LENGTH (method_vec);
752
753 /* Constructors and destructors go in special slots. */
754 if (DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (method))
755 slot = CLASSTYPE_CONSTRUCTOR_SLOT;
756 else if (DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (method))
757 {
758 slot = CLASSTYPE_DESTRUCTOR_SLOT;
759 TYPE_HAS_DESTRUCTOR (type) = 1;
760
761 if (TYPE_FOR_JAVA (type))
762 error (DECL_ARTIFICIAL (method)
763 ? "Java class '%T' cannot have an implicit non-trivial destructor"
764 : "Java class '%T' cannot have a destructor",
765 DECL_CONTEXT (method));
766 }
767 else
768 {
…
866 }
867
868 if (processing_template_decl )
869 /* TYPE is a template class. Don't issue any errors now; wait
870 until instantiation time to complain. */
871 ;
872 else
873 {
…
949 }
950
951 /* Actually insert the new method. */
952 TREE_VEC_ELT (method_vec, slot)
953 = build_overload (method, TREE_VEC_ELT (method_vec, slot));
954
955 /* Add the new binding. */
956 if (!DECL_CONSTRUCTOR_P (method)
957 && !DECL_DESTRUCTOR_P (method))
958 push_class_level_binding (DECL_NAME (method),
959 TREE_VEC_ELT (method_vec, slot));
960 }
另外,对于类模板, maybe_add_class_template_decl_list 也要把该方法记录在该类模板的 decl_list 中。
( 点此打开 )
图 73 :加入默认构造函数