GCC-3.4.6源代码学习笔记(75)

5.3.1.3.    内建宏—pragma指示的处理

所谓的内建宏,也即是标准所定义的宏,计有:__TIME____DATE____FILE____BASE_FILE____LINE____INCLUDE_LEVEL___Pragma__STDC__(参考builtin_array),其处理函数为builtin_macro

 

249  static int

250  builtin_macro (cpp_reader *pfile, cpp_hashnode *node)                         in cppmacro.c

251  {

252    const uchar *buf;

253    size_t len;

254    char *nbuf;

255 

256    if (node->value.builtin == BT_PRAGMA)

257    {

258      /* Don't interpret _Pragma within directives. The standard is

259        not clear on this, but to me this makes most sense.  */

260      if (pfile->state.in_directive)

261        return 0;

262 

263      _cpp_do__Pragma (pfile);

264      return 1;

265    }

 

C99引入了_Pragma操作符。该特性针对#pragma的主要问题:作为一个指示,它不能作为宏扩展的结果中产生。_Pragma是一个操作符,非常像sizeofdefined,可以嵌入到宏里。

它的语法是_Pragma (STRING-LITERAL),其中STRING-LITERAL可以是普通的或宽字符串。通过用“//”替换“/”,以及“/"”替换“"”,来去字符化。其结果,就像放在#pragma指示右边那样,得到处理。例如,

_Pragma ("GCC dependency /"parse.y/"")

#pragma GCC dependency "parse.y"的效果相同。这个效果也可以使用宏实现,例如(我们亦把它作为下面代码学习的例子)

#define DO_PRAGMA(x) _Pragma (#x)

DO_PRAGMA (GCC dependency "parse.y")

标准没有澄清_Pragma操作符可以出现在何处。预处理器在预处理条件指示,如#if中,不接受它。为了安全起见,你最好不要在除#define以外的指示中使用它,并且让它独占一行。

 

1369 void

1370 _cpp_do__Pragma (cpp_reader *pfile)                                                        in cpplib.c

1371 {

1372   const cpp_token *string = get__Pragma_string (pfile);

1373

1374   if (string)

1375     destringize_and_run (pfile, &string->val.str);

1376   else

1377     cpp_error (pfile, CPP_DL_ERROR,

1378          "_Pragma takes a parenthesized string literal");

1379 }

 

_Pragma的内容是字符串,例程get__Pragma_string提取这个内容。

 

1289 static const cpp_token *

1290 get__Pragma_string (cpp_reader *pfile)                                                      in cpplib.c

1291{

1292  const cpp_token *string;

1293

1294  if (get_token_no_padding (pfile)->type != CPP_OPEN_PAREN)

1295    return NULL;

1296

1297  string = get_token_no_padding (pfile);

1298  if (string->type != CPP_STRING && string->type != CPP_WSTRING)

1299    return NULL;

1300

1301  if (get_token_no_padding (pfile)->type != CPP_CLOSE_PAREN)

1302    return NULL;

1303

1304  return string;

1305}

 

这个语句中的符号由get_token_no_padding获取,它只返回非空白符。

 

1276 static const cpp_token *

1277 get_token_no_padding (cpp_reader *pfile)                                                   in cpplib.c

1278 {

1279   for (;;)

1280   {

1281     const cpp_token *result = cpp_get_token (pfile);

1282     if (result->type != CPP_PADDING)

1283       return result;

1284   }

1285 }

 

这个字符串就是应该跟在#pragma后的部分。在执行对应的#pragma操作符之前,这个字符串还要字符化,以“/”替换“//”,以“.”替换“/””。

 

1309 static void

1310 destringize_and_run (cpp_reader *pfile, const cpp_string *in)                        in cpplib.c

1311 {

1312   const unsigned char *src, *limit;

1313   char *dest, *result;

1314

1315   dest = result = alloca (in->len - 1);

1316   src = in->text + 1 + (in->text[0] == 'L');

1317   limit = in->text + in->len - 1;

1318   while (src < limit)

1319   {

1320     /* We know there is a character following the backslash.  */

1321     if (*src == '//' && (src[1] == '//' || src[1] == '"'))

1322       src++;

1323     *dest++ = *src++;

1324   }

1325   *dest = '/n';

1326

1327   /* Ugh; an awful kludge. We are really not set up to be lexing

1328     tokens when in the middle of a macro expansion. Use a new

1329     context to force cpp_get_token to lex, and so skip_rest_of_line

1330     doesn't go beyond the end of the text. Also, remember the

1331     current lexing position so we can return to it later.

1332

1333     Something like line-at-a-time lexing should remove the need for

1334     this.  */

1335   {

1336     cpp_context *saved_context = pfile->context;

1337     cpp_token *saved_cur_token = pfile->cur_token;

1338     tokenrun *saved_cur_run = pfile->cur_run;

1339

1340     pfile->context = xnew (cpp_context);

1341     pfile->context->macro = 0;

1342     pfile->context->prev = 0;

1343     run_directive (pfile, T_PRAGMA, result, dest - result);

1344     free (pfile->context);

1345     pfile->context = saved_context;

1346     pfile->cur_token = saved_cur_token;

1347     pfile->cur_run = saved_cur_run;

1348     pfile->line--;

1349   }

1350

1351   /* See above comment. For the moment, we'd like

1352

1353       token1 _Pragma ("foo") token2

1354

1355     to be output as

1356

1357       token1

1358       # 7 "file.c"

1359       #pragma foo

1360       # 7 "file.c"

1361       token2

1362

1363     Getting the line markers is a little tricky.  */

1364   if (pfile->cb.line_change)

1365     pfile->cb.line_change (pfile, pfile->cur_token, false);

1366 }

 

在上面1343行的run_directive中,这个经过还原的字符串由cpp_push_buffer压入当前cpp_reader的读入缓存。但是在cpp_get_token里看到,如果当前的上下文不是基本上下文的话,是由当前上下文读入符号,而不是从读入缓存读入符号。为了确保能从读入缓存读入符号,这里伪造了一个基本上下文(1342行的prev0)。同时也要保存原来基本上下文的cur_tokencur_run。注意这意味着#pragma指示执行完后,其符号是会被挪作他用的。

 

1136 static void

1137 do_pragma (cpp_reader *pfile)                                                                    in cpplib.c

1138 {

1139   const struct pragma_entry *p = NULL;

1140   const cpp_token *token, *pragma_token = pfile->cur_token;

1141   unsigned int count = 1;

1142

1143   pfile->state.prevent_expansion++;

1144

1145   token = cpp_get_token (pfile);

1146   if (token->type == CPP_NAME)

1147   {

1148     p = lookup_pragma_entry (pfile->pragmas, token->val.node);

1149     if (p && p->is_nspace)

1150     {

1151       count = 2;

1152       token = cpp_get_token (pfile);

1153       if (token->type == CPP_NAME)

1154         p = lookup_pragma_entry (p->u.space, token->val.node);

1155       else

1156         p = NULL;

1157     }

1158   }

1159

1160   if (p)

1161   {

1162     /* Since the handler below doesn't get the line number, that it

1163       might need for diagnostics, make sure it has the right

1164       numbers in place.  */

1165     if (pfile->cb.line_change)

1166       (*pfile->cb.line_change) (pfile, pragma_token, false);

1167     (*p->u.handler) (pfile);

1168   }

1169   else if (pfile->cb.def_pragma)

1170   {

1171     _cpp_backup_tokens (pfile, count);

1172     pfile->cb.def_pragma (pfile, pfile->directive_line);

1173   }

1174

1175   pfile->state.prevent_expansion--;

1176 }

 

系统支持的#pragma操作符已经由cpp_register_pragma注册,而lookup_pragma_entry将为已注册的操作符返回其登记入口。这些注册的句柄构成了一个层级结构,以我们的例子来说,在1152cpp_get_token提取了符号dependency,这是注册在句柄GCC下的。注意到当处理#pragma操作符时,宏是不允许展开的(通过1143行设置prevent_expansion)。

如果操作符没有注册,1169行的钩子def_pragma将被调用,它将给出一个警告消息。对于我们的例子,这个操作符由do_pragma_dependency来完成。

5.3.1.3.1.            #pragma GCC dependency的句柄

当在文件中声明#pragma GCC dependency parse.y,这表示该文件依赖于parse.y,如果parse.y有更新,该文件也要更新。

 

1248 static void

1249 do_pragma_dependency (cpp_reader *pfile)                                                 in cpplib.c

1250 {

1251   const char *fname;

1252   int angle_brackets, ordering;

1253

1254   fname = parse_include (pfile, &angle_brackets);

1255   if (!fname)

1256     return;

1257

1258   ordering = _cpp_compare_file_date (pfile, fname, angle_brackets);

1259   if (ordering < 0)

1260     cpp_error (pfile, CPP_DL_WARNING, "cannot find source file %s", fname);

1261   else if (ordering > 0)

1262   {

1263     cpp_error (pfile, CPP_DL_WARNING,

1264              "current file is older than %s", fname);

1265     if (cpp_get_token (pfile)->type != CPP_EOF)

1266     {

1267       _cpp_backup_tokens (pfile, 1);

1268       do_diagnostic (pfile, CPP_DL_WARNING, 0);

1269     }

1270   }

1271

1272   free ((void *) fname);

1273 }

 

在指示中指定的文件名可以是形式<file>,这被认为是系统头文件,而且将在对应的查找路径下查找。函数parse_include收集这个文件名并确定它是否为系统头文件。

 

634  static const char *

635  parse_include (cpp_reader *pfile, int *pangle_brackets)                                in cpplib.c

636  {

637    char *fname;

638    const cpp_token *header;

639 

640   /* Allow macro expansion.  */

641    header = get_token_no_padding (pfile);

642    if (header->type == CPP_STRING || header->type == CPP_HEADER_NAME)

643    {

644      fname = xmalloc (header->val.str.len - 1);

645      memcpy (fname, header->val.str.text + 1, header->val.str.len - 2);

646      fname[header->val.str.len - 2] = '/0';

647      *pangle_brackets = header->type == CPP_HEADER_NAME;

648    }

649    else if (header->type == CPP_LESS)

650    {

651      fname = glue_header_name (pfile);

652      *pangle_brackets = 1;

653    }

654    else

655    {

656      const unsigned char *dir;

657 

658      if (pfile->directive == &dtable[T_PRAGMA])

659        dir = U"pragma dependency";

660      else

661        dir = pfile->directive->name;

662      cpp_error(pfile, CPP_DL_ERROR, "#%s expects /"FILENAME/" or <FILENAME>",

663              dir);

664 

665      return NULL;

666    }

667 

668    check_eol (pfile);

669    return fname;

670  }

 

显然,只有字母,‘<’及‘>’可以出现在文件名里,否则将给出一个错误消息。收集完文件名的信息,_cpp_compare_file_date将按包含文件的查找规则,找出这个文件,并返回时间戳比较的结果。

 

1023 int

1024 _cpp_compare_file_date (cpp_reader *pfile, const char *fname,                     in cppfiles.c

1025               int angle_brackets)

1026 {

1027   _cpp_file *file;

1028   struct cpp_dir *dir;

1029

1030   dir = search_path_head (pfile, fname, angle_brackets, IT_INCLUDE);

1031   if (!dir)

1032     return -1;

1033

1034   file = _cpp_find_file (pfile, fname, dir, false, angle_brackets);

1035   if (file->err_no)

1036     return -1;

1037

1038   if (file->fd != -1)

1039   {

1040     close (file->fd);

1041     file->fd = -1;

1042   }

1043

1044   return file->st.st_mtime > pfile->buffer->file->st.st_mtime;

1045 }

 

当发现当前文件更旧时,这个#pragma操作符将给出警告。

5.3.1.4.    其它内建宏的处理

如果是其它的内建宏,下面部分的builtin_macro将得到执行。

 

builtin_macro (continue)

 

267    buf = _cpp_builtin_macro_text (pfile, node);

268    len = ustrlen (buf);

269    nbuf = alloca (len + 1);

270    memcpy (nbuf, buf, len);

271    nbuf[len]='/n';

272 

273    cpp_push_buffer (pfile, (uchar *) nbuf, len, /* from_stage3 */ true);

274    _cpp_clean_line (pfile);

275 

276    /* Set pfile->cur_token as required by _cpp_lex_direct.  */

277    pfile->cur_token = _cpp_temp_token (pfile);

278    push_token_context (pfile, NULL, _cpp_lex_direct (pfile), 1);

279    if (pfile->buffer->cur != pfile->buffer->rlimit)

280      cpp_error (pfile, CPP_DL_ICE, "invalid built-in macro /"%s/"",

281               NODE_NAME (node));

282    _cpp_pop_buffer (pfile);

283 

284    return 1;

285  }

 

所有这些内建宏是信息性的。更换文件一节详细描述了line_map结构的文件包含链。其中,宏__BASE_FILE__将被展开为主输入文件名。

 

116   const uchar *

117   _cpp_builtin_macro_text (cpp_reader *pfile, cpp_hashnode *node)        in cppmacro.c

118   {

119     const uchar *result = NULL;

120    unsigned int number = 1;

121 

122    switch (node->value.builtin)

123    {

124      default:

125        cpp_error (pfile, CPP_DL_ICE, "invalid built-in macro /"%s/"",

126         NODE_NAME (node));

127        break;

128 

129      case BT_FILE:

130      case BT_BASE_FILE:

131      {

132        unsigned int len;

133        const char *name;

134        uchar *buf;

135        const struct line_map *map = pfile->map;

136 

137        if (node->value.builtin == BT_BASE_FILE)

138          while (!MAIN_FILE_P (map))

139            map = INCLUDED_FROM (&pfile->line_maps, map);

140 

141        name = map->to_file;

142        len = strlen (name);

143        buf = _cpp_unaligned_alloc (pfile, len * 4 + 3);

144        result = buf;

145        *buf = '"';

146        buf = cpp_quote_string (buf + 1, (const unsigned char *) name, len);

147        *buf++ = '"';

148        *buf = '/0';

149        }

150        break;

151 

152      case BT_INCLUDE_LEVEL:

153        /* The line map depth counts the primary source as level 1, but

154          historically __INCLUDE_DEPTH__ has called the primary source

155          level 0.  */

156        number = pfile->line_maps.depth - 1;

157        break;

158 

159      case BT_SPECLINE:

160        /* If __LINE__ is embedded in a macro, it must expand to the

161          line of the macro's invocation, not its definition.

162          Otherwise things like assert() will not work properly.  */

163        if (CPP_OPTION (pfile, traditional))

164          number = pfile->line;

165        else

166          number = pfile->cur_token[-1].line;

167        number = SOURCE_LINE (pfile->map, number);

168        break;

169 

170        /* __STDC__ has the value 1 under normal circumstances.

171          However, if (a) we are in a system header, (b) the option

172          stdc_0_in_system_headers is true (set by target config), and

173          (c) we are not in strictly conforming mode, then it has the

174          value 0.  */

175      case BT_STDC:

176      {

177        if (CPP_IN_SYSTEM_HEADER (pfile)

178            && CPP_OPTION (pfile, stdc_0_in_system_headers)

179            && !CPP_OPTION (pfile,std))

180          number = 0;

181        else

182          number = 1;

183      }

184      break;

185 

186      case BT_DATE:

187      case BT_TIME:

188        if (pfile->date == NULL)

189        {

190          /* Allocate __DATE__ and __TIME__ strings from permanent

191            storage. We only do this once, and don't generate them

192            at init time, because time() and localtime() are very

193            slow on some systems.  */

194          time_t tt;

195          struct tm *tb = NULL;

196 

197          /* (time_t) -1 is a legitimate value for "number of seconds

198            since the Epoch", so we have to do a little dance to

199            distinguish that from a genuine error.  */

200          errno = 0;

201          tt = time(NULL);

202          if (tt != (time_t)-1 || errno == 0)

203            tb = localtime (&tt);

204 

205          if (tb)

206          {

207            pfile->date = _cpp_unaligned_alloc (pfile,

208                                          sizeof ("/"Oct 11 1347/""));

209            sprintf ((char *) pfile->date, "/"%s %2d %4d/"",

210                  monthnames[tb->tm_mon], tb->tm_mday,

211                   tb->tm_year + 1900);

212 

213            pfile->time = _cpp_unaligned_alloc (pfile,

214                                          sizeof ("/"12:34:56/""));

215            sprintf ((char *) pfile->time, "/"%02d:%02d:%02d/"",

216                   tb->tm_hour, tb->tm_min, tb->tm_sec);

217          }

218          else

219          {

220            cpp_errno (pfile, CPP_DL_WARNING,

221                     "could not determine date and time");

222        

223            pfile->date = U"/"??? ?? ????/"";

224            pfile->time = U"/"??:??:??/"";

225          }

226        }

227 

228      if (node->value.builtin == BT_DATE)

229        result = pfile->date;

230      else

231        result = pfile->time;

232      break;

233    }

234 

235    if (result == NULL)

236    {

237      /* 21 bytes holds all NUL-terminated unsigned 64-bit numbers.  */

238      result = _cpp_unaligned_alloc (pfile, 21);

239      sprintf ((char *) result, "%u", number);

240    }

241 

242    return result;     

243  }

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值