diff options
Diffstat (limited to 'src/cmd')
302 files changed, 18515 insertions, 45696 deletions
diff --git a/src/cmd/5a/a.h b/src/cmd/5a/a.h index a2c87cf48..8b39d610f 100644 --- a/src/cmd/5a/a.h +++ b/src/cmd/5a/a.h @@ -98,6 +98,7 @@ struct Gen { Sym* sym; int32 offset; + int32 offset2; short type; short reg; short name; diff --git a/src/cmd/5a/a.y b/src/cmd/5a/a.y index c9fdf05d5..c506ff9d5 100644 --- a/src/cmd/5a/a.y +++ b/src/cmd/5a/a.y @@ -33,6 +33,7 @@ #include <stdio.h> /* if we don't, bison will, and a.h re-#defines getc */ #include <libc.h> #include "a.h" +#include "../../pkg/runtime/funcdata.h" %} %union { @@ -50,11 +51,11 @@ %left '*' '/' '%' %token <lval> LTYPE1 LTYPE2 LTYPE3 LTYPE4 LTYPE5 %token <lval> LTYPE6 LTYPE7 LTYPE8 LTYPE9 LTYPEA -%token <lval> LTYPEB LTYPEC LTYPED LTYPEE LTYPEF +%token <lval> LTYPEB LTYPEC LTYPED LTYPEE %token <lval> LTYPEG LTYPEH LTYPEI LTYPEJ LTYPEK %token <lval> LTYPEL LTYPEM LTYPEN LTYPEBX LTYPEPLD %token <lval> LCONST LSP LSB LFP LPC -%token <lval> LTYPEX LR LREG LF LFREG LC LCREG LPSR LFCR +%token <lval> LTYPEX LTYPEPC LTYPEF LR LREG LF LFREG LC LCREG LPSR LFCR %token <lval> LCOND LS LAT %token <dval> LFCONST %token <sval> LSCONST @@ -217,10 +218,20 @@ inst: */ | LTYPEB name ',' imm { + $4.type = D_CONST2; + $4.offset2 = ArgsSizeUnknown; outcode($1, Always, &$2, 0, &$4); } | LTYPEB name ',' con ',' imm { + $6.type = D_CONST2; + $6.offset2 = ArgsSizeUnknown; + outcode($1, Always, &$2, $4, &$6); + } +| LTYPEB name ',' con ',' imm '-' con + { + $6.type = D_CONST2; + $6.offset2 = $8; outcode($1, Always, &$2, $4, &$6); } /* @@ -310,6 +321,26 @@ inst: outcode($1, Always, &$2, NREG, &nullgen); } /* + * PCDATA + */ +| LTYPEPC gen ',' gen + { + if($2.type != D_CONST || $4.type != D_CONST) + yyerror("arguments to PCDATA must be integer constants"); + outcode($1, Always, &$2, NREG, &$4); + } +/* + * FUNCDATA + */ +| LTYPEF gen ',' gen + { + if($2.type != D_CONST) + yyerror("index for FUNCDATA must be integer constant"); + if($4.type != D_EXTERN && $4.type != D_STATIC) + yyerror("value for FUNCDATA must be symbol reference"); + outcode($1, Always, &$2, NREG, &$4); + } +/* * END */ | LTYPEE comma diff --git a/src/cmd/5a/doc.go b/src/cmd/5a/doc.go index 29725db04..3e9e78fe6 100644 --- a/src/cmd/5a/doc.go +++ b/src/cmd/5a/doc.go @@ -10,6 +10,10 @@ http://plan9.bell-labs.com/magic/man2html/1/8a +Go-specific considerations are documented at + + http://golang.org/doc/asm + Its target architecture is the ARM, referred to by these tools as arm. */ diff --git a/src/cmd/5a/lex.c b/src/cmd/5a/lex.c index a77e3050d..c1b54e50b 100644 --- a/src/cmd/5a/lex.c +++ b/src/cmd/5a/lex.c @@ -68,7 +68,7 @@ main(int argc, char *argv[]) ARGBEGIN { default: c = ARGC(); - if(c >= 0 || c < sizeof(debug)) + if(c >= 0 && c < sizeof(debug)) debug[c] = 1; break; @@ -191,8 +191,8 @@ struct "R6", LREG, 6, "R7", LREG, 7, "R8", LREG, 8, - "R9", LREG, 9, - "R10", LREG, 10, + "m", LREG, 9, // avoid unintentionally clobber m/g using R9/R10 + "g", LREG, 10, "R11", LREG, 11, "R12", LREG, 12, "R13", LREG, 13, @@ -414,6 +414,8 @@ struct "MULAWB", LTYPEN, AMULAWB, "USEFIELD", LTYPEN, AUSEFIELD, + "PCDATA", LTYPEPC, APCDATA, + "FUNCDATA", LTYPEF, AFUNCDATA, 0 }; @@ -483,14 +485,14 @@ void zname(char *n, int t, int s) { - Bputc(&obuf, ANAME); - Bputc(&obuf, t); /* type */ - Bputc(&obuf, s); /* sym */ + BPUTC(&obuf, ANAME); + BPUTC(&obuf, t); /* type */ + BPUTC(&obuf, s); /* sym */ while(*n) { - Bputc(&obuf, *n); + BPUTC(&obuf, *n); n++; } - Bputc(&obuf, 0); + BPUTC(&obuf, 0); } void @@ -501,11 +503,11 @@ zaddr(Gen *a, int s) char *n; Ieee e; - Bputc(&obuf, a->type); - Bputc(&obuf, a->reg); - Bputc(&obuf, s); - Bputc(&obuf, a->name); - Bputc(&obuf, 0); + BPUTC(&obuf, a->type); + BPUTC(&obuf, a->reg); + BPUTC(&obuf, s); + BPUTC(&obuf, a->name); + BPUTC(&obuf, 0); switch(a->type) { default: print("unknown type %d\n", a->type); @@ -520,38 +522,33 @@ zaddr(Gen *a, int s) case D_REGREG: case D_REGREG2: - Bputc(&obuf, a->offset); + BPUTC(&obuf, a->offset); break; + case D_CONST2: + l = a->offset2; + BPUTLE4(&obuf, l); + // fall through case D_OREG: case D_CONST: case D_BRANCH: case D_SHIFT: l = a->offset; - Bputc(&obuf, l); - Bputc(&obuf, l>>8); - Bputc(&obuf, l>>16); - Bputc(&obuf, l>>24); + BPUTLE4(&obuf, l); break; case D_SCONST: n = a->sval; for(i=0; i<NSNAME; i++) { - Bputc(&obuf, *n); + BPUTC(&obuf, *n); n++; } break; case D_FCONST: ieeedtod(&e, a->dval); - Bputc(&obuf, e.l); - Bputc(&obuf, e.l>>8); - Bputc(&obuf, e.l>>16); - Bputc(&obuf, e.l>>24); - Bputc(&obuf, e.h); - Bputc(&obuf, e.h>>8); - Bputc(&obuf, e.h>>16); - Bputc(&obuf, e.h>>24); + BPUTLE4(&obuf, e.l); + BPUTLE4(&obuf, e.h); break; } } @@ -633,13 +630,10 @@ jackpot: goto jackpot; break; } - Bputc(&obuf, a); - Bputc(&obuf, scond); - Bputc(&obuf, reg); - Bputc(&obuf, stmtline); - Bputc(&obuf, stmtline>>8); - Bputc(&obuf, stmtline>>16); - Bputc(&obuf, stmtline>>24); + BPUTC(&obuf, a); + BPUTC(&obuf, scond); + BPUTC(&obuf, reg); + BPUTLE4(&obuf, stmtline); zaddr(g1, sf); zaddr(g2, st); @@ -713,12 +707,12 @@ outhist(void) q = 0; } if(n) { - Bputc(&obuf, ANAME); - Bputc(&obuf, D_FILE); /* type */ - Bputc(&obuf, 1); /* sym */ - Bputc(&obuf, '<'); + BPUTC(&obuf, ANAME); + BPUTC(&obuf, D_FILE); /* type */ + BPUTC(&obuf, 1); /* sym */ + BPUTC(&obuf, '<'); Bwrite(&obuf, p, n); - Bputc(&obuf, 0); + BPUTC(&obuf, 0); } p = q; if(p == 0 && op) { @@ -728,13 +722,10 @@ outhist(void) } g.offset = h->offset; - Bputc(&obuf, AHISTORY); - Bputc(&obuf, Always); - Bputc(&obuf, 0); - Bputc(&obuf, h->line); - Bputc(&obuf, h->line>>8); - Bputc(&obuf, h->line>>16); - Bputc(&obuf, h->line>>24); + BPUTC(&obuf, AHISTORY); + BPUTC(&obuf, Always); + BPUTC(&obuf, 0); + BPUTLE4(&obuf, h->line); zaddr(&nullgen, 0); zaddr(&g, 0); diff --git a/src/cmd/5a/y.tab.c b/src/cmd/5a/y.tab.c index ce97ee315..dd102a09a 100644 --- a/src/cmd/5a/y.tab.c +++ b/src/cmd/5a/y.tab.c @@ -1,10 +1,8 @@ +/* A Bison parser, made by GNU Bison 2.5. */ -/* A Bison parser, made by GNU Bison 2.4.1. */ - -/* Skeleton implementation for Bison's Yacc-like parsers in C +/* Bison implementation for Yacc-like parsers in C - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 - Free Software Foundation, Inc. + Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -46,7 +44,7 @@ #define YYBISON 1 /* Bison version. */ -#define YYBISON_VERSION "2.4.1" +#define YYBISON_VERSION "2.5" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -67,17 +65,18 @@ /* Copy the first part of user declarations. */ -/* Line 189 of yacc.c */ +/* Line 268 of yacc.c */ #line 31 "a.y" #include <u.h> #include <stdio.h> /* if we don't, bison will, and a.h re-#defines getc */ #include <libc.h> #include "a.h" +#include "../../pkg/runtime/funcdata.h" -/* Line 189 of yacc.c */ -#line 81 "y.tab.c" +/* Line 268 of yacc.c */ +#line 80 "y.tab.c" /* Enabling traces. */ #ifndef YYDEBUG @@ -118,39 +117,40 @@ LTYPEC = 269, LTYPED = 270, LTYPEE = 271, - LTYPEF = 272, - LTYPEG = 273, - LTYPEH = 274, - LTYPEI = 275, - LTYPEJ = 276, - LTYPEK = 277, - LTYPEL = 278, - LTYPEM = 279, - LTYPEN = 280, - LTYPEBX = 281, - LTYPEPLD = 282, - LCONST = 283, - LSP = 284, - LSB = 285, - LFP = 286, - LPC = 287, - LTYPEX = 288, - LR = 289, - LREG = 290, - LF = 291, - LFREG = 292, - LC = 293, - LCREG = 294, - LPSR = 295, - LFCR = 296, - LCOND = 297, - LS = 298, - LAT = 299, - LFCONST = 300, - LSCONST = 301, - LNAME = 302, - LLAB = 303, - LVAR = 304 + LTYPEG = 272, + LTYPEH = 273, + LTYPEI = 274, + LTYPEJ = 275, + LTYPEK = 276, + LTYPEL = 277, + LTYPEM = 278, + LTYPEN = 279, + LTYPEBX = 280, + LTYPEPLD = 281, + LCONST = 282, + LSP = 283, + LSB = 284, + LFP = 285, + LPC = 286, + LTYPEX = 287, + LTYPEPC = 288, + LTYPEF = 289, + LR = 290, + LREG = 291, + LF = 292, + LFREG = 293, + LC = 294, + LCREG = 295, + LPSR = 296, + LFCR = 297, + LCOND = 298, + LS = 299, + LAT = 300, + LFCONST = 301, + LSCONST = 302, + LNAME = 303, + LLAB = 304, + LVAR = 305 }; #endif /* Tokens. */ @@ -168,39 +168,40 @@ #define LTYPEC 269 #define LTYPED 270 #define LTYPEE 271 -#define LTYPEF 272 -#define LTYPEG 273 -#define LTYPEH 274 -#define LTYPEI 275 -#define LTYPEJ 276 -#define LTYPEK 277 -#define LTYPEL 278 -#define LTYPEM 279 -#define LTYPEN 280 -#define LTYPEBX 281 -#define LTYPEPLD 282 -#define LCONST 283 -#define LSP 284 -#define LSB 285 -#define LFP 286 -#define LPC 287 -#define LTYPEX 288 -#define LR 289 -#define LREG 290 -#define LF 291 -#define LFREG 292 -#define LC 293 -#define LCREG 294 -#define LPSR 295 -#define LFCR 296 -#define LCOND 297 -#define LS 298 -#define LAT 299 -#define LFCONST 300 -#define LSCONST 301 -#define LNAME 302 -#define LLAB 303 -#define LVAR 304 +#define LTYPEG 272 +#define LTYPEH 273 +#define LTYPEI 274 +#define LTYPEJ 275 +#define LTYPEK 276 +#define LTYPEL 277 +#define LTYPEM 278 +#define LTYPEN 279 +#define LTYPEBX 280 +#define LTYPEPLD 281 +#define LCONST 282 +#define LSP 283 +#define LSB 284 +#define LFP 285 +#define LPC 286 +#define LTYPEX 287 +#define LTYPEPC 288 +#define LTYPEF 289 +#define LR 290 +#define LREG 291 +#define LF 292 +#define LFREG 293 +#define LC 294 +#define LCREG 295 +#define LPSR 296 +#define LFCR 297 +#define LCOND 298 +#define LS 299 +#define LAT 300 +#define LFCONST 301 +#define LSCONST 302 +#define LNAME 303 +#define LLAB 304 +#define LVAR 305 @@ -209,8 +210,8 @@ typedef union YYSTYPE { -/* Line 214 of yacc.c */ -#line 38 "a.y" +/* Line 293 of yacc.c */ +#line 39 "a.y" Sym *sym; int32 lval; @@ -220,8 +221,8 @@ typedef union YYSTYPE -/* Line 214 of yacc.c */ -#line 225 "y.tab.c" +/* Line 293 of yacc.c */ +#line 226 "y.tab.c" } YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ @@ -232,8 +233,8 @@ typedef union YYSTYPE /* Copy the second part of user declarations. */ -/* Line 264 of yacc.c */ -#line 237 "y.tab.c" +/* Line 343 of yacc.c */ +#line 238 "y.tab.c" #ifdef short # undef short @@ -283,7 +284,7 @@ typedef short int yytype_int16; #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ -# if YYENABLE_NLS +# if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include <libintl.h> /* INFRINGES ON USER NAME SPACE */ # define YY_(msgid) dgettext ("bison-runtime", msgid) @@ -336,11 +337,11 @@ YYID (yyi) # define alloca _alloca # else # define YYSTACK_ALLOC alloca -# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 # endif # endif # endif @@ -363,24 +364,24 @@ YYID (yyi) # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif -# if (defined __cplusplus && ! defined _STDLIB_H \ +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc -# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free -# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif @@ -409,23 +410,7 @@ union yyalloc ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) -/* Copy COUNT objects from FROM to TO. The source and destination do - not overlap. */ -# ifndef YYCOPY -# if defined __GNUC__ && 1 < __GNUC__ -# define YYCOPY(To, From, Count) \ - __builtin_memcpy (To, From, (Count) * sizeof (*(From))) -# else -# define YYCOPY(To, From, Count) \ - do \ - { \ - YYSIZE_T yyi; \ - for (yyi = 0; yyi < (Count); yyi++) \ - (To)[yyi] = (From)[yyi]; \ - } \ - while (YYID (0)) -# endif -# endif +# define YYCOPY_NEEDED 1 /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of @@ -445,23 +430,43 @@ union yyalloc #endif +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + /* YYFINAL -- State number of the termination state. */ #define YYFINAL 2 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 603 +#define YYLAST 609 /* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 70 +#define YYNTOKENS 71 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 35 /* YYNRULES -- Number of rules. */ -#define YYNRULES 130 +#define YYNRULES 133 /* YYNRULES -- Number of states. */ -#define YYNSTATES 329 +#define YYNSTATES 339 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 -#define YYMAXUTOK 304 +#define YYMAXUTOK 305 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) @@ -472,16 +477,16 @@ static const yytype_uint8 yytranslate[] = 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 68, 12, 5, 2, - 66, 67, 10, 8, 63, 9, 2, 11, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 60, 62, - 6, 61, 7, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 69, 12, 5, 2, + 67, 68, 10, 8, 64, 9, 2, 11, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 61, 63, + 6, 62, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 64, 2, 65, 4, 2, 2, 2, 2, 2, + 2, 65, 2, 66, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 3, 2, 69, 2, 2, 2, + 2, 2, 2, 2, 3, 2, 70, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -499,7 +504,7 @@ static const yytype_uint8 yytranslate[] = 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, - 55, 56, 57, 58, 59 + 55, 56, 57, 58, 59, 60 }; #if YYDEBUG @@ -510,92 +515,94 @@ static const yytype_uint16 yyprhs[] = 0, 0, 3, 4, 5, 9, 10, 15, 16, 21, 26, 31, 33, 36, 39, 47, 54, 60, 66, 72, 77, 82, 86, 90, 95, 102, 110, 118, 126, 133, - 140, 144, 149, 156, 163, 168, 172, 178, 184, 192, - 199, 212, 220, 230, 233, 236, 237, 240, 243, 244, - 247, 252, 255, 258, 261, 264, 269, 272, 274, 277, - 281, 283, 287, 291, 293, 295, 297, 302, 304, 306, - 308, 310, 312, 314, 316, 320, 322, 327, 329, 334, - 336, 338, 340, 342, 345, 347, 353, 358, 363, 368, - 373, 375, 377, 379, 381, 386, 388, 390, 392, 397, - 399, 401, 403, 408, 413, 419, 427, 428, 431, 434, - 436, 438, 440, 442, 444, 447, 450, 453, 457, 458, - 461, 463, 467, 471, 475, 479, 483, 488, 493, 497, - 501 + 140, 144, 149, 156, 165, 172, 177, 181, 187, 193, + 201, 208, 221, 229, 239, 242, 247, 252, 255, 256, + 259, 262, 263, 266, 271, 274, 277, 280, 283, 288, + 291, 293, 296, 300, 302, 306, 310, 312, 314, 316, + 321, 323, 325, 327, 329, 331, 333, 335, 339, 341, + 346, 348, 353, 355, 357, 359, 361, 364, 366, 372, + 377, 382, 387, 392, 394, 396, 398, 400, 405, 407, + 409, 411, 416, 418, 420, 422, 427, 432, 438, 446, + 447, 450, 453, 455, 457, 459, 461, 463, 466, 469, + 472, 476, 477, 480, 482, 486, 490, 494, 498, 502, + 507, 512, 516, 520 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int8 yyrhs[] = { - 71, 0, -1, -1, -1, 71, 72, 73, -1, -1, - 58, 60, 74, 73, -1, -1, 57, 60, 75, 73, - -1, 57, 61, 104, 62, -1, 59, 61, 104, 62, - -1, 62, -1, 76, 62, -1, 1, 62, -1, 13, - 77, 88, 63, 95, 63, 90, -1, 13, 77, 88, - 63, 95, 63, -1, 13, 77, 88, 63, 90, -1, - 14, 77, 88, 63, 90, -1, 15, 77, 83, 63, - 83, -1, 16, 77, 78, 79, -1, 16, 77, 78, - 84, -1, 36, 78, 85, -1, 17, 78, 79, -1, - 18, 77, 78, 83, -1, 19, 77, 88, 63, 95, - 78, -1, 20, 77, 86, 63, 64, 82, 65, -1, - 20, 77, 64, 82, 65, 63, 86, -1, 21, 77, - 90, 63, 85, 63, 90, -1, 21, 77, 90, 63, - 85, 78, -1, 21, 77, 78, 85, 63, 90, -1, - 22, 77, 78, -1, 23, 99, 63, 89, -1, 23, - 99, 63, 102, 63, 89, -1, 24, 99, 11, 102, - 63, 80, -1, 25, 77, 90, 78, -1, 29, 78, - 80, -1, 30, 77, 98, 63, 98, -1, 32, 77, - 97, 63, 98, -1, 32, 77, 97, 63, 47, 63, - 98, -1, 33, 77, 98, 63, 98, 78, -1, 31, - 77, 102, 63, 104, 63, 95, 63, 96, 63, 96, - 103, -1, 34, 77, 90, 63, 90, 63, 91, -1, - 35, 77, 90, 63, 90, 63, 90, 63, 95, -1, - 37, 87, -1, 26, 78, -1, -1, 77, 52, -1, - 77, 53, -1, -1, 63, 78, -1, 102, 66, 42, - 67, -1, 57, 100, -1, 58, 100, -1, 68, 102, - -1, 68, 87, -1, 68, 10, 68, 87, -1, 68, - 56, -1, 81, -1, 68, 55, -1, 68, 9, 55, - -1, 95, -1, 95, 9, 95, -1, 95, 78, 82, - -1, 90, -1, 80, -1, 92, -1, 92, 66, 95, - 67, -1, 50, -1, 51, -1, 102, -1, 87, -1, - 98, -1, 85, -1, 99, -1, 66, 95, 67, -1, - 85, -1, 102, 66, 94, 67, -1, 99, -1, 99, - 66, 94, 67, -1, 86, -1, 90, -1, 89, -1, - 92, -1, 68, 102, -1, 95, -1, 66, 95, 63, - 95, 67, -1, 95, 6, 6, 93, -1, 95, 7, - 7, 93, -1, 95, 9, 7, 93, -1, 95, 54, - 7, 93, -1, 95, -1, 102, -1, 45, -1, 42, - -1, 44, 66, 104, 67, -1, 94, -1, 39, -1, - 49, -1, 48, 66, 104, 67, -1, 98, -1, 81, - -1, 47, -1, 46, 66, 102, 67, -1, 102, 66, - 101, 67, -1, 57, 100, 66, 101, 67, -1, 57, - 6, 7, 100, 66, 40, 67, -1, -1, 8, 102, - -1, 9, 102, -1, 40, -1, 39, -1, 41, -1, - 38, -1, 59, -1, 9, 102, -1, 8, 102, -1, - 69, 102, -1, 66, 104, 67, -1, -1, 63, 104, - -1, 102, -1, 104, 8, 104, -1, 104, 9, 104, - -1, 104, 10, 104, -1, 104, 11, 104, -1, 104, - 12, 104, -1, 104, 6, 6, 104, -1, 104, 7, - 7, 104, -1, 104, 5, 104, -1, 104, 4, 104, - -1, 104, 3, 104, -1 + 72, 0, -1, -1, -1, 72, 73, 74, -1, -1, + 59, 61, 75, 74, -1, -1, 58, 61, 76, 74, + -1, 58, 62, 105, 63, -1, 60, 62, 105, 63, + -1, 63, -1, 77, 63, -1, 1, 63, -1, 13, + 78, 89, 64, 96, 64, 91, -1, 13, 78, 89, + 64, 96, 64, -1, 13, 78, 89, 64, 91, -1, + 14, 78, 89, 64, 91, -1, 15, 78, 84, 64, + 84, -1, 16, 78, 79, 80, -1, 16, 78, 79, + 85, -1, 35, 79, 86, -1, 17, 79, 80, -1, + 18, 78, 79, 84, -1, 19, 78, 89, 64, 96, + 79, -1, 20, 78, 87, 64, 65, 83, 66, -1, + 20, 78, 65, 83, 66, 64, 87, -1, 21, 78, + 91, 64, 86, 64, 91, -1, 21, 78, 91, 64, + 86, 79, -1, 21, 78, 79, 86, 64, 91, -1, + 22, 78, 79, -1, 23, 100, 64, 90, -1, 23, + 100, 64, 103, 64, 90, -1, 23, 100, 64, 103, + 64, 90, 9, 103, -1, 24, 100, 11, 103, 64, + 81, -1, 25, 78, 91, 79, -1, 28, 79, 81, + -1, 29, 78, 99, 64, 99, -1, 31, 78, 98, + 64, 99, -1, 31, 78, 98, 64, 48, 64, 99, + -1, 32, 78, 99, 64, 99, 79, -1, 30, 78, + 103, 64, 105, 64, 96, 64, 97, 64, 97, 104, + -1, 33, 78, 91, 64, 91, 64, 92, -1, 34, + 78, 91, 64, 91, 64, 91, 64, 96, -1, 36, + 88, -1, 43, 84, 64, 84, -1, 44, 84, 64, + 84, -1, 26, 79, -1, -1, 78, 53, -1, 78, + 54, -1, -1, 64, 79, -1, 103, 67, 41, 68, + -1, 58, 101, -1, 59, 101, -1, 69, 103, -1, + 69, 88, -1, 69, 10, 69, 88, -1, 69, 57, + -1, 82, -1, 69, 56, -1, 69, 9, 56, -1, + 96, -1, 96, 9, 96, -1, 96, 79, 83, -1, + 91, -1, 81, -1, 93, -1, 93, 67, 96, 68, + -1, 51, -1, 52, -1, 103, -1, 88, -1, 99, + -1, 86, -1, 100, -1, 67, 96, 68, -1, 86, + -1, 103, 67, 95, 68, -1, 100, -1, 100, 67, + 95, 68, -1, 87, -1, 91, -1, 90, -1, 93, + -1, 69, 103, -1, 96, -1, 67, 96, 64, 96, + 68, -1, 96, 6, 6, 94, -1, 96, 7, 7, + 94, -1, 96, 9, 7, 94, -1, 96, 55, 7, + 94, -1, 96, -1, 103, -1, 46, -1, 41, -1, + 45, 67, 105, 68, -1, 95, -1, 38, -1, 50, + -1, 49, 67, 105, 68, -1, 99, -1, 82, -1, + 48, -1, 47, 67, 103, 68, -1, 103, 67, 102, + 68, -1, 58, 101, 67, 102, 68, -1, 58, 6, + 7, 101, 67, 39, 68, -1, -1, 8, 103, -1, + 9, 103, -1, 39, -1, 38, -1, 40, -1, 37, + -1, 60, -1, 9, 103, -1, 8, 103, -1, 70, + 103, -1, 67, 105, 68, -1, -1, 64, 105, -1, + 103, -1, 105, 8, 105, -1, 105, 9, 105, -1, + 105, 10, 105, -1, 105, 11, 105, -1, 105, 12, + 105, -1, 105, 6, 6, 105, -1, 105, 7, 7, + 105, -1, 105, 5, 105, -1, 105, 4, 105, -1, + 105, 3, 105, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { - 0, 67, 67, 69, 68, 76, 75, 83, 82, 88, - 93, 99, 100, 101, 107, 111, 115, 122, 129, 136, - 140, 147, 154, 161, 168, 175, 184, 196, 200, 204, - 211, 218, 222, 229, 236, 243, 250, 254, 258, 262, - 269, 291, 299, 308, 315, 321, 324, 328, 333, 334, - 337, 343, 352, 360, 366, 371, 376, 382, 385, 391, - 399, 403, 412, 418, 419, 420, 421, 426, 432, 438, - 444, 445, 448, 449, 457, 466, 467, 476, 477, 483, - 486, 487, 488, 490, 498, 506, 515, 521, 527, 533, - 541, 547, 555, 556, 560, 568, 569, 575, 576, 584, - 585, 588, 594, 602, 610, 618, 628, 631, 635, 641, - 642, 643, 646, 647, 651, 655, 659, 663, 669, 672, - 678, 679, 683, 687, 691, 695, 699, 703, 707, 711, - 715 + 0, 68, 68, 70, 69, 77, 76, 84, 83, 89, + 94, 100, 101, 102, 108, 112, 116, 123, 130, 137, + 141, 148, 155, 162, 169, 176, 185, 197, 201, 205, + 212, 219, 225, 231, 240, 247, 254, 261, 265, 269, + 273, 280, 302, 310, 319, 326, 335, 346, 352, 355, + 359, 364, 365, 368, 374, 383, 391, 397, 402, 407, + 413, 416, 422, 430, 434, 443, 449, 450, 451, 452, + 457, 463, 469, 475, 476, 479, 480, 488, 497, 498, + 507, 508, 514, 517, 518, 519, 521, 529, 537, 546, + 552, 558, 564, 572, 578, 586, 587, 591, 599, 600, + 606, 607, 615, 616, 619, 625, 633, 641, 649, 659, + 662, 666, 672, 673, 674, 677, 678, 682, 686, 690, + 694, 700, 703, 709, 710, 714, 718, 722, 726, 730, + 734, 738, 742, 746 }; #endif @@ -607,16 +614,16 @@ static const char *const yytname[] = "$end", "error", "$undefined", "'|'", "'^'", "'&'", "'<'", "'>'", "'+'", "'-'", "'*'", "'/'", "'%'", "LTYPE1", "LTYPE2", "LTYPE3", "LTYPE4", "LTYPE5", "LTYPE6", "LTYPE7", "LTYPE8", "LTYPE9", "LTYPEA", "LTYPEB", - "LTYPEC", "LTYPED", "LTYPEE", "LTYPEF", "LTYPEG", "LTYPEH", "LTYPEI", - "LTYPEJ", "LTYPEK", "LTYPEL", "LTYPEM", "LTYPEN", "LTYPEBX", "LTYPEPLD", - "LCONST", "LSP", "LSB", "LFP", "LPC", "LTYPEX", "LR", "LREG", "LF", - "LFREG", "LC", "LCREG", "LPSR", "LFCR", "LCOND", "LS", "LAT", "LFCONST", - "LSCONST", "LNAME", "LLAB", "LVAR", "':'", "'='", "';'", "','", "'['", - "']'", "'('", "')'", "'$'", "'~'", "$accept", "prog", "$@1", "line", - "$@2", "$@3", "inst", "cond", "comma", "rel", "ximm", "fcon", "reglist", - "gen", "nireg", "ireg", "ioreg", "oreg", "imsr", "imm", "reg", "regreg", - "shift", "rcon", "sreg", "spreg", "creg", "frcon", "freg", "name", - "offset", "pointer", "con", "oexpr", "expr", 0 + "LTYPEC", "LTYPED", "LTYPEE", "LTYPEG", "LTYPEH", "LTYPEI", "LTYPEJ", + "LTYPEK", "LTYPEL", "LTYPEM", "LTYPEN", "LTYPEBX", "LTYPEPLD", "LCONST", + "LSP", "LSB", "LFP", "LPC", "LTYPEX", "LTYPEPC", "LTYPEF", "LR", "LREG", + "LF", "LFREG", "LC", "LCREG", "LPSR", "LFCR", "LCOND", "LS", "LAT", + "LFCONST", "LSCONST", "LNAME", "LLAB", "LVAR", "':'", "'='", "';'", + "','", "'['", "']'", "'('", "')'", "'$'", "'~'", "$accept", "prog", + "$@1", "line", "$@2", "$@3", "inst", "cond", "comma", "rel", "ximm", + "fcon", "reglist", "gen", "nireg", "ireg", "ioreg", "oreg", "imsr", + "imm", "reg", "regreg", "shift", "rcon", "sreg", "spreg", "creg", + "frcon", "freg", "name", "offset", "pointer", "con", "oexpr", "expr", 0 }; #endif @@ -631,27 +638,28 @@ static const yytype_uint16 yytoknum[] = 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, - 58, 61, 59, 44, 91, 93, 40, 41, 36, 126 + 305, 58, 61, 59, 44, 91, 93, 40, 41, 36, + 126 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { - 0, 70, 71, 72, 71, 74, 73, 75, 73, 73, - 73, 73, 73, 73, 76, 76, 76, 76, 76, 76, - 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, - 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, - 76, 76, 76, 76, 76, 77, 77, 77, 78, 78, - 79, 79, 79, 80, 80, 80, 80, 80, 81, 81, - 82, 82, 82, 83, 83, 83, 83, 83, 83, 83, - 83, 83, 84, 84, 85, 86, 86, 87, 87, 87, - 88, 88, 88, 89, 90, 91, 92, 92, 92, 92, - 93, 93, 94, 94, 94, 95, 95, 96, 96, 97, - 97, 98, 98, 99, 99, 99, 100, 100, 100, 101, - 101, 101, 102, 102, 102, 102, 102, 102, 103, 103, - 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, - 104 + 0, 71, 72, 73, 72, 75, 74, 76, 74, 74, + 74, 74, 74, 74, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 78, 78, + 78, 79, 79, 80, 80, 80, 81, 81, 81, 81, + 81, 82, 82, 83, 83, 83, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 85, 85, 86, 87, 87, + 88, 88, 88, 89, 89, 89, 90, 91, 92, 93, + 93, 93, 93, 94, 94, 95, 95, 95, 96, 96, + 97, 97, 98, 98, 99, 99, 100, 100, 100, 101, + 101, 101, 102, 102, 102, 103, 103, 103, 103, 103, + 103, 104, 104, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ @@ -660,289 +668,297 @@ static const yytype_uint8 yyr2[] = 0, 2, 0, 0, 3, 0, 4, 0, 4, 4, 4, 1, 2, 2, 7, 6, 5, 5, 5, 4, 4, 3, 3, 4, 6, 7, 7, 7, 6, 6, - 3, 4, 6, 6, 4, 3, 5, 5, 7, 6, - 12, 7, 9, 2, 2, 0, 2, 2, 0, 2, - 4, 2, 2, 2, 2, 4, 2, 1, 2, 3, - 1, 3, 3, 1, 1, 1, 4, 1, 1, 1, - 1, 1, 1, 1, 3, 1, 4, 1, 4, 1, - 1, 1, 1, 2, 1, 5, 4, 4, 4, 4, - 1, 1, 1, 1, 4, 1, 1, 1, 4, 1, - 1, 1, 4, 4, 5, 7, 0, 2, 2, 1, - 1, 1, 1, 1, 2, 2, 2, 3, 0, 2, - 1, 3, 3, 3, 3, 3, 4, 4, 3, 3, - 3 + 3, 4, 6, 8, 6, 4, 3, 5, 5, 7, + 6, 12, 7, 9, 2, 4, 4, 2, 0, 2, + 2, 0, 2, 4, 2, 2, 2, 2, 4, 2, + 1, 2, 3, 1, 3, 3, 1, 1, 1, 4, + 1, 1, 1, 1, 1, 1, 1, 3, 1, 4, + 1, 4, 1, 1, 1, 1, 2, 1, 5, 4, + 4, 4, 4, 1, 1, 1, 1, 4, 1, 1, + 1, 4, 1, 1, 1, 4, 4, 5, 7, 0, + 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, + 3, 0, 2, 1, 3, 3, 3, 3, 3, 4, + 4, 3, 3, 3 }; -/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state - STATE-NUM when YYTABLE doesn't specify something else to do. Zero +/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { - 2, 3, 1, 0, 0, 45, 45, 45, 45, 48, - 45, 45, 45, 45, 45, 0, 0, 45, 48, 48, - 45, 45, 45, 45, 45, 45, 48, 0, 0, 0, - 0, 11, 4, 0, 13, 0, 0, 0, 48, 48, - 0, 48, 0, 0, 48, 48, 0, 0, 112, 106, - 113, 0, 0, 0, 0, 0, 0, 44, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 75, 79, 43, - 77, 0, 7, 0, 5, 0, 12, 96, 93, 0, - 92, 46, 47, 0, 0, 81, 80, 82, 95, 84, - 0, 0, 101, 67, 68, 0, 64, 57, 0, 70, - 63, 65, 71, 69, 0, 49, 106, 106, 22, 0, - 0, 0, 0, 0, 0, 0, 0, 84, 30, 115, - 114, 0, 0, 0, 0, 120, 0, 116, 0, 0, - 0, 48, 35, 0, 0, 0, 100, 0, 99, 0, - 0, 0, 0, 21, 0, 0, 0, 0, 0, 0, - 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 58, 56, 54, 53, 0, 0, 106, 19, - 20, 72, 73, 0, 51, 52, 0, 23, 0, 0, - 48, 0, 0, 0, 0, 106, 107, 108, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, - 31, 0, 110, 109, 111, 0, 0, 34, 0, 0, - 0, 0, 0, 0, 0, 74, 0, 0, 8, 9, - 6, 10, 0, 16, 84, 0, 0, 0, 0, 17, - 0, 59, 0, 18, 0, 51, 0, 0, 48, 0, - 0, 0, 0, 0, 48, 0, 0, 130, 129, 128, - 0, 0, 121, 122, 123, 124, 125, 0, 103, 0, - 36, 0, 101, 37, 48, 0, 0, 78, 76, 94, - 15, 86, 90, 91, 87, 88, 89, 102, 55, 66, - 50, 24, 0, 61, 62, 0, 29, 48, 28, 0, - 104, 126, 127, 32, 33, 0, 0, 39, 0, 0, - 14, 26, 25, 27, 0, 0, 38, 0, 41, 0, - 105, 0, 0, 0, 0, 97, 0, 0, 42, 0, - 0, 0, 0, 118, 85, 98, 0, 40, 119 + 2, 3, 1, 0, 0, 48, 48, 48, 48, 51, + 48, 48, 48, 48, 48, 0, 0, 48, 51, 51, + 48, 48, 48, 48, 48, 48, 51, 0, 0, 0, + 0, 0, 0, 11, 4, 0, 13, 0, 0, 0, + 51, 51, 0, 51, 0, 0, 51, 51, 0, 0, + 115, 109, 116, 0, 0, 0, 0, 0, 0, 47, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, + 82, 44, 80, 0, 99, 96, 0, 95, 0, 104, + 70, 71, 0, 67, 60, 0, 73, 66, 68, 98, + 87, 74, 72, 0, 7, 0, 5, 0, 12, 49, + 50, 0, 0, 84, 83, 85, 0, 0, 0, 52, + 109, 109, 22, 0, 0, 0, 0, 0, 0, 0, + 0, 87, 30, 118, 117, 0, 0, 0, 0, 123, + 0, 119, 0, 0, 0, 51, 36, 0, 0, 0, + 103, 0, 102, 0, 0, 0, 0, 21, 0, 0, + 0, 0, 0, 0, 0, 61, 59, 57, 56, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 86, 0, 0, 0, 109, 19, 20, 75, 76, 0, + 54, 55, 0, 23, 0, 0, 51, 0, 0, 0, + 0, 109, 110, 111, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 120, 31, 0, 113, 112, + 114, 0, 0, 35, 0, 0, 0, 0, 0, 0, + 0, 77, 0, 0, 0, 0, 62, 0, 45, 0, + 0, 0, 0, 0, 46, 8, 9, 6, 10, 16, + 87, 17, 18, 54, 0, 0, 51, 0, 0, 0, + 0, 0, 51, 0, 0, 133, 132, 131, 0, 0, + 124, 125, 126, 127, 128, 0, 106, 0, 37, 0, + 104, 38, 51, 0, 0, 81, 79, 97, 105, 58, + 69, 89, 93, 94, 90, 91, 92, 15, 53, 24, + 0, 64, 65, 0, 29, 51, 28, 0, 107, 129, + 130, 32, 34, 0, 0, 40, 0, 0, 14, 26, + 25, 27, 0, 0, 0, 39, 0, 42, 0, 108, + 33, 0, 0, 0, 0, 100, 0, 0, 43, 0, + 0, 0, 0, 121, 88, 101, 0, 41, 122 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { - -1, 1, 3, 32, 149, 147, 33, 35, 105, 108, - 96, 97, 179, 98, 170, 67, 68, 99, 84, 85, - 86, 308, 87, 271, 88, 117, 316, 137, 102, 70, - 124, 205, 125, 327, 126 + -1, 1, 3, 34, 168, 166, 35, 37, 109, 112, + 83, 84, 185, 85, 176, 69, 70, 86, 102, 103, + 87, 317, 88, 281, 89, 121, 326, 141, 91, 72, + 128, 211, 129, 337, 130 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ -#define YYPACT_NINF -114 +#define YYPACT_NINF -128 static const yytype_int16 yypact[] = { - -114, 19, -114, 311, -20, -114, -114, -114, -114, -36, - -114, -114, -114, -114, -114, 413, 413, -114, -36, -36, - -114, -114, -114, -114, -114, -114, -36, 427, -22, -9, - -7, -114, -114, 11, -114, 480, 480, 341, 45, -36, - 229, 45, 480, 210, 436, 45, 435, 435, -114, 152, - -114, 435, 435, 25, 15, 79, 517, -114, 24, 134, - 393, 197, 134, 517, 517, 28, 71, -114, -114, -114, - 35, 58, -114, 435, -114, 435, -114, -114, -114, 69, - -114, -114, -114, 435, 51, -114, -114, -114, -114, 46, - 57, 75, -114, -114, -114, 119, -114, -114, 82, -114, - -114, 88, -114, 58, 368, -114, 159, 159, -114, 113, - 373, 114, 411, 120, 123, 28, 145, -114, -114, -114, - -114, 193, 435, 435, 173, -114, 54, -114, 395, 111, - 435, -36, -114, 178, 182, 21, -114, 198, -114, 201, - 207, 212, 411, -114, 206, 168, 558, 311, 310, 311, - 506, 435, -114, 411, 271, 274, 285, 286, 411, 435, - 87, 228, -114, -114, -114, 58, 373, 411, 152, -114, - -114, -114, -114, 231, -114, -114, 257, -114, 411, 235, - 5, 259, 168, 275, 28, 159, -114, -114, 111, 435, - 435, 435, 333, 344, 435, 435, 435, 435, 435, -114, - -114, 289, -114, -114, -114, 300, 308, -114, 151, 435, - 319, 176, 151, 411, 411, -114, 317, 322, -114, -114, - -114, -114, 224, -114, 312, 71, 71, 71, 71, -114, - 323, -114, 427, -114, 328, 173, 64, 329, -36, 315, - 411, 411, 411, 411, 334, 339, 332, 577, 247, 584, - 435, 435, 273, 273, -114, -114, -114, 340, -114, 24, - -114, 350, 351, -114, -36, 353, 365, -114, -114, -114, - 411, -114, -114, -114, -114, -114, -114, -114, -114, -114, - -114, -114, 439, -114, -114, 364, -114, 157, -114, 398, - -114, 518, 518, -114, -114, 411, 151, -114, 374, 411, - -114, -114, -114, -114, 382, 394, -114, 411, -114, 397, - -114, 155, 403, 411, 392, -114, 404, 411, -114, 435, - 155, 401, 299, 406, -114, -114, 435, -114, 568 + -128, 4, -128, 315, -35, -128, -128, -128, -128, -10, + -128, -128, -128, -128, -128, 44, 44, -128, -10, -10, + -128, -128, -128, -128, -128, -128, -10, 416, 371, 371, + -49, 9, 32, -128, -128, 38, -128, 487, 487, 344, + 69, -10, 391, 69, 487, 209, 489, 69, 317, 317, + -128, 49, -128, 317, 317, 42, 48, 106, 67, -128, + 61, 191, 25, 93, 191, 67, 67, 68, 170, -128, + -128, -128, 72, 84, -128, -128, 86, -128, 109, -128, + -128, -128, 233, -128, -128, 80, -128, -128, 115, -128, + 426, -128, 84, 120, -128, 317, -128, 317, -128, -128, + -128, 317, 137, -128, -128, -128, 148, 155, 397, -128, + 74, 74, -128, 164, 371, 204, 240, 207, 206, 68, + 223, -128, -128, -128, -128, 270, 317, 317, 227, -128, + 183, -128, 90, 160, 317, -10, -128, 234, 237, 16, + -128, 254, -128, 255, 256, 257, 240, -128, 212, 168, + 548, 317, 317, 428, 258, -128, -128, -128, 84, 371, + 240, 318, 316, 335, 348, 371, 315, 502, 315, 512, + -128, 240, 240, 371, 49, -128, -128, -128, -128, 289, + -128, -128, 330, -128, 240, 291, 11, 307, 168, 312, + 68, 74, -128, -128, 160, 317, 317, 317, 377, 379, + 317, 317, 317, 317, 317, -128, -128, 324, -128, -128, + -128, 325, 337, -128, 77, 317, 338, 126, 77, 240, + 240, -128, 339, 342, 249, 347, -128, 416, -128, 352, + 170, 170, 170, 170, -128, -128, -128, -128, -128, -128, + 362, -128, -128, 227, -2, 359, -10, 366, 240, 240, + 240, 240, 375, 336, 384, 562, 590, 597, 317, 317, + 213, 213, -128, -128, -128, 385, -128, 61, -128, 357, + 395, -128, -10, 396, 398, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -128, 240, -128, -128, + 434, -128, -128, 400, -128, 432, -128, 424, -128, 436, + 436, 459, -128, 240, 77, -128, 402, 240, -128, -128, + -128, -128, 404, 317, 411, -128, 240, -128, 415, -128, + -128, 216, 418, 240, 413, -128, 421, 240, -128, 317, + 216, 419, 302, 425, -128, -128, 317, -128, 573 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { - -114, -114, -114, -78, -114, -114, -114, 529, 2, 367, - -50, 415, 48, -88, -114, -48, -40, -21, 47, -113, - -19, -114, -28, 137, -59, -35, 154, -114, -49, 18, - -83, 295, -11, -114, -25 + -128, -128, -128, -77, -128, -128, -128, 538, 50, 382, + -57, 429, 33, -7, -128, -48, -43, -21, 36, -127, + -23, -128, 29, 17, -101, -28, 161, -128, -37, -8, + -65, 299, 2, -128, -32 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which - number is the opposite. If zero, do what YYDEFACT says. - If YYTABLE_NINF, syntax error. */ -#define YYTABLE_NINF -61 + number is the opposite. If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -64 static const yytype_int16 yytable[] = { - 89, 89, 89, 113, 54, 54, 69, 89, 132, 101, - 133, 40, 138, 139, 240, 200, 71, 143, 100, 2, - 57, 58, 177, 174, 175, 116, 103, 39, 65, 109, - 210, 144, 114, 53, 55, 119, 120, 131, 72, 73, - 104, 127, 34, 110, 140, 141, 115, 118, 148, 134, - 150, 74, 154, 155, 75, 156, 171, 189, 190, 191, - 192, 193, 194, 195, 196, 197, 198, 183, 39, 218, - -60, 220, 152, 76, 164, 89, 162, 180, 233, 46, - 47, 129, 101, 90, 165, 235, 216, 217, 128, 111, - 130, 100, 95, 173, 142, 46, 47, 81, 82, 103, - 157, 145, 245, 202, 203, 204, 237, 144, 39, 48, - 77, 186, 187, 78, 153, 79, 80, 201, 224, 206, - 158, 199, 172, 217, 146, 48, 222, 46, 160, 161, - 50, 89, 234, 207, 223, 151, 244, 51, 101, 229, - 52, 159, 231, 238, 293, 166, 50, 100, 230, 120, - 202, 203, 204, 51, 167, 103, 52, 48, 121, 260, - 122, 123, 263, 264, 247, 248, 249, 122, 123, 252, - 253, 254, 255, 256, 162, 163, 49, 178, 50, 176, - 91, 92, 241, 181, 261, 66, 81, 82, 52, 182, - 272, 272, 272, 272, 265, 266, 77, 91, 92, 78, - 185, 79, 80, 314, 315, 283, 180, 180, 184, 294, - 78, 278, 79, 80, 273, 273, 273, 273, 46, 47, - 39, 71, 91, 262, 286, 291, 292, 189, 190, 191, - 192, 193, 194, 195, 196, 197, 198, 46, 47, 188, - 281, 208, 301, 91, 92, 209, 288, 306, 48, 81, - 82, 300, 191, 192, 193, 194, 195, 196, 197, 198, - 305, 211, 81, 82, 212, 135, 297, 48, 303, 50, - 213, 114, 312, 215, 112, 214, 66, 225, 318, 52, - 309, 226, 321, 196, 197, 198, 106, 107, 50, 284, - 285, 269, 227, 228, 322, 51, 232, 236, 52, 237, - 239, 328, 189, 190, 191, 192, 193, 194, 195, 196, - 197, 198, 4, 189, 190, 191, 192, 193, 194, 195, - 196, 197, 198, 242, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 243, 250, - 19, 20, 21, 22, 23, 24, 25, 26, 27, 46, - 47, 251, 257, 189, 190, 191, 192, 193, 194, 195, - 196, 197, 198, 274, 275, 276, 325, 258, 28, 29, - 30, 259, 219, 31, 231, 270, 46, 47, 282, 48, - 77, 46, 47, 78, 267, 79, 80, 91, 92, 268, - 277, 93, 94, 81, 82, 279, 280, 287, 49, 290, - 50, 46, 47, 46, 47, 289, 48, 66, 83, 95, - 52, 48, 77, 295, 296, 78, 298, 79, 80, 91, - 92, 46, 47, 93, 94, 168, 107, 50, 299, 302, - 49, 48, 50, 48, 66, 46, 47, 52, 304, 66, - 307, 95, 52, 46, 47, 81, 82, 46, 47, 310, - 77, 48, 50, 78, 50, 79, 80, 311, 319, 51, - 313, 51, 52, 83, 52, 48, 317, 320, 324, 326, - 49, 169, 50, 48, 323, 77, 136, 48, 78, 51, - 79, 80, 52, 246, 49, 0, 50, 0, 81, 82, - 0, 0, 0, 66, 50, 0, 52, 0, 50, 39, - 0, 51, 0, 0, 52, 66, 0, 0, 52, 189, - 190, 191, 192, 193, 194, 195, 196, 197, 198, 77, - 0, 0, 78, 0, 79, 80, 194, 195, 196, 197, - 198, 0, 81, 82, 0, 36, 37, 38, 0, 41, - 42, 43, 44, 45, 0, 0, 56, 0, 83, 59, - 60, 61, 62, 63, 64, 0, 77, 0, 0, 78, - 0, 79, 80, 0, 0, 0, 0, 0, 221, 81, - 82, 189, 190, 191, 192, 193, 194, 195, 196, 197, - 198, 190, 191, 192, 193, 194, 195, 196, 197, 198, - 192, 193, 194, 195, 196, 197, 198, 202, 203, 204, - 78, 0, 79, 80 + 90, 90, 117, 136, 2, 206, 71, 55, 57, 90, + 90, 90, 94, 95, 104, 104, 90, 56, 56, 147, + 248, 104, 93, 120, 137, 216, 142, 143, 36, 73, + 92, 92, 107, 48, 49, 135, 208, 209, 210, 245, + 148, 92, 144, 145, 113, 180, 181, 118, 222, 223, + 123, 124, 48, 49, 41, 125, 131, 126, 127, 42, + 177, 157, 50, 167, 138, 169, 105, 105, 59, 60, + 96, 189, 155, 105, 106, 41, 67, -63, 99, 100, + 115, 50, 126, 127, 158, 52, 90, 223, 186, 235, + 108, 237, 53, 114, 97, 54, 119, 122, 48, 49, + 178, 98, 51, 170, 52, 74, 132, 183, 75, 243, + 179, 53, 76, 77, 54, 133, 92, 134, 148, 224, + 99, 100, 99, 100, 78, 79, 253, 50, 192, 193, + 82, 90, 229, 41, 207, 146, 212, 90, 301, 149, + 78, 79, 252, 240, 159, 90, 99, 100, 239, 241, + 52, 150, 228, 151, 225, 124, 246, 53, 234, 101, + 54, 92, 139, 255, 256, 257, 242, 92, 260, 261, + 262, 263, 264, 78, 270, 92, 152, 268, 48, 49, + 271, 272, 160, 269, 165, 213, 195, 196, 197, 198, + 199, 200, 201, 202, 203, 204, 273, 274, 208, 209, + 210, 171, 282, 282, 282, 282, 279, 50, 74, 75, + 302, 75, 172, 76, 77, 76, 77, 48, 49, 173, + 291, 186, 186, 202, 203, 204, 299, 300, 294, 73, + 52, 182, 283, 283, 283, 283, 249, 53, 78, 79, + 54, 48, 153, 154, 99, 100, 50, 309, 284, 285, + 286, 205, 195, 196, 197, 198, 199, 200, 201, 202, + 203, 204, 99, 100, 308, 324, 325, 315, 184, 52, + 50, 187, 311, 188, 116, 314, 68, 191, 74, 54, + 221, 75, 292, 293, 318, 76, 77, 190, 322, 155, + 156, 51, 118, 52, 194, 328, 289, 332, 214, 331, + 68, 215, 296, 54, 338, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 320, 4, 277, 217, 218, + 219, 220, 305, 231, 230, 48, 49, 227, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 232, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 48, 49, 50, 233, 244, 247, 28, 29, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 335, 245, 250, 30, 31, 32, 251, 52, 33, 48, + 49, 50, 74, 258, 53, 75, 259, 54, 265, 76, + 77, 78, 79, 266, 226, 80, 81, 99, 100, 48, + 49, 267, 51, 297, 52, 48, 49, 275, 50, 74, + 276, 68, 75, 82, 54, 278, 76, 77, 78, 79, + 280, 303, 80, 81, 48, 49, 287, 288, 50, 51, + 290, 52, 161, 162, 50, 163, 48, 49, 68, 295, + 82, 54, 48, 49, 200, 201, 202, 203, 204, 110, + 111, 52, 298, 50, 101, 174, 111, 52, 53, 304, + 306, 54, 307, 312, 68, 50, 310, 54, 313, 316, + 74, 50, 319, 75, 51, 321, 52, 76, 77, 323, + 329, 164, 327, 68, 226, 330, 54, 334, 52, 336, + 175, 333, 140, 254, 52, 53, 41, 0, 54, 0, + 0, 68, 0, 0, 54, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 74, 0, 74, 75, 0, + 75, 0, 76, 77, 76, 77, 0, 0, 0, 0, + 99, 100, 99, 100, 38, 39, 40, 0, 43, 44, + 45, 46, 47, 41, 0, 58, 101, 0, 61, 62, + 63, 64, 65, 66, 0, 236, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 238, 195, 196, 197, 198, + 199, 200, 201, 202, 203, 204, 208, 209, 210, 75, + 0, 0, 0, 76, 77, 197, 198, 199, 200, 201, + 202, 203, 204, 198, 199, 200, 201, 202, 203, 204 }; +#define yypact_value_is_default(yystate) \ + ((yystate) == (-128)) + +#define yytable_value_is_error(yytable_value) \ + YYID (0) + static const yytype_int16 yycheck[] = { - 35, 36, 37, 43, 15, 16, 27, 42, 58, 37, - 59, 9, 61, 62, 9, 128, 27, 65, 37, 0, - 18, 19, 110, 106, 107, 44, 37, 63, 26, 40, - 9, 66, 43, 15, 16, 46, 47, 56, 60, 61, - 38, 52, 62, 41, 63, 64, 44, 45, 73, 60, - 75, 60, 6, 7, 61, 9, 104, 3, 4, 5, - 6, 7, 8, 9, 10, 11, 12, 115, 63, 147, - 65, 149, 83, 62, 95, 110, 55, 112, 166, 8, - 9, 66, 110, 36, 95, 168, 145, 146, 63, 42, - 11, 110, 68, 104, 66, 8, 9, 52, 53, 110, - 54, 66, 185, 39, 40, 41, 42, 142, 63, 38, - 39, 122, 123, 42, 63, 44, 45, 128, 153, 130, - 63, 67, 104, 182, 66, 38, 151, 8, 9, 10, - 59, 166, 167, 131, 153, 66, 184, 66, 166, 158, - 69, 66, 55, 178, 257, 63, 59, 166, 159, 160, - 39, 40, 41, 66, 66, 166, 69, 38, 6, 208, - 8, 9, 211, 212, 189, 190, 191, 8, 9, 194, - 195, 196, 197, 198, 55, 56, 57, 63, 59, 66, - 46, 47, 180, 63, 209, 66, 52, 53, 69, 66, - 225, 226, 227, 228, 213, 214, 39, 46, 47, 42, - 7, 44, 45, 48, 49, 240, 241, 242, 63, 259, - 42, 232, 44, 45, 225, 226, 227, 228, 8, 9, - 63, 232, 46, 47, 243, 250, 251, 3, 4, 5, - 6, 7, 8, 9, 10, 11, 12, 8, 9, 66, - 238, 63, 282, 46, 47, 63, 244, 296, 38, 52, - 53, 270, 5, 6, 7, 8, 9, 10, 11, 12, - 295, 63, 52, 53, 63, 68, 264, 38, 287, 59, - 63, 282, 307, 67, 64, 63, 66, 6, 313, 69, - 299, 7, 317, 10, 11, 12, 57, 58, 59, 241, - 242, 67, 7, 7, 319, 66, 68, 66, 69, 42, - 65, 326, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 1, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 64, 13, 14, 15, 16, 17, 18, - 19, 20, 21, 22, 23, 24, 25, 26, 63, 6, - 29, 30, 31, 32, 33, 34, 35, 36, 37, 8, - 9, 7, 63, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 226, 227, 228, 67, 67, 57, 58, - 59, 63, 62, 62, 55, 63, 8, 9, 63, 38, - 39, 8, 9, 42, 67, 44, 45, 46, 47, 67, - 67, 50, 51, 52, 53, 67, 67, 63, 57, 67, - 59, 8, 9, 8, 9, 66, 38, 66, 68, 68, - 69, 38, 39, 63, 63, 42, 63, 44, 45, 46, - 47, 8, 9, 50, 51, 57, 58, 59, 63, 65, - 57, 38, 59, 38, 66, 8, 9, 69, 40, 66, - 66, 68, 69, 8, 9, 52, 53, 8, 9, 67, - 39, 38, 59, 42, 59, 44, 45, 63, 66, 66, - 63, 66, 69, 68, 69, 38, 63, 63, 67, 63, - 57, 104, 59, 38, 320, 39, 61, 38, 42, 66, - 44, 45, 69, 188, 57, -1, 59, -1, 52, 53, - -1, -1, -1, 66, 59, -1, 69, -1, 59, 63, - -1, 66, -1, -1, 69, 66, -1, -1, 69, 3, - 4, 5, 6, 7, 8, 9, 10, 11, 12, 39, - -1, -1, 42, -1, 44, 45, 8, 9, 10, 11, - 12, -1, 52, 53, -1, 6, 7, 8, -1, 10, - 11, 12, 13, 14, -1, -1, 17, -1, 68, 20, - 21, 22, 23, 24, 25, -1, 39, -1, -1, 42, - -1, 44, 45, -1, -1, -1, -1, -1, 62, 52, - 53, 3, 4, 5, 6, 7, 8, 9, 10, 11, - 12, 4, 5, 6, 7, 8, 9, 10, 11, 12, - 6, 7, 8, 9, 10, 11, 12, 39, 40, 41, - 42, -1, 44, 45 + 28, 29, 45, 60, 0, 132, 27, 15, 16, 37, + 38, 39, 61, 62, 37, 38, 44, 15, 16, 67, + 9, 44, 29, 46, 61, 9, 63, 64, 63, 27, + 28, 29, 39, 8, 9, 58, 38, 39, 40, 41, + 68, 39, 65, 66, 42, 110, 111, 45, 149, 150, + 48, 49, 8, 9, 64, 6, 54, 8, 9, 9, + 108, 82, 37, 95, 62, 97, 37, 38, 18, 19, + 61, 119, 56, 44, 38, 64, 26, 66, 53, 54, + 44, 37, 8, 9, 82, 60, 114, 188, 116, 166, + 40, 168, 67, 43, 62, 70, 46, 47, 8, 9, + 108, 63, 58, 101, 60, 38, 64, 114, 41, 174, + 108, 67, 45, 46, 70, 67, 114, 11, 146, 151, + 53, 54, 53, 54, 47, 48, 191, 37, 126, 127, + 69, 159, 160, 64, 132, 67, 134, 165, 265, 67, + 47, 48, 190, 171, 64, 173, 53, 54, 171, 172, + 60, 67, 159, 67, 152, 153, 184, 67, 165, 69, + 70, 159, 69, 195, 196, 197, 173, 165, 200, 201, + 202, 203, 204, 47, 48, 173, 67, 214, 8, 9, + 217, 218, 67, 215, 64, 135, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 219, 220, 38, 39, + 40, 64, 230, 231, 232, 233, 227, 37, 38, 41, + 267, 41, 64, 45, 46, 45, 46, 8, 9, 64, + 248, 249, 250, 10, 11, 12, 258, 259, 251, 227, + 60, 67, 230, 231, 232, 233, 186, 67, 47, 48, + 70, 8, 9, 10, 53, 54, 37, 290, 231, 232, + 233, 68, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 53, 54, 287, 49, 50, 304, 64, 60, + 37, 64, 295, 67, 65, 303, 67, 7, 38, 70, + 68, 41, 249, 250, 307, 45, 46, 64, 316, 56, + 57, 58, 290, 60, 67, 323, 246, 329, 64, 327, + 67, 64, 252, 70, 336, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 313, 1, 68, 64, 64, + 64, 64, 272, 7, 6, 8, 9, 69, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 7, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 8, 9, 37, 7, 67, 66, 43, 44, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 68, 41, 65, 58, 59, 60, 64, 60, 63, 8, + 9, 37, 38, 6, 67, 41, 7, 70, 64, 45, + 46, 47, 48, 68, 56, 51, 52, 53, 54, 8, + 9, 64, 58, 67, 60, 8, 9, 68, 37, 38, + 68, 67, 41, 69, 70, 68, 45, 46, 47, 48, + 68, 64, 51, 52, 8, 9, 64, 68, 37, 58, + 64, 60, 6, 7, 37, 9, 8, 9, 67, 64, + 69, 70, 8, 9, 8, 9, 10, 11, 12, 58, + 59, 60, 68, 37, 69, 58, 59, 60, 67, 64, + 64, 70, 64, 39, 67, 37, 66, 70, 9, 67, + 38, 37, 68, 41, 58, 64, 60, 45, 46, 64, + 67, 55, 64, 67, 56, 64, 70, 68, 60, 64, + 108, 330, 63, 194, 60, 67, 64, -1, 70, -1, + -1, 67, -1, -1, 70, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 38, -1, 38, 41, -1, + 41, -1, 45, 46, 45, 46, -1, -1, -1, -1, + 53, 54, 53, 54, 6, 7, 8, -1, 10, 11, + 12, 13, 14, 64, -1, 17, 69, -1, 20, 21, + 22, 23, 24, 25, -1, 63, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 63, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 38, 39, 40, 41, + -1, -1, -1, 45, 46, 5, 6, 7, 8, 9, + 10, 11, 12, 6, 7, 8, 9, 10, 11, 12 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { - 0, 71, 0, 72, 1, 13, 14, 15, 16, 17, - 18, 19, 20, 21, 22, 23, 24, 25, 26, 29, - 30, 31, 32, 33, 34, 35, 36, 37, 57, 58, - 59, 62, 73, 76, 62, 77, 77, 77, 77, 63, - 78, 77, 77, 77, 77, 77, 8, 9, 38, 57, - 59, 66, 69, 99, 102, 99, 77, 78, 78, 77, - 77, 77, 77, 77, 77, 78, 66, 85, 86, 87, - 99, 102, 60, 61, 60, 61, 62, 39, 42, 44, - 45, 52, 53, 68, 88, 89, 90, 92, 94, 95, - 88, 46, 47, 50, 51, 68, 80, 81, 83, 87, - 90, 92, 98, 102, 78, 78, 57, 58, 79, 102, - 78, 88, 64, 86, 102, 78, 90, 95, 78, 102, - 102, 6, 8, 9, 100, 102, 104, 102, 63, 66, - 11, 90, 80, 98, 102, 68, 81, 97, 98, 98, - 90, 90, 66, 85, 95, 66, 66, 75, 104, 74, - 104, 66, 102, 63, 6, 7, 9, 54, 63, 66, - 9, 10, 55, 56, 87, 102, 63, 66, 57, 79, - 84, 85, 99, 102, 100, 100, 66, 83, 63, 82, - 95, 63, 66, 85, 63, 7, 102, 102, 66, 3, - 4, 5, 6, 7, 8, 9, 10, 11, 12, 67, - 89, 102, 39, 40, 41, 101, 102, 78, 63, 63, - 9, 63, 63, 63, 63, 67, 94, 94, 73, 62, - 73, 62, 104, 90, 95, 6, 7, 7, 7, 90, - 102, 55, 68, 83, 95, 100, 66, 42, 95, 65, - 9, 78, 64, 63, 85, 100, 101, 104, 104, 104, - 6, 7, 104, 104, 104, 104, 104, 63, 67, 63, - 98, 104, 47, 98, 98, 90, 90, 67, 67, 67, - 63, 93, 95, 102, 93, 93, 93, 67, 87, 67, - 67, 78, 63, 95, 82, 82, 90, 63, 78, 66, - 67, 104, 104, 89, 80, 63, 63, 78, 63, 63, - 90, 86, 65, 90, 40, 95, 98, 66, 91, 90, - 67, 63, 95, 63, 48, 49, 96, 63, 95, 66, - 63, 95, 104, 96, 67, 67, 63, 103, 104 + 0, 72, 0, 73, 1, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 43, 44, + 58, 59, 60, 63, 74, 77, 63, 78, 78, 78, + 78, 64, 79, 78, 78, 78, 78, 78, 8, 9, + 37, 58, 60, 67, 70, 100, 103, 100, 78, 79, + 79, 78, 78, 78, 78, 78, 78, 79, 67, 86, + 87, 88, 100, 103, 38, 41, 45, 46, 47, 48, + 51, 52, 69, 81, 82, 84, 88, 91, 93, 95, + 96, 99, 103, 84, 61, 62, 61, 62, 63, 53, + 54, 69, 89, 90, 91, 93, 89, 84, 79, 79, + 58, 59, 80, 103, 79, 89, 65, 87, 103, 79, + 91, 96, 79, 103, 103, 6, 8, 9, 101, 103, + 105, 103, 64, 67, 11, 91, 81, 99, 103, 69, + 82, 98, 99, 99, 91, 91, 67, 86, 96, 67, + 67, 67, 67, 9, 10, 56, 57, 88, 103, 64, + 67, 6, 7, 9, 55, 64, 76, 105, 75, 105, + 103, 64, 64, 64, 58, 80, 85, 86, 100, 103, + 101, 101, 67, 84, 64, 83, 96, 64, 67, 86, + 64, 7, 103, 103, 67, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 68, 90, 103, 38, 39, + 40, 102, 103, 79, 64, 64, 9, 64, 64, 64, + 64, 68, 95, 95, 105, 103, 56, 69, 84, 96, + 6, 7, 7, 7, 84, 74, 63, 74, 63, 91, + 96, 91, 84, 101, 67, 41, 96, 66, 9, 79, + 65, 64, 86, 101, 102, 105, 105, 105, 6, 7, + 105, 105, 105, 105, 105, 64, 68, 64, 99, 105, + 48, 99, 99, 91, 91, 68, 68, 68, 68, 88, + 68, 94, 96, 103, 94, 94, 94, 64, 68, 79, + 64, 96, 83, 83, 91, 64, 79, 67, 68, 105, + 105, 90, 81, 64, 64, 79, 64, 64, 91, 87, + 66, 91, 39, 9, 96, 99, 67, 92, 91, 68, + 103, 64, 96, 64, 49, 50, 97, 64, 96, 67, + 64, 96, 105, 97, 68, 68, 64, 104, 105 }; #define yyerrok (yyerrstatus = 0) @@ -957,9 +973,18 @@ static const yytype_uint8 yystos[] = /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. - Once GCC version 2 has supplanted version 1, this can go. */ + Once GCC version 2 has supplanted version 1, this can go. However, + YYFAIL appears to be in use. Nevertheless, it is formally deprecated + in Bison 2.4.2's NEWS entry, where a plan to phase it out is + discussed. */ #define YYFAIL goto yyerrlab +#if defined YYFAIL + /* This is here to suppress warnings from the GCC cpp's + -Wunused-macros. Normally we don't worry about that warning, but + some users do, and we want to make it easy for users to remove + YYFAIL uses, which will produce warnings from Bison 2.5. */ +#endif #define YYRECOVERING() (!!yyerrstatus) @@ -969,7 +994,6 @@ do \ { \ yychar = (Token); \ yylval = (Value); \ - yytoken = YYTRANSLATE (yychar); \ YYPOPSTACK (1); \ goto yybackup; \ } \ @@ -1011,19 +1035,10 @@ while (YYID (0)) #endif -/* YY_LOCATION_PRINT -- Print the location on the stream. - This macro was not mandated originally: define only if we know - we won't break user code: when these are the locations we know. */ +/* This macro is provided for backward compatibility. */ #ifndef YY_LOCATION_PRINT -# if YYLTYPE_IS_TRIVIAL -# define YY_LOCATION_PRINT(File, Loc) \ - fprintf (File, "%d.%d-%d.%d", \ - (Loc).first_line, (Loc).first_column, \ - (Loc).last_line, (Loc).last_column) -# else -# define YY_LOCATION_PRINT(File, Loc) ((void) 0) -# endif +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) #endif @@ -1215,7 +1230,6 @@ int yydebug; # define YYMAXDEPTH 10000 #endif - #if YYERROR_VERBOSE @@ -1318,115 +1332,142 @@ yytnamerr (char *yyres, const char *yystr) } # endif -/* Copy into YYRESULT an error message about the unexpected token - YYCHAR while in state YYSTATE. Return the number of bytes copied, - including the terminating null byte. If YYRESULT is null, do not - copy anything; just return the number of bytes that would be - copied. As a special case, return 0 if an ordinary "syntax error" - message will do. Return YYSIZE_MAXIMUM if overflow occurs during - size calculation. */ -static YYSIZE_T -yysyntax_error (char *yyresult, int yystate, int yychar) -{ - int yyn = yypact[yystate]; +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. - if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) - return 0; - else - { - int yytype = YYTRANSLATE (yychar); - YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); - YYSIZE_T yysize = yysize0; - YYSIZE_T yysize1; - int yysize_overflow = 0; - enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; - char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; - int yyx; - -# if 0 - /* This is so xgettext sees the translatable formats that are - constructed on the fly. */ - YY_("syntax error, unexpected %s"); - YY_("syntax error, unexpected %s, expecting %s"); - YY_("syntax error, unexpected %s, expecting %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); -# endif - char *yyfmt; - char const *yyf; - static char const yyunexpected[] = "syntax error, unexpected %s"; - static char const yyexpecting[] = ", expecting %s"; - static char const yyor[] = " or %s"; - char yyformat[sizeof yyunexpected - + sizeof yyexpecting - 1 - + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) - * (sizeof yyor - 1))]; - char const *yyprefix = yyexpecting; - - /* Start YYX at -YYN if negative to avoid negative indexes in - YYCHECK. */ - int yyxbegin = yyn < 0 ? -yyn : 0; - - /* Stay within bounds of both yycheck and yytname. */ - int yychecklim = YYLAST - yyn + 1; - int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; - int yycount = 1; - - yyarg[0] = yytname[yytype]; - yyfmt = yystpcpy (yyformat, yyunexpected); - - for (yyx = yyxbegin; yyx < yyxend; ++yyx) - if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) - { - if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) - { - yycount = 1; - yysize = yysize0; - yyformat[sizeof yyunexpected - 1] = '\0'; - break; - } - yyarg[yycount++] = yytname[yyx]; - yysize1 = yysize + yytnamerr (0, yytname[yyx]); - yysize_overflow |= (yysize1 < yysize); - yysize = yysize1; - yyfmt = yystpcpy (yyfmt, yyprefix); - yyprefix = yyor; - } + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) +{ + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = 0; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; + + /* There are many possibilities here to consider: + - Assume YYFAIL is not used. It's too flawed to consider. See + <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html> + for details. YYERROR is fine as it does not invoke this + function. + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } - yyf = YY_(yyformat); - yysize1 = yysize + yystrlen (yyf); - yysize_overflow |= (yysize1 < yysize); - yysize = yysize1; + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } - if (yysize_overflow) - return YYSIZE_MAXIMUM; + yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; - if (yyresult) - { - /* Avoid sprintf, as that infringes on the user's name space. - Don't have undefined behavior even if the translation - produced a string with the wrong number of "%s"s. */ - char *yyp = yyresult; - int yyi = 0; - while ((*yyp = *yyf) != '\0') - { - if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) - { - yyp += yytnamerr (yyp, yyarg[yyi++]); - yyf += 2; - } - else - { - yyp++; - yyf++; - } - } - } - return yysize; + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; } #endif /* YYERROR_VERBOSE */ - /*-----------------------------------------------. | Release the memory associated to this symbol. | @@ -1459,6 +1500,7 @@ yydestruct (yymsg, yytype, yyvaluep) } } + /* Prevent warnings from -Wmissing-prototypes. */ #ifdef YYPARSE_PARAM #if defined __STDC__ || defined __cplusplus @@ -1485,10 +1527,9 @@ YYSTYPE yylval; int yynerrs; - -/*-------------------------. -| yyparse or yypush_parse. | -`-------------------------*/ +/*----------. +| yyparse. | +`----------*/ #ifdef YYPARSE_PARAM #if (defined __STDC__ || defined __C99__FUNC__ \ @@ -1512,8 +1553,6 @@ yyparse () #endif #endif { - - int yystate; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; @@ -1668,7 +1707,7 @@ yybackup: /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; - if (yyn == YYPACT_NINF) + if (yypact_value_is_default (yyn)) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ @@ -1699,8 +1738,8 @@ yybackup: yyn = yytable[yyn]; if (yyn <= 0) { - if (yyn == 0 || yyn == YYTABLE_NINF) - goto yyerrlab; + if (yytable_value_is_error (yyn)) + goto yyerrlab; yyn = -yyn; goto yyreduce; } @@ -1755,8 +1794,8 @@ yyreduce: { case 3: -/* Line 1455 of yacc.c */ -#line 69 "a.y" +/* Line 1806 of yacc.c */ +#line 70 "a.y" { stmtline = lineno; } @@ -1764,8 +1803,8 @@ yyreduce: case 5: -/* Line 1455 of yacc.c */ -#line 76 "a.y" +/* Line 1806 of yacc.c */ +#line 77 "a.y" { if((yyvsp[(1) - (2)].sym)->value != pc) yyerror("redeclaration of %s", (yyvsp[(1) - (2)].sym)->name); @@ -1775,8 +1814,8 @@ yyreduce: case 7: -/* Line 1455 of yacc.c */ -#line 83 "a.y" +/* Line 1806 of yacc.c */ +#line 84 "a.y" { (yyvsp[(1) - (2)].sym)->type = LLAB; (yyvsp[(1) - (2)].sym)->value = pc; @@ -1785,8 +1824,8 @@ yyreduce: case 9: -/* Line 1455 of yacc.c */ -#line 89 "a.y" +/* Line 1806 of yacc.c */ +#line 90 "a.y" { (yyvsp[(1) - (4)].sym)->type = LVAR; (yyvsp[(1) - (4)].sym)->value = (yyvsp[(3) - (4)].lval); @@ -1795,8 +1834,8 @@ yyreduce: case 10: -/* Line 1455 of yacc.c */ -#line 94 "a.y" +/* Line 1806 of yacc.c */ +#line 95 "a.y" { if((yyvsp[(1) - (4)].sym)->value != (yyvsp[(3) - (4)].lval)) yyerror("redeclaration of %s", (yyvsp[(1) - (4)].sym)->name); @@ -1806,8 +1845,8 @@ yyreduce: case 14: -/* Line 1455 of yacc.c */ -#line 108 "a.y" +/* Line 1806 of yacc.c */ +#line 109 "a.y" { outcode((yyvsp[(1) - (7)].lval), (yyvsp[(2) - (7)].lval), &(yyvsp[(3) - (7)].gen), (yyvsp[(5) - (7)].lval), &(yyvsp[(7) - (7)].gen)); } @@ -1815,8 +1854,8 @@ yyreduce: case 15: -/* Line 1455 of yacc.c */ -#line 112 "a.y" +/* Line 1806 of yacc.c */ +#line 113 "a.y" { outcode((yyvsp[(1) - (6)].lval), (yyvsp[(2) - (6)].lval), &(yyvsp[(3) - (6)].gen), (yyvsp[(5) - (6)].lval), &nullgen); } @@ -1824,8 +1863,8 @@ yyreduce: case 16: -/* Line 1455 of yacc.c */ -#line 116 "a.y" +/* Line 1806 of yacc.c */ +#line 117 "a.y" { outcode((yyvsp[(1) - (5)].lval), (yyvsp[(2) - (5)].lval), &(yyvsp[(3) - (5)].gen), NREG, &(yyvsp[(5) - (5)].gen)); } @@ -1833,8 +1872,8 @@ yyreduce: case 17: -/* Line 1455 of yacc.c */ -#line 123 "a.y" +/* Line 1806 of yacc.c */ +#line 124 "a.y" { outcode((yyvsp[(1) - (5)].lval), (yyvsp[(2) - (5)].lval), &(yyvsp[(3) - (5)].gen), NREG, &(yyvsp[(5) - (5)].gen)); } @@ -1842,8 +1881,8 @@ yyreduce: case 18: -/* Line 1455 of yacc.c */ -#line 130 "a.y" +/* Line 1806 of yacc.c */ +#line 131 "a.y" { outcode((yyvsp[(1) - (5)].lval), (yyvsp[(2) - (5)].lval), &(yyvsp[(3) - (5)].gen), NREG, &(yyvsp[(5) - (5)].gen)); } @@ -1851,8 +1890,8 @@ yyreduce: case 19: -/* Line 1455 of yacc.c */ -#line 137 "a.y" +/* Line 1806 of yacc.c */ +#line 138 "a.y" { outcode((yyvsp[(1) - (4)].lval), (yyvsp[(2) - (4)].lval), &nullgen, NREG, &(yyvsp[(4) - (4)].gen)); } @@ -1860,8 +1899,8 @@ yyreduce: case 20: -/* Line 1455 of yacc.c */ -#line 141 "a.y" +/* Line 1806 of yacc.c */ +#line 142 "a.y" { outcode((yyvsp[(1) - (4)].lval), (yyvsp[(2) - (4)].lval), &nullgen, NREG, &(yyvsp[(4) - (4)].gen)); } @@ -1869,8 +1908,8 @@ yyreduce: case 21: -/* Line 1455 of yacc.c */ -#line 148 "a.y" +/* Line 1806 of yacc.c */ +#line 149 "a.y" { outcode((yyvsp[(1) - (3)].lval), Always, &nullgen, NREG, &(yyvsp[(3) - (3)].gen)); } @@ -1878,8 +1917,8 @@ yyreduce: case 22: -/* Line 1455 of yacc.c */ -#line 155 "a.y" +/* Line 1806 of yacc.c */ +#line 156 "a.y" { outcode((yyvsp[(1) - (3)].lval), Always, &nullgen, NREG, &(yyvsp[(3) - (3)].gen)); } @@ -1887,8 +1926,8 @@ yyreduce: case 23: -/* Line 1455 of yacc.c */ -#line 162 "a.y" +/* Line 1806 of yacc.c */ +#line 163 "a.y" { outcode((yyvsp[(1) - (4)].lval), (yyvsp[(2) - (4)].lval), &nullgen, NREG, &(yyvsp[(4) - (4)].gen)); } @@ -1896,8 +1935,8 @@ yyreduce: case 24: -/* Line 1455 of yacc.c */ -#line 169 "a.y" +/* Line 1806 of yacc.c */ +#line 170 "a.y" { outcode((yyvsp[(1) - (6)].lval), (yyvsp[(2) - (6)].lval), &(yyvsp[(3) - (6)].gen), (yyvsp[(5) - (6)].lval), &nullgen); } @@ -1905,8 +1944,8 @@ yyreduce: case 25: -/* Line 1455 of yacc.c */ -#line 176 "a.y" +/* Line 1806 of yacc.c */ +#line 177 "a.y" { Gen g; @@ -1919,8 +1958,8 @@ yyreduce: case 26: -/* Line 1455 of yacc.c */ -#line 185 "a.y" +/* Line 1806 of yacc.c */ +#line 186 "a.y" { Gen g; @@ -1933,8 +1972,8 @@ yyreduce: case 27: -/* Line 1455 of yacc.c */ -#line 197 "a.y" +/* Line 1806 of yacc.c */ +#line 198 "a.y" { outcode((yyvsp[(1) - (7)].lval), (yyvsp[(2) - (7)].lval), &(yyvsp[(5) - (7)].gen), (yyvsp[(3) - (7)].gen).reg, &(yyvsp[(7) - (7)].gen)); } @@ -1942,8 +1981,8 @@ yyreduce: case 28: -/* Line 1455 of yacc.c */ -#line 201 "a.y" +/* Line 1806 of yacc.c */ +#line 202 "a.y" { outcode((yyvsp[(1) - (6)].lval), (yyvsp[(2) - (6)].lval), &(yyvsp[(5) - (6)].gen), (yyvsp[(3) - (6)].gen).reg, &(yyvsp[(3) - (6)].gen)); } @@ -1951,8 +1990,8 @@ yyreduce: case 29: -/* Line 1455 of yacc.c */ -#line 205 "a.y" +/* Line 1806 of yacc.c */ +#line 206 "a.y" { outcode((yyvsp[(1) - (6)].lval), (yyvsp[(2) - (6)].lval), &(yyvsp[(4) - (6)].gen), (yyvsp[(6) - (6)].gen).reg, &(yyvsp[(6) - (6)].gen)); } @@ -1960,8 +1999,8 @@ yyreduce: case 30: -/* Line 1455 of yacc.c */ -#line 212 "a.y" +/* Line 1806 of yacc.c */ +#line 213 "a.y" { outcode((yyvsp[(1) - (3)].lval), (yyvsp[(2) - (3)].lval), &nullgen, NREG, &nullgen); } @@ -1969,62 +2008,68 @@ yyreduce: case 31: -/* Line 1455 of yacc.c */ -#line 219 "a.y" +/* Line 1806 of yacc.c */ +#line 220 "a.y" { + (yyvsp[(4) - (4)].gen).type = D_CONST2; + (yyvsp[(4) - (4)].gen).offset2 = ArgsSizeUnknown; outcode((yyvsp[(1) - (4)].lval), Always, &(yyvsp[(2) - (4)].gen), 0, &(yyvsp[(4) - (4)].gen)); } break; case 32: -/* Line 1455 of yacc.c */ -#line 223 "a.y" +/* Line 1806 of yacc.c */ +#line 226 "a.y" { + (yyvsp[(6) - (6)].gen).type = D_CONST2; + (yyvsp[(6) - (6)].gen).offset2 = ArgsSizeUnknown; outcode((yyvsp[(1) - (6)].lval), Always, &(yyvsp[(2) - (6)].gen), (yyvsp[(4) - (6)].lval), &(yyvsp[(6) - (6)].gen)); } break; case 33: -/* Line 1455 of yacc.c */ -#line 230 "a.y" +/* Line 1806 of yacc.c */ +#line 232 "a.y" { - outcode((yyvsp[(1) - (6)].lval), Always, &(yyvsp[(2) - (6)].gen), (yyvsp[(4) - (6)].lval), &(yyvsp[(6) - (6)].gen)); + (yyvsp[(6) - (8)].gen).type = D_CONST2; + (yyvsp[(6) - (8)].gen).offset2 = (yyvsp[(8) - (8)].lval); + outcode((yyvsp[(1) - (8)].lval), Always, &(yyvsp[(2) - (8)].gen), (yyvsp[(4) - (8)].lval), &(yyvsp[(6) - (8)].gen)); } break; case 34: -/* Line 1455 of yacc.c */ -#line 237 "a.y" +/* Line 1806 of yacc.c */ +#line 241 "a.y" { - outcode((yyvsp[(1) - (4)].lval), (yyvsp[(2) - (4)].lval), &(yyvsp[(3) - (4)].gen), NREG, &nullgen); + outcode((yyvsp[(1) - (6)].lval), Always, &(yyvsp[(2) - (6)].gen), (yyvsp[(4) - (6)].lval), &(yyvsp[(6) - (6)].gen)); } break; case 35: -/* Line 1455 of yacc.c */ -#line 244 "a.y" +/* Line 1806 of yacc.c */ +#line 248 "a.y" { - outcode((yyvsp[(1) - (3)].lval), Always, &nullgen, NREG, &(yyvsp[(3) - (3)].gen)); + outcode((yyvsp[(1) - (4)].lval), (yyvsp[(2) - (4)].lval), &(yyvsp[(3) - (4)].gen), NREG, &nullgen); } break; case 36: -/* Line 1455 of yacc.c */ -#line 251 "a.y" +/* Line 1806 of yacc.c */ +#line 255 "a.y" { - outcode((yyvsp[(1) - (5)].lval), (yyvsp[(2) - (5)].lval), &(yyvsp[(3) - (5)].gen), NREG, &(yyvsp[(5) - (5)].gen)); + outcode((yyvsp[(1) - (3)].lval), Always, &nullgen, NREG, &(yyvsp[(3) - (3)].gen)); } break; case 37: -/* Line 1455 of yacc.c */ -#line 255 "a.y" +/* Line 1806 of yacc.c */ +#line 262 "a.y" { outcode((yyvsp[(1) - (5)].lval), (yyvsp[(2) - (5)].lval), &(yyvsp[(3) - (5)].gen), NREG, &(yyvsp[(5) - (5)].gen)); } @@ -2032,26 +2077,35 @@ yyreduce: case 38: -/* Line 1455 of yacc.c */ -#line 259 "a.y" +/* Line 1806 of yacc.c */ +#line 266 "a.y" { - outcode((yyvsp[(1) - (7)].lval), (yyvsp[(2) - (7)].lval), &(yyvsp[(3) - (7)].gen), (yyvsp[(5) - (7)].lval), &(yyvsp[(7) - (7)].gen)); + outcode((yyvsp[(1) - (5)].lval), (yyvsp[(2) - (5)].lval), &(yyvsp[(3) - (5)].gen), NREG, &(yyvsp[(5) - (5)].gen)); } break; case 39: -/* Line 1455 of yacc.c */ -#line 263 "a.y" +/* Line 1806 of yacc.c */ +#line 270 "a.y" { - outcode((yyvsp[(1) - (6)].lval), (yyvsp[(2) - (6)].lval), &(yyvsp[(3) - (6)].gen), (yyvsp[(5) - (6)].gen).reg, &nullgen); + outcode((yyvsp[(1) - (7)].lval), (yyvsp[(2) - (7)].lval), &(yyvsp[(3) - (7)].gen), (yyvsp[(5) - (7)].lval), &(yyvsp[(7) - (7)].gen)); } break; case 40: -/* Line 1455 of yacc.c */ -#line 270 "a.y" +/* Line 1806 of yacc.c */ +#line 274 "a.y" + { + outcode((yyvsp[(1) - (6)].lval), (yyvsp[(2) - (6)].lval), &(yyvsp[(3) - (6)].gen), (yyvsp[(5) - (6)].gen).reg, &nullgen); + } + break; + + case 41: + +/* Line 1806 of yacc.c */ +#line 281 "a.y" { Gen g; @@ -2072,19 +2126,19 @@ yyreduce: } break; - case 41: + case 42: -/* Line 1455 of yacc.c */ -#line 292 "a.y" +/* Line 1806 of yacc.c */ +#line 303 "a.y" { outcode((yyvsp[(1) - (7)].lval), (yyvsp[(2) - (7)].lval), &(yyvsp[(3) - (7)].gen), (yyvsp[(5) - (7)].gen).reg, &(yyvsp[(7) - (7)].gen)); } break; - case 42: + case 43: -/* Line 1455 of yacc.c */ -#line 300 "a.y" +/* Line 1806 of yacc.c */ +#line 311 "a.y" { (yyvsp[(7) - (9)].gen).type = D_REGREG2; (yyvsp[(7) - (9)].gen).offset = (yyvsp[(9) - (9)].lval); @@ -2092,55 +2146,79 @@ yyreduce: } break; - case 43: + case 44: -/* Line 1455 of yacc.c */ -#line 309 "a.y" +/* Line 1806 of yacc.c */ +#line 320 "a.y" { outcode((yyvsp[(1) - (2)].lval), Always, &(yyvsp[(2) - (2)].gen), NREG, &nullgen); } break; - case 44: + case 45: -/* Line 1455 of yacc.c */ -#line 316 "a.y" +/* Line 1806 of yacc.c */ +#line 327 "a.y" + { + if((yyvsp[(2) - (4)].gen).type != D_CONST || (yyvsp[(4) - (4)].gen).type != D_CONST) + yyerror("arguments to PCDATA must be integer constants"); + outcode((yyvsp[(1) - (4)].lval), Always, &(yyvsp[(2) - (4)].gen), NREG, &(yyvsp[(4) - (4)].gen)); + } + break; + + case 46: + +/* Line 1806 of yacc.c */ +#line 336 "a.y" + { + if((yyvsp[(2) - (4)].gen).type != D_CONST) + yyerror("index for FUNCDATA must be integer constant"); + if((yyvsp[(4) - (4)].gen).type != D_EXTERN && (yyvsp[(4) - (4)].gen).type != D_STATIC) + yyerror("value for FUNCDATA must be symbol reference"); + outcode((yyvsp[(1) - (4)].lval), Always, &(yyvsp[(2) - (4)].gen), NREG, &(yyvsp[(4) - (4)].gen)); + } + break; + + case 47: + +/* Line 1806 of yacc.c */ +#line 347 "a.y" { outcode((yyvsp[(1) - (2)].lval), Always, &nullgen, NREG, &nullgen); } break; - case 45: + case 48: -/* Line 1455 of yacc.c */ -#line 321 "a.y" +/* Line 1806 of yacc.c */ +#line 352 "a.y" { (yyval.lval) = Always; } break; - case 46: + case 49: -/* Line 1455 of yacc.c */ -#line 325 "a.y" +/* Line 1806 of yacc.c */ +#line 356 "a.y" { (yyval.lval) = ((yyvsp[(1) - (2)].lval) & ~C_SCOND) | (yyvsp[(2) - (2)].lval); } break; - case 47: + case 50: -/* Line 1455 of yacc.c */ -#line 329 "a.y" +/* Line 1806 of yacc.c */ +#line 360 "a.y" { (yyval.lval) = (yyvsp[(1) - (2)].lval) | (yyvsp[(2) - (2)].lval); } break; - case 50: + case 53: -/* Line 1455 of yacc.c */ -#line 338 "a.y" +/* Line 1806 of yacc.c */ +#line 369 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_BRANCH; @@ -2148,10 +2226,10 @@ yyreduce: } break; - case 51: + case 54: -/* Line 1455 of yacc.c */ -#line 344 "a.y" +/* Line 1806 of yacc.c */ +#line 375 "a.y" { (yyval.gen) = nullgen; if(pass == 2) @@ -2162,10 +2240,10 @@ yyreduce: } break; - case 52: + case 55: -/* Line 1455 of yacc.c */ -#line 353 "a.y" +/* Line 1806 of yacc.c */ +#line 384 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_BRANCH; @@ -2174,10 +2252,10 @@ yyreduce: } break; - case 53: + case 56: -/* Line 1455 of yacc.c */ -#line 361 "a.y" +/* Line 1806 of yacc.c */ +#line 392 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_CONST; @@ -2185,30 +2263,30 @@ yyreduce: } break; - case 54: + case 57: -/* Line 1455 of yacc.c */ -#line 367 "a.y" +/* Line 1806 of yacc.c */ +#line 398 "a.y" { (yyval.gen) = (yyvsp[(2) - (2)].gen); (yyval.gen).type = D_CONST; } break; - case 55: + case 58: -/* Line 1455 of yacc.c */ -#line 372 "a.y" +/* Line 1806 of yacc.c */ +#line 403 "a.y" { (yyval.gen) = (yyvsp[(4) - (4)].gen); (yyval.gen).type = D_OCONST; } break; - case 56: + case 59: -/* Line 1455 of yacc.c */ -#line 377 "a.y" +/* Line 1806 of yacc.c */ +#line 408 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SCONST; @@ -2216,10 +2294,10 @@ yyreduce: } break; - case 58: + case 61: -/* Line 1455 of yacc.c */ -#line 386 "a.y" +/* Line 1806 of yacc.c */ +#line 417 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2227,10 +2305,10 @@ yyreduce: } break; - case 59: + case 62: -/* Line 1455 of yacc.c */ -#line 392 "a.y" +/* Line 1806 of yacc.c */ +#line 423 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2238,19 +2316,19 @@ yyreduce: } break; - case 60: + case 63: -/* Line 1455 of yacc.c */ -#line 400 "a.y" +/* Line 1806 of yacc.c */ +#line 431 "a.y" { (yyval.lval) = 1 << (yyvsp[(1) - (1)].lval); } break; - case 61: + case 64: -/* Line 1455 of yacc.c */ -#line 404 "a.y" +/* Line 1806 of yacc.c */ +#line 435 "a.y" { int i; (yyval.lval)=0; @@ -2261,29 +2339,29 @@ yyreduce: } break; - case 62: + case 65: -/* Line 1455 of yacc.c */ -#line 413 "a.y" +/* Line 1806 of yacc.c */ +#line 444 "a.y" { (yyval.lval) = (1<<(yyvsp[(1) - (3)].lval)) | (yyvsp[(3) - (3)].lval); } break; - case 66: + case 69: -/* Line 1455 of yacc.c */ -#line 422 "a.y" +/* Line 1806 of yacc.c */ +#line 453 "a.y" { (yyval.gen) = (yyvsp[(1) - (4)].gen); (yyval.gen).reg = (yyvsp[(3) - (4)].lval); } break; - case 67: + case 70: -/* Line 1455 of yacc.c */ -#line 427 "a.y" +/* Line 1806 of yacc.c */ +#line 458 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_PSR; @@ -2291,10 +2369,10 @@ yyreduce: } break; - case 68: + case 71: -/* Line 1455 of yacc.c */ -#line 433 "a.y" +/* Line 1806 of yacc.c */ +#line 464 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FPCR; @@ -2302,10 +2380,10 @@ yyreduce: } break; - case 69: + case 72: -/* Line 1455 of yacc.c */ -#line 439 "a.y" +/* Line 1806 of yacc.c */ +#line 470 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_OREG; @@ -2313,10 +2391,10 @@ yyreduce: } break; - case 73: + case 76: -/* Line 1455 of yacc.c */ -#line 450 "a.y" +/* Line 1806 of yacc.c */ +#line 481 "a.y" { (yyval.gen) = (yyvsp[(1) - (1)].gen); if((yyvsp[(1) - (1)].gen).name != D_EXTERN && (yyvsp[(1) - (1)].gen).name != D_STATIC) { @@ -2324,10 +2402,10 @@ yyreduce: } break; - case 74: + case 77: -/* Line 1455 of yacc.c */ -#line 458 "a.y" +/* Line 1806 of yacc.c */ +#line 489 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_OREG; @@ -2336,10 +2414,10 @@ yyreduce: } break; - case 76: + case 79: -/* Line 1455 of yacc.c */ -#line 468 "a.y" +/* Line 1806 of yacc.c */ +#line 499 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_OREG; @@ -2348,10 +2426,10 @@ yyreduce: } break; - case 78: + case 81: -/* Line 1455 of yacc.c */ -#line 478 "a.y" +/* Line 1806 of yacc.c */ +#line 509 "a.y" { (yyval.gen) = (yyvsp[(1) - (4)].gen); (yyval.gen).type = D_OREG; @@ -2359,10 +2437,10 @@ yyreduce: } break; - case 83: + case 86: -/* Line 1455 of yacc.c */ -#line 491 "a.y" +/* Line 1806 of yacc.c */ +#line 522 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_CONST; @@ -2370,10 +2448,10 @@ yyreduce: } break; - case 84: + case 87: -/* Line 1455 of yacc.c */ -#line 499 "a.y" +/* Line 1806 of yacc.c */ +#line 530 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_REG; @@ -2381,10 +2459,10 @@ yyreduce: } break; - case 85: + case 88: -/* Line 1455 of yacc.c */ -#line 507 "a.y" +/* Line 1806 of yacc.c */ +#line 538 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_REGREG; @@ -2393,10 +2471,10 @@ yyreduce: } break; - case 86: + case 89: -/* Line 1455 of yacc.c */ -#line 516 "a.y" +/* Line 1806 of yacc.c */ +#line 547 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SHIFT; @@ -2404,10 +2482,10 @@ yyreduce: } break; - case 87: + case 90: -/* Line 1455 of yacc.c */ -#line 522 "a.y" +/* Line 1806 of yacc.c */ +#line 553 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SHIFT; @@ -2415,10 +2493,10 @@ yyreduce: } break; - case 88: + case 91: -/* Line 1455 of yacc.c */ -#line 528 "a.y" +/* Line 1806 of yacc.c */ +#line 559 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SHIFT; @@ -2426,10 +2504,10 @@ yyreduce: } break; - case 89: + case 92: -/* Line 1455 of yacc.c */ -#line 534 "a.y" +/* Line 1806 of yacc.c */ +#line 565 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SHIFT; @@ -2437,10 +2515,10 @@ yyreduce: } break; - case 90: + case 93: -/* Line 1455 of yacc.c */ -#line 542 "a.y" +/* Line 1806 of yacc.c */ +#line 573 "a.y" { if((yyval.lval) < 0 || (yyval.lval) >= 16) print("register value out of range\n"); @@ -2448,10 +2526,10 @@ yyreduce: } break; - case 91: + case 94: -/* Line 1455 of yacc.c */ -#line 548 "a.y" +/* Line 1806 of yacc.c */ +#line 579 "a.y" { if((yyval.lval) < 0 || (yyval.lval) >= 32) print("shift value out of range\n"); @@ -2459,19 +2537,19 @@ yyreduce: } break; - case 93: + case 96: -/* Line 1455 of yacc.c */ -#line 557 "a.y" +/* Line 1806 of yacc.c */ +#line 588 "a.y" { (yyval.lval) = REGPC; } break; - case 94: + case 97: -/* Line 1455 of yacc.c */ -#line 561 "a.y" +/* Line 1806 of yacc.c */ +#line 592 "a.y" { if((yyvsp[(3) - (4)].lval) < 0 || (yyvsp[(3) - (4)].lval) >= NREG) print("register value out of range\n"); @@ -2479,19 +2557,19 @@ yyreduce: } break; - case 96: + case 99: -/* Line 1455 of yacc.c */ -#line 570 "a.y" +/* Line 1806 of yacc.c */ +#line 601 "a.y" { (yyval.lval) = REGSP; } break; - case 98: + case 101: -/* Line 1455 of yacc.c */ -#line 577 "a.y" +/* Line 1806 of yacc.c */ +#line 608 "a.y" { if((yyvsp[(3) - (4)].lval) < 0 || (yyvsp[(3) - (4)].lval) >= NREG) print("register value out of range\n"); @@ -2499,10 +2577,10 @@ yyreduce: } break; - case 101: + case 104: -/* Line 1455 of yacc.c */ -#line 589 "a.y" +/* Line 1806 of yacc.c */ +#line 620 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FREG; @@ -2510,10 +2588,10 @@ yyreduce: } break; - case 102: + case 105: -/* Line 1455 of yacc.c */ -#line 595 "a.y" +/* Line 1806 of yacc.c */ +#line 626 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FREG; @@ -2521,10 +2599,10 @@ yyreduce: } break; - case 103: + case 106: -/* Line 1455 of yacc.c */ -#line 603 "a.y" +/* Line 1806 of yacc.c */ +#line 634 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_OREG; @@ -2534,10 +2612,10 @@ yyreduce: } break; - case 104: + case 107: -/* Line 1455 of yacc.c */ -#line 611 "a.y" +/* Line 1806 of yacc.c */ +#line 642 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_OREG; @@ -2547,10 +2625,10 @@ yyreduce: } break; - case 105: + case 108: -/* Line 1455 of yacc.c */ -#line 619 "a.y" +/* Line 1806 of yacc.c */ +#line 650 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_OREG; @@ -2560,181 +2638,181 @@ yyreduce: } break; - case 106: + case 109: -/* Line 1455 of yacc.c */ -#line 628 "a.y" +/* Line 1806 of yacc.c */ +#line 659 "a.y" { (yyval.lval) = 0; } break; - case 107: + case 110: -/* Line 1455 of yacc.c */ -#line 632 "a.y" +/* Line 1806 of yacc.c */ +#line 663 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; - case 108: + case 111: -/* Line 1455 of yacc.c */ -#line 636 "a.y" +/* Line 1806 of yacc.c */ +#line 667 "a.y" { (yyval.lval) = -(yyvsp[(2) - (2)].lval); } break; - case 113: + case 116: -/* Line 1455 of yacc.c */ -#line 648 "a.y" +/* Line 1806 of yacc.c */ +#line 679 "a.y" { (yyval.lval) = (yyvsp[(1) - (1)].sym)->value; } break; - case 114: + case 117: -/* Line 1455 of yacc.c */ -#line 652 "a.y" +/* Line 1806 of yacc.c */ +#line 683 "a.y" { (yyval.lval) = -(yyvsp[(2) - (2)].lval); } break; - case 115: + case 118: -/* Line 1455 of yacc.c */ -#line 656 "a.y" +/* Line 1806 of yacc.c */ +#line 687 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; - case 116: + case 119: -/* Line 1455 of yacc.c */ -#line 660 "a.y" +/* Line 1806 of yacc.c */ +#line 691 "a.y" { (yyval.lval) = ~(yyvsp[(2) - (2)].lval); } break; - case 117: + case 120: -/* Line 1455 of yacc.c */ -#line 664 "a.y" +/* Line 1806 of yacc.c */ +#line 695 "a.y" { (yyval.lval) = (yyvsp[(2) - (3)].lval); } break; - case 118: + case 121: -/* Line 1455 of yacc.c */ -#line 669 "a.y" +/* Line 1806 of yacc.c */ +#line 700 "a.y" { (yyval.lval) = 0; } break; - case 119: + case 122: -/* Line 1455 of yacc.c */ -#line 673 "a.y" +/* Line 1806 of yacc.c */ +#line 704 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; - case 121: + case 124: -/* Line 1455 of yacc.c */ -#line 680 "a.y" +/* Line 1806 of yacc.c */ +#line 711 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) + (yyvsp[(3) - (3)].lval); } break; - case 122: + case 125: -/* Line 1455 of yacc.c */ -#line 684 "a.y" +/* Line 1806 of yacc.c */ +#line 715 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) - (yyvsp[(3) - (3)].lval); } break; - case 123: + case 126: -/* Line 1455 of yacc.c */ -#line 688 "a.y" +/* Line 1806 of yacc.c */ +#line 719 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) * (yyvsp[(3) - (3)].lval); } break; - case 124: + case 127: -/* Line 1455 of yacc.c */ -#line 692 "a.y" +/* Line 1806 of yacc.c */ +#line 723 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) / (yyvsp[(3) - (3)].lval); } break; - case 125: + case 128: -/* Line 1455 of yacc.c */ -#line 696 "a.y" +/* Line 1806 of yacc.c */ +#line 727 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) % (yyvsp[(3) - (3)].lval); } break; - case 126: + case 129: -/* Line 1455 of yacc.c */ -#line 700 "a.y" +/* Line 1806 of yacc.c */ +#line 731 "a.y" { (yyval.lval) = (yyvsp[(1) - (4)].lval) << (yyvsp[(4) - (4)].lval); } break; - case 127: + case 130: -/* Line 1455 of yacc.c */ -#line 704 "a.y" +/* Line 1806 of yacc.c */ +#line 735 "a.y" { (yyval.lval) = (yyvsp[(1) - (4)].lval) >> (yyvsp[(4) - (4)].lval); } break; - case 128: + case 131: -/* Line 1455 of yacc.c */ -#line 708 "a.y" +/* Line 1806 of yacc.c */ +#line 739 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) & (yyvsp[(3) - (3)].lval); } break; - case 129: + case 132: -/* Line 1455 of yacc.c */ -#line 712 "a.y" +/* Line 1806 of yacc.c */ +#line 743 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) ^ (yyvsp[(3) - (3)].lval); } break; - case 130: + case 133: -/* Line 1455 of yacc.c */ -#line 716 "a.y" +/* Line 1806 of yacc.c */ +#line 747 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) | (yyvsp[(3) - (3)].lval); } @@ -2742,10 +2820,21 @@ yyreduce: -/* Line 1455 of yacc.c */ -#line 2747 "y.tab.c" +/* Line 1806 of yacc.c */ +#line 2825 "y.tab.c" default: break; } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); @@ -2773,6 +2862,10 @@ yyreduce: | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { @@ -2780,37 +2873,36 @@ yyerrlab: #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) { - YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); - if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) - { - YYSIZE_T yyalloc = 2 * yysize; - if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) - yyalloc = YYSTACK_ALLOC_MAXIMUM; - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); - yymsg = (char *) YYSTACK_ALLOC (yyalloc); - if (yymsg) - yymsg_alloc = yyalloc; - else - { - yymsg = yymsgbuf; - yymsg_alloc = sizeof yymsgbuf; - } - } - - if (0 < yysize && yysize <= yymsg_alloc) - { - (void) yysyntax_error (yymsg, yystate, yychar); - yyerror (yymsg); - } - else - { - yyerror (YY_("syntax error")); - if (yysize != 0) - goto yyexhaustedlab; - } + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; } +# undef YYSYNTAX_ERROR #endif } @@ -2869,7 +2961,7 @@ yyerrlab1: for (;;) { yyn = yypact[yystate]; - if (yyn != YYPACT_NINF) + if (!yypact_value_is_default (yyn)) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) @@ -2928,8 +3020,13 @@ yyexhaustedlab: yyreturn: if (yychar != YYEMPTY) - yydestruct ("Cleanup: discarding lookahead", - yytoken, &yylval); + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + } /* Do not reclaim the symbols of the rule which action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); diff --git a/src/cmd/5a/y.tab.h b/src/cmd/5a/y.tab.h index 1dd0cb08a..92230a2a5 100644 --- a/src/cmd/5a/y.tab.h +++ b/src/cmd/5a/y.tab.h @@ -1,10 +1,8 @@ +/* A Bison parser, made by GNU Bison 2.5. */ -/* A Bison parser, made by GNU Bison 2.4.1. */ - -/* Skeleton interface for Bison's Yacc-like parsers in C +/* Bison interface for Yacc-like parsers in C - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 - Free Software Foundation, Inc. + Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -53,39 +51,40 @@ LTYPEC = 269, LTYPED = 270, LTYPEE = 271, - LTYPEF = 272, - LTYPEG = 273, - LTYPEH = 274, - LTYPEI = 275, - LTYPEJ = 276, - LTYPEK = 277, - LTYPEL = 278, - LTYPEM = 279, - LTYPEN = 280, - LTYPEBX = 281, - LTYPEPLD = 282, - LCONST = 283, - LSP = 284, - LSB = 285, - LFP = 286, - LPC = 287, - LTYPEX = 288, - LR = 289, - LREG = 290, - LF = 291, - LFREG = 292, - LC = 293, - LCREG = 294, - LPSR = 295, - LFCR = 296, - LCOND = 297, - LS = 298, - LAT = 299, - LFCONST = 300, - LSCONST = 301, - LNAME = 302, - LLAB = 303, - LVAR = 304 + LTYPEG = 272, + LTYPEH = 273, + LTYPEI = 274, + LTYPEJ = 275, + LTYPEK = 276, + LTYPEL = 277, + LTYPEM = 278, + LTYPEN = 279, + LTYPEBX = 280, + LTYPEPLD = 281, + LCONST = 282, + LSP = 283, + LSB = 284, + LFP = 285, + LPC = 286, + LTYPEX = 287, + LTYPEPC = 288, + LTYPEF = 289, + LR = 290, + LREG = 291, + LF = 292, + LFREG = 293, + LC = 294, + LCREG = 295, + LPSR = 296, + LFCR = 297, + LCOND = 298, + LS = 299, + LAT = 300, + LFCONST = 301, + LSCONST = 302, + LNAME = 303, + LLAB = 304, + LVAR = 305 }; #endif /* Tokens. */ @@ -103,39 +102,40 @@ #define LTYPEC 269 #define LTYPED 270 #define LTYPEE 271 -#define LTYPEF 272 -#define LTYPEG 273 -#define LTYPEH 274 -#define LTYPEI 275 -#define LTYPEJ 276 -#define LTYPEK 277 -#define LTYPEL 278 -#define LTYPEM 279 -#define LTYPEN 280 -#define LTYPEBX 281 -#define LTYPEPLD 282 -#define LCONST 283 -#define LSP 284 -#define LSB 285 -#define LFP 286 -#define LPC 287 -#define LTYPEX 288 -#define LR 289 -#define LREG 290 -#define LF 291 -#define LFREG 292 -#define LC 293 -#define LCREG 294 -#define LPSR 295 -#define LFCR 296 -#define LCOND 297 -#define LS 298 -#define LAT 299 -#define LFCONST 300 -#define LSCONST 301 -#define LNAME 302 -#define LLAB 303 -#define LVAR 304 +#define LTYPEG 272 +#define LTYPEH 273 +#define LTYPEI 274 +#define LTYPEJ 275 +#define LTYPEK 276 +#define LTYPEL 277 +#define LTYPEM 278 +#define LTYPEN 279 +#define LTYPEBX 280 +#define LTYPEPLD 281 +#define LCONST 282 +#define LSP 283 +#define LSB 284 +#define LFP 285 +#define LPC 286 +#define LTYPEX 287 +#define LTYPEPC 288 +#define LTYPEF 289 +#define LR 290 +#define LREG 291 +#define LF 292 +#define LFREG 293 +#define LC 294 +#define LCREG 295 +#define LPSR 296 +#define LFCR 297 +#define LCOND 298 +#define LS 299 +#define LAT 300 +#define LFCONST 301 +#define LSCONST 302 +#define LNAME 303 +#define LLAB 304 +#define LVAR 305 @@ -144,8 +144,8 @@ typedef union YYSTYPE { -/* Line 1676 of yacc.c */ -#line 38 "a.y" +/* Line 2068 of yacc.c */ +#line 39 "a.y" Sym *sym; int32 lval; @@ -155,7 +155,7 @@ typedef union YYSTYPE -/* Line 1676 of yacc.c */ +/* Line 2068 of yacc.c */ #line 160 "y.tab.h" } YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 diff --git a/src/cmd/5c/cgen.c b/src/cmd/5c/cgen.c index 5ff4f633d..08ed36055 100644 --- a/src/cmd/5c/cgen.c +++ b/src/cmd/5c/cgen.c @@ -28,8 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. - #include "gc.h" +#include "../../pkg/runtime/funcdata.h" void _cgen(Node *n, Node *nn, int inrel) @@ -366,12 +366,14 @@ _cgen(Node *n, Node *nn, int inrel) if(REGARG >= 0) o = reg[REGARG]; gargs(r, &nod, &nod1); + gpcdata(PCDATA_ArgSize, curarg); if(l->addable < INDEXED) { reglcgen(&nod, l, Z); gopcode(OFUNC, Z, Z, &nod); regfree(&nod); } else gopcode(OFUNC, Z, Z, l); + gpcdata(PCDATA_ArgSize, -1); if(REGARG >= 0) if(o != reg[REGARG]) reg[REGARG]--; diff --git a/src/cmd/5c/gc.h b/src/cmd/5c/gc.h index a0fc63c60..084da7e6a 100644 --- a/src/cmd/5c/gc.h +++ b/src/cmd/5c/gc.h @@ -298,6 +298,7 @@ int sconst(Node*); int sval(int32); void gpseudo(int, Sym*, Node*); void gprefetch(Node*); +void gpcdata(int, int); /* * swt.c diff --git a/src/cmd/5c/peep.c b/src/cmd/5c/peep.c index 2f902e02a..22328c18c 100644 --- a/src/cmd/5c/peep.c +++ b/src/cmd/5c/peep.c @@ -127,8 +127,10 @@ loop1: } continue; case AMOVH: + case AMOVHS: case AMOVHU: case AMOVB: + case AMOVBS: case AMOVBU: if(p->to.type != D_REG) continue; @@ -152,6 +154,7 @@ loop1: switch(p->as) { case AMOVW: case AMOVB: + case AMOVBS: case AMOVBU: if(p->from.type == D_OREG && p->from.offset == 0) xtramodes(r, &p->from); @@ -462,7 +465,7 @@ copy1(Adr *v1, Adr *v2, Reg *r, int f) } t = copyu(p, v2, A); switch(t) { - case 2: /* rar, cant split */ + case 2: /* rar, can't split */ if(debug['P']) print("; %Drar; return 0\n", v2); return 0; @@ -824,7 +827,7 @@ xtramodes(Reg *r, Adr *a) Adr v; p = r->prog; - if(p->as == AMOVB && p->from.type == D_OREG) /* byte load */ + if((p->as == AMOVB || p->as == AMOVBS) && p->from.type == D_OREG) /* byte load */ return 0; v = *a; v.type = D_REG; @@ -836,7 +839,7 @@ xtramodes(Reg *r, Adr *a) case AADD: if(p1->from.type == D_REG || (p1->from.type == D_SHIFT && (p1->from.offset&(1<<4)) == 0 && - (p->as != AMOVB || (a == &p->from && (p1->from.offset&~0xf) == 0))) || + ((p->as != AMOVB && p->as != AMOVBS) || (a == &p->from && (p1->from.offset&~0xf) == 0))) || (p1->from.type == D_CONST && p1->from.offset > -4096 && p1->from.offset < 4096)) if(nochange(uniqs(r1), r, p1)) { @@ -961,8 +964,10 @@ copyu(Prog *p, Adr *v, Adr *s) case AMOVF: case AMOVD: case AMOVH: + case AMOVHS: case AMOVHU: case AMOVB: + case AMOVBS: case AMOVBU: case AMOVDW: case AMOVWD: diff --git a/src/cmd/5c/reg.c b/src/cmd/5c/reg.c index 42c5193de..3d67872b4 100644 --- a/src/cmd/5c/reg.c +++ b/src/cmd/5c/reg.c @@ -112,6 +112,7 @@ regopt(Prog *p) case AGLOBL: case ANAME: case ASIGNAME: + case AFUNCDATA: continue; } r = rega(); @@ -174,8 +175,10 @@ regopt(Prog *p) */ case ANOP: case AMOVB: + case AMOVBS: case AMOVBU: case AMOVH: + case AMOVHS: case AMOVHU: case AMOVW: case AMOVF: @@ -460,6 +463,7 @@ brk: case AGLOBL: case ANAME: case ASIGNAME: + case AFUNCDATA: break; } } @@ -555,9 +559,9 @@ addmove(Reg *r, int bn, int rn, int f) p1->as = AMOVW; if(v->etype == TCHAR || v->etype == TUCHAR) - p1->as = AMOVB; + p1->as = AMOVBS; if(v->etype == TSHORT || v->etype == TUSHORT) - p1->as = AMOVH; + p1->as = AMOVHS; if(v->etype == TFLOAT) p1->as = AMOVF; if(v->etype == TDOUBLE) diff --git a/src/cmd/5c/sgen.c b/src/cmd/5c/sgen.c index 92a0f64f8..efcc0437b 100644 --- a/src/cmd/5c/sgen.c +++ b/src/cmd/5c/sgen.c @@ -35,11 +35,9 @@ Prog* gtext(Sym *s, int32 stkoff) { int32 a; - - a = 0; - if(!(textflag & NOSPLIT)) - a = argsize(); - else if(stkoff >= 128) + + a = argsize(); + if((textflag & NOSPLIT) != 0 && stkoff >= 128) yyerror("stack frame too large for NOSPLIT function"); gpseudo(ATEXT, s, nodconst(stkoff)); diff --git a/src/cmd/5c/swt.c b/src/cmd/5c/swt.c index 87b77518b..0f0c457f8 100644 --- a/src/cmd/5c/swt.c +++ b/src/cmd/5c/swt.c @@ -525,12 +525,12 @@ outhist(Biobuf *b) q = 0; } if(n) { - Bputc(b, ANAME); - Bputc(b, D_FILE); - Bputc(b, 1); - Bputc(b, '<'); + BPUTC(b, ANAME); + BPUTC(b, D_FILE); + BPUTC(b, 1); + BPUTC(b, '<'); Bwrite(b, p, n); - Bputc(b, 0); + BPUTC(b, 0); } p = q; if(p == 0 && op) { diff --git a/src/cmd/5c/txt.c b/src/cmd/5c/txt.c index b8675fe60..6d9b69d00 100644 --- a/src/cmd/5c/txt.c +++ b/src/cmd/5c/txt.c @@ -139,9 +139,7 @@ gclean(void) continue; if(s->type == types[TENUM]) continue; - textflag = s->dataflag; gpseudo(AGLOBL, s, nodconst(s->type->width)); - textflag = 0; } nextpc(); p->as = AEND; @@ -596,13 +594,13 @@ gmove(Node *f, Node *t) a = AMOVD; break; case TCHAR: - a = AMOVB; + a = AMOVBS; break; case TUCHAR: a = AMOVBU; break; case TSHORT: - a = AMOVH; + a = AMOVHS; break; case TUSHORT: a = AMOVHU; @@ -632,13 +630,13 @@ gmove(Node *f, Node *t) a = AMOVBU; break; case TCHAR: - a = AMOVB; + a = AMOVBS; break; case TUSHORT: a = AMOVHU; break; case TSHORT: - a = AMOVH; + a = AMOVHS; break; case TFLOAT: a = AMOVF; @@ -763,13 +761,13 @@ gmove(Node *f, Node *t) switch(tt) { case TDOUBLE: regalloc(&nod, f, Z); - gins(AMOVH, f, &nod); + gins(AMOVHS, f, &nod); gins(AMOVWD, &nod, t); regfree(&nod); return; case TFLOAT: regalloc(&nod, f, Z); - gins(AMOVH, f, &nod); + gins(AMOVHS, f, &nod); gins(AMOVWF, &nod, t); regfree(&nod); return; @@ -778,7 +776,7 @@ gmove(Node *f, Node *t) case TULONG: case TLONG: case TIND: - a = AMOVH; + a = AMOVHS; break; case TSHORT: case TUSHORT: @@ -821,13 +819,13 @@ gmove(Node *f, Node *t) switch(tt) { case TDOUBLE: regalloc(&nod, f, Z); - gins(AMOVB, f, &nod); + gins(AMOVBS, f, &nod); gins(AMOVWD, &nod, t); regfree(&nod); return; case TFLOAT: regalloc(&nod, f, Z); - gins(AMOVB, f, &nod); + gins(AMOVBS, f, &nod); gins(AMOVWF, &nod, t); regfree(&nod); return; @@ -838,7 +836,7 @@ gmove(Node *f, Node *t) case TIND: case TSHORT: case TUSHORT: - a = AMOVB; + a = AMOVBS; break; case TCHAR: case TUCHAR: @@ -895,13 +893,13 @@ gmover(Node *f, Node *t) if(typechlp[ft] && typechlp[tt] && ewidth[ft] >= ewidth[tt]){ switch(tt){ case TSHORT: - a = AMOVH; + a = AMOVHS; break; case TUSHORT: a = AMOVHU; break; case TCHAR: - a = AMOVB; + a = AMOVBS; break; case TUCHAR: a = AMOVBU; @@ -1181,10 +1179,17 @@ gpseudo(int a, Sym *s, Node *n) p->from.type = D_OREG; p->from.sym = s; p->from.name = D_EXTERN; - if(a == ATEXT || a == AGLOBL) { + + switch(a) { + case ATEXT: p->reg = textflag; textflag = 0; + break; + case AGLOBL: + p->reg = s->dataflag; + break; } + if(s->class == CSTATIC) p->from.name = D_STATIC; naddr(n, &p->to); @@ -1193,6 +1198,15 @@ gpseudo(int a, Sym *s, Node *n) } void +gpcdata(int index, int value) +{ + Node n1; + + n1 = *nodconst(index); + gins(APCDATA, &n1, nodconst(value)); +} + +void gprefetch(Node *n) { Node n1; diff --git a/src/cmd/5g/cgen.c b/src/cmd/5g/cgen.c index 1620f410a..2d260e72d 100644 --- a/src/cmd/5g/cgen.c +++ b/src/cmd/5g/cgen.c @@ -34,6 +34,8 @@ cgen(Node *n, Node *res) case OSLICE: case OSLICEARR: case OSLICESTR: + case OSLICE3: + case OSLICE3ARR: if (res->op != ONAME || !res->addable) { tempname(&n1, n->type); cgen_slice(n, &n1); @@ -77,6 +79,7 @@ cgen(Node *n, Node *res) // can't do in walk because n->left->addable // changes if n->left is an escaping local variable. switch(n->op) { + case OSPTR: case OLEN: if(isslice(n->left->type) || istype(n->left->type, TSTRING)) n->addable = n->left->addable; @@ -315,6 +318,22 @@ cgen(Node *n, Node *res) regfree(&n1); break; + case OSPTR: + // pointer is the first word of string or slice. + if(isconst(nl, CTSTR)) { + regalloc(&n1, types[tptr], res); + p1 = gins(AMOVW, N, &n1); + datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from); + gmove(&n1, res); + regfree(&n1); + break; + } + igen(nl, &n1, res); + n1.type = n->type; + gmove(&n1, res); + regfree(&n1); + break; + case OLEN: if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) { // map has len in the first 32-bit word. @@ -464,6 +483,16 @@ abop: // asymmetric binary cgen(nl, &n1); } gins(a, &n2, &n1); + // Normalize result for types smaller than word. + if(n->type->width < widthptr) { + switch(n->op) { + case OADD: + case OSUB: + case OMUL: + gins(optoas(OAS, n->type), &n1, &n1); + break; + } + } gmove(&n1, res); regfree(&n1); if(n2.op != OLITERAL) @@ -550,6 +579,7 @@ cgenindex(Node *n, Node *res, int bounded) /* * generate: * res = &n; + * The generated code checks that the result is not nil. */ void agen(Node *n, Node *res) @@ -629,6 +659,8 @@ agen(Node *n, Node *res) case OSLICE: case OSLICEARR: case OSLICESTR: + case OSLICE3: + case OSLICE3ARR: tempname(&n1, n->type); cgen_slice(n, &n1); agen(&n1, res); @@ -675,25 +707,11 @@ agen(Node *n, Node *res) case OIND: cgen(nl, res); + cgen_checknil(res); break; case ODOT: agen(nl, res); - // explicit check for nil if struct is large enough - // that we might derive too big a pointer. If the left node - // was ODOT we have already done the nil check. - if(nl->op != ODOT) - if(nl->type->width >= unmappedzero) { - regalloc(&n1, types[tptr], N); - gmove(res, &n1); - regalloc(&n2, types[TUINT8], &n1); - n1.op = OINDREG; - n1.type = types[TUINT8]; - n1.xoffset = 0; - gmove(&n1, &n2); - regfree(&n1); - regfree(&n2); - } if(n->xoffset != 0) { nodconst(&n1, types[TINT32], n->xoffset); regalloc(&n2, n1.type, N); @@ -709,19 +727,7 @@ agen(Node *n, Node *res) case ODOTPTR: cgen(nl, res); - // explicit check for nil if struct is large enough - // that we might derive too big a pointer. - if(nl->type->type->width >= unmappedzero) { - regalloc(&n1, types[tptr], N); - gmove(res, &n1); - regalloc(&n2, types[TUINT8], &n1); - n1.op = OINDREG; - n1.type = types[TUINT8]; - n1.xoffset = 0; - gmove(&n1, &n2); - regfree(&n1); - regfree(&n2); - } + cgen_checknil(res); if(n->xoffset != 0) { nodconst(&n1, types[TINT32], n->xoffset); regalloc(&n2, n1.type, N); @@ -747,11 +753,12 @@ ret: * * on exit, a has been changed to be *newreg. * caller must regfree(a). + * The generated code checks that the result is not *nil. */ void igen(Node *n, Node *a, Node *res) { - Node n1, n2; + Node n1; int r; if(debug['g']) { @@ -792,19 +799,7 @@ igen(Node *n, Node *a, Node *res) regalloc(a, types[tptr], res); cgen(n->left, a); } - // explicit check for nil if struct is large enough - // that we might derive too big a pointer. - if(n->left->type->type->width >= unmappedzero) { - regalloc(&n1, types[tptr], N); - gmove(a, &n1); - regalloc(&n2, types[TUINT8], &n1); - n1.op = OINDREG; - n1.type = types[TUINT8]; - n1.xoffset = 0; - gmove(&n1, &n2); - regfree(&n1); - regfree(&n2); - } + cgen_checknil(a); a->op = OINDREG; a->xoffset = n->xoffset; a->type = n->type; @@ -894,6 +889,7 @@ cgenr(Node *n, Node *a, Node *res) * newreg = &n; * * caller must regfree(a). + * The generated code checks that the result is not nil. */ void agenr(Node *n, Node *a, Node *res) @@ -925,6 +921,7 @@ agenr(Node *n, Node *a, Node *res) case OIND: cgenr(n->left, a, res); + cgen_checknil(a); break; case OINDEX: @@ -966,20 +963,6 @@ agenr(Node *n, Node *a, Node *res) // i is in &n1 (if not constant) // w is width - // explicit check for nil if array is large enough - // that we might derive too big a pointer. - if(isfixedarray(nl->type) && nl->type->width >= unmappedzero) { - regalloc(&n4, types[tptr], N); - gmove(&n3, &n4); - regalloc(&tmp, types[TUINT8], &n4); - n4.op = OINDREG; - n4.type = types[TUINT8]; - n4.xoffset = 0; - gmove(&n4, &tmp); - regfree(&n4); - regfree(&tmp); - } - // constant index if(isconst(nr, CTINT)) { if(isconst(nl, CTSTR)) diff --git a/src/cmd/5g/gg.h b/src/cmd/5g/gg.h index 45a9a887e..860817f69 100644 --- a/src/cmd/5g/gg.h +++ b/src/cmd/5g/gg.h @@ -39,7 +39,7 @@ struct Prog uint32 loc; // pc offset in this func uint32 lineno; // source line that generated this Prog* link; // next instruction in this func - void* regp; // points to enclosing Reg struct + void* opt; // for optimizer passes short as; // opcode uchar reg; // doubles as width in DATA op uchar scond; @@ -51,7 +51,7 @@ struct Prog #define REGALLOC_R0 0 #define REGALLOC_RMAX REGEXT -#define REGALLOC_F0 (REGALLOC_RMAX+1) +#define REGALLOC_F0 NREG #define REGALLOC_FMAX (REGALLOC_F0 + FREGEXT) EXTERN int32 dynloc; @@ -73,7 +73,6 @@ EXTERN int maxstksize; * gen.c */ void compile(Node*); -void proglist(void); void gen(Node*); Node* lookdot(Node*, Node*, int); void cgen_as(Node*, Node*); @@ -120,7 +119,6 @@ void cgen64(Node*, Node*); * gsubr.c */ void clearp(Prog*); -void proglist(void); Prog* gbranch(int, Type*, int); Prog* prog(int); void gconv(int, int); @@ -148,6 +146,7 @@ void split64(Node*, Node*, Node*); void splitclean(void); Node* ncon(uint32 i); void gtrack(Sym*); +void gargsize(int32); /* * obj.c diff --git a/src/cmd/5g/ggen.c b/src/cmd/5g/ggen.c index de1671bb6..040c3d2a9 100644 --- a/src/cmd/5g/ggen.c +++ b/src/cmd/5g/ggen.c @@ -9,9 +9,15 @@ #include "gg.h" #include "opt.h" +static Prog* appendp(Prog*, int, int, int, int32, int, int, int32); + void -defframe(Prog *ptxt) +defframe(Prog *ptxt, Bvec *bv) { + int i, j, first; + uint32 frame; + Prog *p, *p1; + // fill in argument size ptxt->to.type = D_CONST2; ptxt->to.offset2 = rnd(curfn->type->argwid, widthptr); @@ -19,8 +25,62 @@ defframe(Prog *ptxt) // fill in final stack size if(stksize > maxstksize) maxstksize = stksize; - ptxt->to.offset = rnd(maxstksize+maxarg, widthptr); + frame = rnd(maxstksize+maxarg, widthptr); + ptxt->to.offset = frame; maxstksize = 0; + + // insert code to clear pointered part of the frame, + // so that garbage collector only sees initialized values + // when it looks for pointers. + p = ptxt; + while(p->link->as == AFUNCDATA || p->link->as == APCDATA || p->link->as == ATYPE) + p = p->link; + if(stkzerosize >= 8*widthptr) { + p = appendp(p, AMOVW, D_CONST, NREG, 0, D_REG, 0, 0); + p = appendp(p, AADD, D_CONST, NREG, 4+frame-stkzerosize, D_REG, 1, 0); + p->reg = REGSP; + p = appendp(p, AADD, D_CONST, NREG, stkzerosize, D_REG, 2, 0); + p->reg = 1; + p1 = p = appendp(p, AMOVW, D_REG, 0, 0, D_OREG, 1, 4); + p->scond |= C_PBIT; + p = appendp(p, ACMP, D_REG, 1, 0, D_NONE, 0, 0); + p->reg = 2; + p = appendp(p, ABNE, D_NONE, NREG, 0, D_BRANCH, NREG, 0); + patch(p, p1); + } else { + first = 1; + j = (stkptrsize - stkzerosize)/widthptr * 2; + for(i=0; i<stkzerosize; i+=widthptr) { + if(bvget(bv, j) || bvget(bv, j+1)) { + if(first) { + p = appendp(p, AMOVW, D_CONST, NREG, 0, D_REG, 0, 0); + first = 0; + } + p = appendp(p, AMOVW, D_REG, 0, 0, D_OREG, REGSP, 4+frame-stkzerosize+i); + } + j += 2; + } + } +} + +static Prog* +appendp(Prog *p, int as, int ftype, int freg, int32 foffset, int ttype, int treg, int32 toffset) +{ + Prog *q; + + q = mal(sizeof(*q)); + clearp(q); + q->as = as; + q->lineno = p->lineno; + q->from.type = ftype; + q->from.reg = freg; + q->from.offset = foffset; + q->to.type = ttype; + q->to.reg = treg; + q->to.offset = toffset; + q->link = p->link; + p->link = q; + return q; } // Sweep the prog list to mark any used nodes. @@ -39,7 +99,7 @@ markautoused(Prog* p) } } -// Fixup instructions after compactframe has moved all autos around. +// Fixup instructions after allocauto (formerly compactframe) has moved all autos around. void fixautoused(Prog* p) { @@ -73,9 +133,28 @@ fixautoused(Prog* p) void ginscall(Node *f, int proc) { + int32 arg; Prog *p; Node n1, r, r1, con; + if(f->type != T) + setmaxarg(f->type); + + arg = -1; + // Most functions have a fixed-size argument block, so traceback uses that during unwind. + // Not all, though: there are some variadic functions in package runtime, + // and for those we emit call-specific metadata recorded by caller. + // Reflect generates functions with variable argsize (see reflect.methodValueCall/makeFuncStub), + // so we do this for all indirect calls as well. + if(f->type != T && (f->sym == S || (f->sym != S && f->sym->pkg == runtimepkg) || proc == 1 || proc == 2)) { + arg = f->type->argwid; + if(proc == 1 || proc == 2) + arg += 3*widthptr; + } + + if(arg != -1) + gargsize(arg); + switch(proc) { default: fatal("ginscall: bad proc %d", proc); @@ -84,6 +163,20 @@ ginscall(Node *f, int proc) case 0: // normal call case -1: // normal call but no return if(f->op == ONAME && f->class == PFUNC) { + if(f == deferreturn) { + // Deferred calls will appear to be returning to + // the BL deferreturn(SB) that we are about to emit. + // However, the stack trace code will show the line + // of the instruction before that return PC. + // To avoid that instruction being an unrelated instruction, + // insert a NOP so that we will have the right line number. + // ARM NOP 0x00000000 is really AND.EQ R0, R0, R0. + // Use the latter form because the NOP pseudo-instruction + // would be removed by the linker. + nodreg(&r, types[TINT], 0); + p = gins(AAND, &r, &r); + p->scond = C_SCOND_EQ; + } p = gins(ABL, N, f); afunclit(&p->to, f); if(proc == -1 || noreturn(p)) @@ -156,6 +249,9 @@ ginscall(Node *f, int proc) } break; } + + if(arg != -1) + gargsize(-1); } /* @@ -211,6 +307,7 @@ cgen_callinter(Node *n, Node *res, int proc) nodo.xoffset -= widthptr; cgen(&nodo, &nodr); // REG = 0(REG) -- i.tab + cgen_checknil(&nodr); // in case offset is huge nodo.xoffset = n->left->xoffset + 3*widthptr + 8; @@ -225,14 +322,11 @@ cgen_callinter(Node *n, Node *res, int proc) p->from.type = D_CONST; // REG = &(20+offset(REG)) -- i.tab->fun[f] } - // BOTCH nodr.type = fntype; nodr.type = n->left->type; ginscall(&nodr, proc); regfree(&nodr); regfree(&nodo); - - setmaxarg(n->left->type); } /* @@ -260,8 +354,6 @@ cgen_call(Node *n, int proc) genlist(n->list); // assign the args t = n->left->type; - setmaxarg(t); - // call tempname pointer if(n->left->ullman >= UINF) { regalloc(&nod, types[tptr], N); @@ -365,11 +457,19 @@ cgen_aret(Node *n, Node *res) void cgen_ret(Node *n) { + Prog *p; + genlist(n->list); // copy out args - if(hasdefer || curfn->exit) + if(hasdefer || curfn->exit) { gjmp(retpc); - else - gins(ARET, N, N); + return; + } + p = gins(ARET, N, N); + if(n->op == ORETJMP) { + p->to.name = D_EXTERN; + p->to.type = D_CONST; + p->to.sym = n->left->sym; + } } /* @@ -601,6 +701,8 @@ cgen_shift(int op, int bounded, Node *nl, Node *nr, Node *res) gshift(AMOVW, &n2, SHIFT_LL, v, &n1); gshift(AORR, &n2, SHIFT_LR, w-v, &n1); regfree(&n2); + // Ensure sign/zero-extended result. + gins(optoas(OAS, nl->type), &n1, &n1); } gmove(&n1, res); regfree(&n1); @@ -626,6 +728,8 @@ cgen_shift(int op, int bounded, Node *nl, Node *nr, Node *res) else // OLSH gshift(AMOVW, &n1, SHIFT_LL, sc, &n1); } + if(w < 32 && op == OLSH) + gins(optoas(OAS, nl->type), &n1, &n1); gmove(&n1, res); regfree(&n1); return; @@ -699,6 +803,9 @@ cgen_shift(int op, int bounded, Node *nl, Node *nr, Node *res) regfree(&n3); patch(p3, pc); + // Left-shift of smaller word must be sign/zero-extended. + if(w < 32 && op == OLSH) + gins(optoas(OAS, nl->type), &n2, &n2); gmove(&n2, res); regfree(&n1); @@ -759,7 +866,7 @@ clearfat(Node *nl) } while(c > 0) { - p = gins(AMOVBU, &nz, &dst); + p = gins(AMOVB, &nz, &dst); p->to.type = D_OREG; p->to.offset = 1; p->scond |= C_PBIT; @@ -769,3 +876,43 @@ clearfat(Node *nl) regfree(&dst); regfree(&nz); } + +// Called after regopt and peep have run. +// Expand CHECKNIL pseudo-op into actual nil pointer check. +void +expandchecks(Prog *firstp) +{ + int reg; + Prog *p, *p1; + + for(p = firstp; p != P; p = p->link) { + if(p->as != ACHECKNIL) + continue; + if(debug_checknil && p->lineno > 1) // p->lineno==1 in generated wrappers + warnl(p->lineno, "generated nil check"); + if(p->from.type != D_REG) + fatal("invalid nil check %P", p); + reg = p->from.reg; + // check is + // CMP arg, $0 + // MOV.EQ arg, 0(arg) + p1 = mal(sizeof *p1); + clearp(p1); + p1->link = p->link; + p->link = p1; + p1->lineno = p->lineno; + p1->loc = 9999; + p1->as = AMOVW; + p1->from.type = D_REG; + p1->from.reg = reg; + p1->to.type = D_OREG; + p1->to.reg = reg; + p1->to.offset = 0; + p1->scond = C_SCOND_EQ; + p->as = ACMP; + p->from.type = D_CONST; + p->from.reg = NREG; + p->from.offset = 0; + p->reg = reg; + } +} diff --git a/src/cmd/5g/gobj.c b/src/cmd/5g/gobj.c index 9c5fb2a96..212ffc271 100644 --- a/src/cmd/5g/gobj.c +++ b/src/cmd/5g/gobj.c @@ -35,9 +35,9 @@ void zname(Biobuf *b, Sym *s, int t) { - Bputc(b, ANAME); /* as */ - Bputc(b, t); /* type */ - Bputc(b, s->sym); /* sym */ + BPUTC(b, ANAME); /* as */ + BPUTC(b, t); /* type */ + BPUTC(b, s->sym); /* sym */ Bputname(b, s); } @@ -45,12 +45,12 @@ zname(Biobuf *b, Sym *s, int t) void zfile(Biobuf *b, char *p, int n) { - Bputc(b, ANAME); - Bputc(b, D_FILE); - Bputc(b, 1); - Bputc(b, '<'); + BPUTC(b, ANAME); + BPUTC(b, D_FILE); + BPUTC(b, 1); + BPUTC(b, '<'); Bwrite(b, p, n); - Bputc(b, 0); + BPUTC(b, 0); } void @@ -58,13 +58,10 @@ zhist(Biobuf *b, int line, vlong offset) { Addr a; - Bputc(b, AHISTORY); - Bputc(b, C_SCOND_NONE); - Bputc(b, NREG); - Bputc(b, line); - Bputc(b, line>>8); - Bputc(b, line>>16); - Bputc(b, line>>24); + BPUTC(b, AHISTORY); + BPUTC(b, C_SCOND_NONE); + BPUTC(b, NREG); + BPUTLE4(b, line); zaddr(b, &zprog.from, 0, 0); a = zprog.to; if(offset != 0) { @@ -91,11 +88,11 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype) fatal("We should no longer generate these as types"); default: - Bputc(b, a->type); - Bputc(b, a->reg); - Bputc(b, s); - Bputc(b, a->name); - Bputc(b, gotype); + BPUTC(b, a->type); + BPUTC(b, a->reg); + BPUTC(b, s); + BPUTC(b, a->name); + BPUTC(b, gotype); } switch(a->type) { @@ -110,10 +107,7 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype) case D_CONST2: l = a->offset2; - Bputc(b, l); - Bputc(b, l>>8); - Bputc(b, l>>16); - Bputc(b, l>>24); // fall through + BPUTLE4(b, l); // fall through case D_OREG: case D_CONST: case D_SHIFT: @@ -122,10 +116,7 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype) case D_EXTERN: case D_PARAM: l = a->offset; - Bputc(b, l); - Bputc(b, l>>8); - Bputc(b, l>>16); - Bputc(b, l>>24); + BPUTLE4(b, l); break; case D_BRANCH: @@ -133,37 +124,26 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype) fatal("unpatched branch"); a->offset = a->u.branch->loc; l = a->offset; - Bputc(b, l); - Bputc(b, l>>8); - Bputc(b, l>>16); - Bputc(b, l>>24); + BPUTLE4(b, l); break; case D_SCONST: n = a->u.sval; for(i=0; i<NSNAME; i++) { - Bputc(b, *n); + BPUTC(b, *n); n++; } break; case D_REGREG: case D_REGREG2: - Bputc(b, a->offset); + BPUTC(b, a->offset); break; case D_FCONST: ieeedtod(&e, a->u.dval); - l = e; - Bputc(b, l); - Bputc(b, l>>8); - Bputc(b, l>>16); - Bputc(b, l>>24); - l = e >> 32; - Bputc(b, l); - Bputc(b, l>>8); - Bputc(b, l>>16); - Bputc(b, l>>24); + BPUTLE4(b, e); + BPUTLE4(b, e >> 32); break; } } @@ -271,13 +251,10 @@ dumpfuncs(void) break; } - Bputc(bout, p->as); - Bputc(bout, p->scond); - Bputc(bout, p->reg); - Bputc(bout, p->lineno); - Bputc(bout, p->lineno>>8); - Bputc(bout, p->lineno>>16); - Bputc(bout, p->lineno>>24); + BPUTC(bout, p->as); + BPUTC(bout, p->scond); + BPUTC(bout, p->reg); + BPUTLE4(bout, p->lineno); zaddr(bout, &p->from, sf, gf); zaddr(bout, &p->to, st, gt); } @@ -518,87 +495,6 @@ dsymptr(Sym *s, int off, Sym *x, int xoff) return off; } - -void -genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface) -{ - // TODO(kaib): re-implement genembedtramp - genwrapper(rcvr, method, newnam, iface); -/* - Sym *e; - int c, d, o; - Prog *p; - Type *f; - - e = method->sym; - for(d=0; d<nelem(dotlist); d++) { - c = adddot1(e, rcvr, d, nil, 0); - if(c == 1) - goto out; - } - fatal("genembedtramp %T.%S", rcvr, method->sym); - -out: - newplist()->name = newname(newnam); - - //TEXT main·S_test2(SB),7,$0 - p = pc; - gins(ATEXT, N, N); - p->from.type = D_OREG; - p->from.name = D_EXTERN; - p->from.sym = newnam; - p->to.type = D_CONST2; - p->reg = 7; - p->to.offset2 = 0; - p->to.reg = NREG; -//print("1. %P\n", p); - - o = 0; - for(c=d-1; c>=0; c--) { - f = dotlist[c].field; - o += f->width; - if(!isptr[f->type->etype]) - continue; - - //MOVW o(R0), R0 - p = pc; - gins(AMOVW, N, N); - p->from.type = D_OREG; - p->from.reg = REGARG; - p->from.offset = o; - p->to.type = D_REG; - p->to.reg = REGARG; -//print("2. %P\n", p); - o = 0; - } - if(o != 0) { - //MOVW $XX(R0), R0 - p = pc; - gins(AMOVW, N, N); - p->from.type = D_CONST; - p->from.reg = REGARG; - p->from.offset = o; - p->to.type = D_REG; - p->to.reg = REGARG; -//print("3. %P\n", p); - } - - f = dotlist[0].field; - //B main·*Sub_test2(SB) - if(isptr[f->type->etype]) - f = f->type; - p = pc; - gins(AB, N, N); - p->to.type = D_OREG; - p->to.reg = NREG; - p->to.name = D_EXTERN; - p->to.sym = methodsym(method->sym, ptrto(f->type), 0); -//print("4. %P\n", p); - - pc->as = ARET; // overwrite AEND -*/ -} - void nopout(Prog *p) { diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c index 815d6fab2..27749b7a7 100644 --- a/src/cmd/5g/gsubr.c +++ b/src/cmd/5g/gsubr.c @@ -31,9 +31,11 @@ #include <u.h> #include <libc.h> #include "gg.h" +#include "../../pkg/runtime/funcdata.h" -// TODO(kaib): Can make this bigger if we move +// TODO(rsc): Can make this bigger if we move // the text segment up higher in 5l for all GOOS. +// At the same time, can raise StackBig in ../../pkg/runtime/stack.h. long unmappedzero = 4096; void @@ -209,6 +211,16 @@ ggloblnod(Node *nam) } void +gargsize(int32 size) +{ + Node n1, n2; + + nodconst(&n1, types[TINT32], PCDATA_ArgSize); + nodconst(&n2, types[TINT32], size); + gins(APCDATA, &n1, &n2); +} + +void ggloblsym(Sym *s, int32 width, int dupok, int rodata) { Prog *p; @@ -694,16 +706,24 @@ gmove(Node *f, Node *t) * integer copy and truncate */ case CASE(TINT8, TINT8): // same size + if(!ismem(f)) { + a = AMOVB; + break; + } case CASE(TUINT8, TINT8): case CASE(TINT16, TINT8): // truncate case CASE(TUINT16, TINT8): case CASE(TINT32, TINT8): case CASE(TUINT32, TINT8): - a = AMOVB; + a = AMOVBS; break; - case CASE(TINT8, TUINT8): case CASE(TUINT8, TUINT8): + if(!ismem(f)) { + a = AMOVB; + break; + } + case CASE(TINT8, TUINT8): case CASE(TINT16, TUINT8): case CASE(TUINT16, TUINT8): case CASE(TINT32, TUINT8): @@ -713,7 +733,7 @@ gmove(Node *f, Node *t) case CASE(TINT64, TINT8): // truncate low word case CASE(TUINT64, TINT8): - a = AMOVB; + a = AMOVBS; goto trunc64; case CASE(TINT64, TUINT8): @@ -722,14 +742,22 @@ gmove(Node *f, Node *t) goto trunc64; case CASE(TINT16, TINT16): // same size + if(!ismem(f)) { + a = AMOVH; + break; + } case CASE(TUINT16, TINT16): case CASE(TINT32, TINT16): // truncate case CASE(TUINT32, TINT16): - a = AMOVH; + a = AMOVHS; break; - case CASE(TINT16, TUINT16): case CASE(TUINT16, TUINT16): + if(!ismem(f)) { + a = AMOVH; + break; + } + case CASE(TINT16, TUINT16): case CASE(TINT32, TUINT16): case CASE(TUINT32, TUINT16): a = AMOVHU; @@ -737,7 +765,7 @@ gmove(Node *f, Node *t) case CASE(TINT64, TINT16): // truncate low word case CASE(TUINT64, TINT16): - a = AMOVH; + a = AMOVHS; goto trunc64; case CASE(TINT64, TUINT16): @@ -789,7 +817,7 @@ gmove(Node *f, Node *t) case CASE(TINT8, TUINT16): case CASE(TINT8, TINT32): case CASE(TINT8, TUINT32): - a = AMOVB; + a = AMOVBS; goto rdst; case CASE(TINT8, TINT64): // convert via int32 case CASE(TINT8, TUINT64): @@ -809,7 +837,7 @@ gmove(Node *f, Node *t) case CASE(TINT16, TINT32): // sign extend int16 case CASE(TINT16, TUINT32): - a = AMOVH; + a = AMOVHS; goto rdst; case CASE(TINT16, TINT64): // convert via int32 case CASE(TINT16, TUINT64): @@ -881,13 +909,13 @@ gmove(Node *f, Node *t) ta = AMOVW; switch(tt) { case TINT8: - ta = AMOVB; + ta = AMOVBS; break; case TUINT8: ta = AMOVBU; break; case TINT16: - ta = AMOVH; + ta = AMOVHS; break; case TUINT16: ta = AMOVHU; @@ -928,13 +956,13 @@ gmove(Node *f, Node *t) fa = AMOVW; switch(ft) { case TINT8: - fa = AMOVB; + fa = AMOVBS; break; case TUINT8: fa = AMOVBU; break; case TINT16: - fa = AMOVH; + fa = AMOVHS; break; case TUINT16: fa = AMOVHU; @@ -1161,48 +1189,6 @@ gregshift(int as, Node *lhs, int32 stype, Node *reg, Node *rhs) return p; } -// Generate an instruction referencing *n -// to force segv on nil pointer dereference. -void -checkref(Node *n, int force) -{ - Node m1, m2; - - if(!force && isptr[n->type->etype] && n->type->type->width < unmappedzero) - return; - - regalloc(&m1, types[TUINTPTR], n); - regalloc(&m2, types[TUINT8], n); - cgen(n, &m1); - m1.xoffset = 0; - m1.op = OINDREG; - m1.type = types[TUINT8]; - gins(AMOVBU, &m1, &m2); - regfree(&m2); - regfree(&m1); -} - -static void -checkoffset(Addr *a, int canemitcode) -{ - Prog *p; - Node n1; - - if(a->offset < unmappedzero) - return; - if(!canemitcode) - fatal("checkoffset %#x, cannot emit code", a->offset); - - // cannot rely on unmapped nil page at 0 to catch - // reference with large offset. instead, emit explicit - // test of 0(reg). - regalloc(&n1, types[TUINTPTR], N); - p = gins(AMOVB, N, &n1); - p->from = *a; - p->from.offset = 0; - regfree(&n1); -} - /* * generate code to compute n; * make a refer to result. @@ -1266,7 +1252,6 @@ naddr(Node *n, Addr *a, int canemitcode) a->reg = n->val.u.reg; a->sym = n->sym; a->offset = n->xoffset; - checkoffset(a, canemitcode); break; case OPARAM: @@ -1374,8 +1359,16 @@ naddr(Node *n, Addr *a, int canemitcode) a->etype = TINT32; if(a->type == D_CONST && a->offset == 0) break; // len(nil) - if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero) - checkoffset(a, canemitcode); + break; + + case OSPTR: + // pointer in a string or slice + naddr(n->left, a, canemitcode); + if(a->type == D_CONST && a->offset == 0) + break; // ptr(nil) + a->etype = simtype[TUINTPTR]; + a->offset += Array_array; + a->width = widthptr; break; case OLEN: @@ -1385,8 +1378,6 @@ naddr(Node *n, Addr *a, int canemitcode) if(a->type == D_CONST && a->offset == 0) break; // len(nil) a->offset += Array_nel; - if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero) - checkoffset(a, canemitcode); break; case OCAP: @@ -1396,8 +1387,6 @@ naddr(Node *n, Addr *a, int canemitcode) if(a->type == D_CONST && a->offset == 0) break; // cap(nil) a->offset += Array_cap; - if(a->offset >= unmappedzero && a->offset-Array_cap < unmappedzero) - checkoffset(a, canemitcode); break; case OADDR: @@ -1563,16 +1552,19 @@ optoas(int op, Type *t) break; case CASE(OAS, TBOOL): - case CASE(OAS, TINT8): a = AMOVB; break; + case CASE(OAS, TINT8): + a = AMOVBS; + break; + case CASE(OAS, TUINT8): a = AMOVBU; break; case CASE(OAS, TINT16): - a = AMOVH; + a = AMOVHS; break; case CASE(OAS, TUINT16): @@ -1865,7 +1857,8 @@ lit: default: return 0; case AADD: case ASUB: case AAND: case AORR: case AEOR: - case AMOVB: case AMOVBU: case AMOVH: case AMOVHU: + case AMOVB: case AMOVBS: case AMOVBU: + case AMOVH: case AMOVHS: case AMOVHU: case AMOVW: break; } @@ -1900,13 +1893,15 @@ odot: n1.xoffset = oary[0]; } else { cgen(nn, reg); + cgen_checknil(reg); n1.xoffset = -(oary[0]+1); } for(i=1; i<o; i++) { if(oary[i] >= 0) - fatal("cant happen"); + fatal("can't happen"); gins(AMOVW, &n1, reg); + cgen_checknil(reg); n1.xoffset = -(oary[i]+1); } @@ -1954,9 +1949,10 @@ oindex: // load the array (reg) if(l->ullman > r->ullman) { regalloc(reg, types[tptr], N); - if(o & OPtrto) + if(o & OPtrto) { cgen(l, reg); - else + cgen_checknil(reg); + } else agen(l, reg); } @@ -1973,9 +1969,10 @@ oindex: // load the array (reg) if(l->ullman <= r->ullman) { regalloc(reg, types[tptr], N); - if(o & OPtrto) + if(o & OPtrto) { cgen(l, reg); - else + cgen_checknil(reg); + } else agen(l, reg); } @@ -1987,20 +1984,10 @@ oindex: n2.type = types[tptr]; n2.xoffset = Array_nel; } else { - if(l->type->width >= unmappedzero && l->op == OIND) { - // cannot rely on page protections to - // catch array ptr == 0, so dereference. - n2 = *reg; - n2.op = OINDREG; - n2.type = types[TUINTPTR]; - n2.xoffset = 0; - regalloc(&n3, n2.type, N); - gins(AMOVW, &n2, &n3); - regfree(&n3); - } - nodconst(&n2, types[TUINT32], l->type->bound); if(o & OPtrto) nodconst(&n2, types[TUINT32], l->type->type->bound); + else + nodconst(&n2, types[TUINT32], l->type->bound); } regalloc(&n3, n2.type, N); cgen(&n2, &n3); @@ -2048,14 +2035,14 @@ oindex_const: // can multiply by width statically regalloc(reg, types[tptr], N); - if(o & OPtrto) + if(o & OPtrto) { cgen(l, reg); - else + cgen_checknil(reg); + } else agen(l, reg); v = mpgetfix(r->val.u.xval); if(o & ODynam) { - if(!debug['B'] && !n->bounded) { n1 = *reg; n1.op = OINDREG; diff --git a/src/cmd/5g/opt.h b/src/cmd/5g/opt.h index af7d654de..15b9d1458 100644 --- a/src/cmd/5g/opt.h +++ b/src/cmd/5g/opt.h @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include "../gc/popt.h" + #define Z N #define Adr Addr @@ -50,9 +52,10 @@ typedef struct Rgn Rgn; // A Reg is a wrapper around a single Prog (one instruction) that holds // register optimization information while the optimizer runs. // r->prog is the instruction. -// r->prog->regp points back to r. +// r->prog->opt points back to r. struct Reg { + Flow f; Bits set; // variables written by this instruction. Bits use1; // variables read by prog->from. @@ -66,19 +69,6 @@ struct Reg Bits act; int32 regu; // register used bitmap - int32 rpo; // reverse post ordering - int32 active; - - uint16 loop; // x5 for every loop - uchar refset; // diagnostic generated - - Reg* p1; // predecessors of this instruction: p1, - Reg* p2; // and then p2 linked though p2link. - Reg* p2link; - Reg* s1; // successors of this instruction (at most two: s1 and s2). - Reg* s2; - Reg* link; // next instruction in function code - Prog* prog; // actual instruction }; #define R ((Reg*)0) @@ -93,8 +83,6 @@ struct Rgn EXTERN int32 exregoffset; // not set EXTERN int32 exfregoffset; // not set -EXTERN Reg* firstr; -EXTERN Reg* lastr; EXTERN Reg zreg; EXTERN Reg* freer; EXTERN Reg** rpo2r; @@ -132,36 +120,81 @@ void regopt(Prog*); void addmove(Reg*, int, int, int); Bits mkvar(Reg *r, Adr *a); void prop(Reg*, Bits, Bits); -void loopit(Reg*, int32); void synch(Reg*, Bits); uint32 allreg(uint32, Rgn*); void paint1(Reg*, int); uint32 paint2(Reg*, int); void paint3(Reg*, int, int32, int); void addreg(Adr*, int); -void dumpit(char *str, Reg *r0); -int noreturn(Prog *p); +void dumpit(char *str, Flow *r0, int); /* * peep.c */ -void peep(void); -void excise(Reg*); -Reg* uniqp(Reg*); -Reg* uniqs(Reg*); -int regtyp(Adr*); -int anyvar(Adr*); -int subprop(Reg*); -int copyprop(Reg*); -int copy1(Adr*, Adr*, Reg*, int); +void peep(Prog*); +void excise(Flow*); int copyu(Prog*, Adr*, Adr*); -int copyas(Adr*, Adr*); -int copyau(Adr*, Adr*); -int copysub(Adr*, Adr*, Adr*, int); -int copysub1(Prog*, Adr*, Adr*, int); - int32 RtoB(int); int32 FtoB(int); int BtoR(int32); int BtoF(int32); + +/* + * prog.c + */ +typedef struct ProgInfo ProgInfo; +struct ProgInfo +{ + uint32 flags; // the bits below +}; + +enum +{ + // Pseudo-op, like TEXT, GLOBL, TYPE, PCDATA, FUNCDATA. + Pseudo = 1<<1, + + // There's nothing to say about the instruction, + // but it's still okay to see. + OK = 1<<2, + + // Size of right-side write, or right-side read if no write. + SizeB = 1<<3, + SizeW = 1<<4, + SizeL = 1<<5, + SizeQ = 1<<6, + SizeF = 1<<7, // float aka float32 + SizeD = 1<<8, // double aka float64 + + // Left side: address taken, read, write. + LeftAddr = 1<<9, + LeftRead = 1<<10, + LeftWrite = 1<<11, + + // Register in middle; never written. + RegRead = 1<<12, + CanRegRead = 1<<13, + + // Right side: address taken, read, write. + RightAddr = 1<<14, + RightRead = 1<<15, + RightWrite = 1<<16, + + // Instruction kinds + Move = 1<<17, // straight move + Conv = 1<<18, // size conversion + Cjmp = 1<<19, // conditional jump + Break = 1<<20, // breaks control flow (no fallthrough) + Call = 1<<21, // function call + Jump = 1<<22, // jump + Skip = 1<<23, // data instruction +}; + +void proginfo(ProgInfo*, Prog*); + +// To allow use of AJMP and ACALL in ../gc/popt.c. +enum +{ + AJMP = AB, + ACALL = ABL, +}; diff --git a/src/cmd/5g/peep.c b/src/cmd/5g/peep.c index b6202a882..c78fb3d1c 100644 --- a/src/cmd/5g/peep.c +++ b/src/cmd/5g/peep.c @@ -34,59 +34,45 @@ #include "gg.h" #include "opt.h" -int xtramodes(Reg*, Adr*); -int shiftprop(Reg *r); -void constprop(Adr *c1, Adr *v1, Reg *r); -void predicate(void); -int copyau1(Prog *p, Adr *v); -int isdconst(Addr *a); +static int xtramodes(Graph*, Flow*, Adr*); +static int shortprop(Flow *r); +static int subprop(Flow*); +static int copyprop(Graph*, Flow*); +static int copy1(Adr*, Adr*, Flow*, int); +static int copyas(Adr*, Adr*); +static int copyau(Adr*, Adr*); +static int copysub(Adr*, Adr*, Adr*, int); +static int copysub1(Prog*, Adr*, Adr*, int); +static Flow* findpre(Flow *r, Adr *v); +static int copyau1(Prog *p, Adr *v); +static int isdconst(Addr *a); + +static uint32 gactive; + +// UNUSED +int shiftprop(Flow *r); +void constprop(Adr *c1, Adr *v1, Flow *r); +void predicate(Graph*); void -peep(void) +peep(Prog *firstp) { - Reg *r, *r1, *r2; - Prog *p, *p1; + Flow *r; + Graph *g; + Prog *p; int t; - p1 = nil; -/* - * complete R structure - */ - for(r=firstr; r!=R; r=r1) { - r1 = r->link; - if(r1 == R) - break; - p = r->prog->link; - while(p != r1->prog) - switch(p->as) { - default: - r2 = rega(); - r->link = r2; - r2->link = r1; - - r2->prog = p; - r2->p1 = r; - r->s1 = r2; - r2->s1 = r1; - r1->p1 = r2; - - r = r2; - - case ADATA: - case AGLOBL: - case ANAME: - case ASIGNAME: - case ALOCALS: - case ATYPE: - p = p->link; - } - } -//dumpit("begin", firstr); + g = flowstart(firstp, sizeof(Flow)); + if(g == nil) + return; + gactive = 0; loop1: + if(debug['P'] && debug['v']) + dumpit("loop1", g->start, 0); t = 0; - for(r=firstr; r!=R; r=r->link) { + for(r=g->start; r!=nil; r=r->link) { p = r->prog; switch(p->as) { case ASLL: @@ -102,18 +88,20 @@ loop1: // } break; + case AMOVB: + case AMOVH: case AMOVW: case AMOVF: case AMOVD: if(regtyp(&p->from)) if(p->from.type == p->to.type) if(p->scond == C_SCOND_NONE) { - if(copyprop(r)) { + if(copyprop(g, r)) { excise(r); t++; break; } - if(subprop(r) && copyprop(r)) { + if(subprop(r) && copyprop(g, r)) { excise(r); t++; break; @@ -121,6 +109,16 @@ loop1: } break; + case AMOVHS: + case AMOVHU: + case AMOVBS: + case AMOVBU: + if(p->from.type == D_REG) { + if(shortprop(r)) + t++; + } + break; + #ifdef NOTDEF if(p->scond == C_SCOND_NONE) if(regtyp(&p->to)) @@ -134,8 +132,7 @@ loop1: if(t) goto loop1; - - for(r=firstr; r!=R; r=r->link) { + for(r=g->start; r!=nil; r=r->link) { p = r->prog; switch(p->as) { case AEOR: @@ -152,42 +149,21 @@ loop1: p->reg = NREG; } break; - - case AMOVH: - case AMOVHU: - case AMOVB: - case AMOVBU: - /* - * look for MOVB x,R; MOVB R,R - */ - r1 = r->link; - if(p->to.type != D_REG) - break; - if(r1 == R) - break; - p1 = r1->prog; - if(p1->as != p->as) - break; - if(p1->from.type != D_REG || p1->from.reg != p->to.reg) - break; - if(p1->to.type != D_REG || p1->to.reg != p->to.reg) - break; - excise(r1); - break; } } - for(r=firstr; r!=R; r=r->link) { + for(r=g->start; r!=nil; r=r->link) { p = r->prog; switch(p->as) { case AMOVW: case AMOVB: + case AMOVBS: case AMOVBU: if(p->from.type == D_OREG && p->from.offset == 0) - xtramodes(r, &p->from); + xtramodes(g, r, &p->from); else if(p->to.type == D_OREG && p->to.offset == 0) - xtramodes(r, &p->to); + xtramodes(g, r, &p->to); else continue; break; @@ -198,7 +174,7 @@ loop1: // if(isdconst(&p->from) || p->from.offset != 0) // continue; // r2 = r->s1; -// if(r2 == R) +// if(r2 == nil) // continue; // t = r2->prog->as; // switch(t) { @@ -225,8 +201,8 @@ loop1: // r1 = r; // do // r1 = uniqp(r1); -// while (r1 != R && r1->prog->as == ANOP); -// if(r1 == R) +// while (r1 != nil && r1->prog->as == ANOP); +// if(r1 == nil) // continue; // p1 = r1->prog; // if(p1->to.type != D_REG) @@ -261,44 +237,9 @@ loop1: } } -// predicate(); -} +// predicate(g); -/* - * uniqp returns a "unique" predecessor to instruction r. - * If the instruction is the first one or has multiple - * predecessors due to jump, R is returned. - */ -Reg* -uniqp(Reg *r) -{ - Reg *r1; - - r1 = r->p1; - if(r1 == R) { - r1 = r->p2; - if(r1 == R || r1->p2link != R) - return R; - } else - if(r->p2 != R) - return R; - return r1; -} - -Reg* -uniqs(Reg *r) -{ - Reg *r1; - - r1 = r->s1; - if(r1 == R) { - r1 = r->s2; - if(r1 == R) - return R; - } else - if(r->s2 != R) - return R; - return r1; + flowend(g); } int @@ -326,13 +267,14 @@ regtyp(Adr *a) * hopefully, then the former or latter MOV * will be eliminated by copy propagation. */ -int -subprop(Reg *r0) +static int +subprop(Flow *r0) { Prog *p; Adr *v1, *v2; - Reg *r; + Flow *r; int t; + ProgInfo info; p = r0->prog; v1 = &p->from; @@ -341,70 +283,34 @@ subprop(Reg *r0) v2 = &p->to; if(!regtyp(v2)) return 0; - for(r=uniqp(r0); r!=R; r=uniqp(r)) { - if(uniqs(r) == R) + for(r=uniqp(r0); r!=nil; r=uniqp(r)) { + if(uniqs(r) == nil) break; p = r->prog; - switch(p->as) { - case ABL: + proginfo(&info, p); + if(info.flags & Call) return 0; + if((info.flags & CanRegRead) && p->to.type == D_REG) { + info.flags |= RegRead; + info.flags &= ~(CanRegRead | RightRead); + p->reg = p->to.reg; + } + + switch(p->as) { case AMULLU: case AMULA: case AMVN: return 0; - - case ACMN: - case AADD: - case ASUB: - case ASBC: - case ARSB: - case ASLL: - case ASRL: - case ASRA: - case AORR: - case AAND: - case AEOR: - case AMUL: - case AMULU: - case ADIV: - case ADIVU: - case AMOD: - case AMODU: - - case AADDD: - case AADDF: - case ASUBD: - case ASUBF: - case AMULD: - case AMULF: - case ADIVD: - case ADIVF: - if(p->to.type == v1->type) - if(p->to.reg == v1->reg) - if(p->scond == C_SCOND_NONE) { - if(p->reg == NREG) - p->reg = p->to.reg; - goto gotit; - } - break; - - case AMOVF: - case AMOVD: - case AMOVW: + } + + if((info.flags & (RightRead|RightWrite)) == RightWrite) { if(p->to.type == v1->type) if(p->to.reg == v1->reg) if(p->scond == C_SCOND_NONE) goto gotit; - break; - - case AMOVM: - t = 1<<v2->reg; - if((p->from.type == D_CONST && (p->from.offset&t)) || - (p->to.type == D_CONST && (p->to.offset&t))) - return 0; - break; } + if(copyau(&p->from, v2) || copyau1(p, v2) || copyau(&p->to, v2)) @@ -452,49 +358,48 @@ gotit: * set v1 F=1 * set v2 return success */ -int -copyprop(Reg *r0) +static int +copyprop(Graph *g, Flow *r0) { Prog *p; Adr *v1, *v2; - Reg *r; + USED(g); p = r0->prog; v1 = &p->from; v2 = &p->to; if(copyas(v1, v2)) return 1; - for(r=firstr; r!=R; r=r->link) - r->active = 0; + gactive++; return copy1(v1, v2, r0->s1, 0); } -int -copy1(Adr *v1, Adr *v2, Reg *r, int f) +static int +copy1(Adr *v1, Adr *v2, Flow *r, int f) { int t; Prog *p; - if(r->active) { + if(r->active == gactive) { if(debug['P']) print("act set; return 1\n"); return 1; } - r->active = 1; + r->active = gactive; if(debug['P']) print("copy %D->%D f=%d\n", v1, v2, f); - for(; r != R; r = r->s1) { + for(; r != nil; r = r->s1) { p = r->prog; if(debug['P']) print("%P", p); - if(!f && uniqp(r) == R) { + if(!f && uniqp(r) == nil) { f = 1; if(debug['P']) print("; merge; f=%d", f); } t = copyu(p, v2, A); switch(t) { - case 2: /* rar, cant split */ + case 2: /* rar, can't split */ if(debug['P']) print("; %Drar; return 0\n", v2); return 0; @@ -546,6 +451,7 @@ copy1(Adr *v1, Adr *v2, Reg *r, int f) return 1; } +// UNUSED /* * The idea is to remove redundant constants. * $c1->v1 @@ -554,17 +460,17 @@ copy1(Adr *v1, Adr *v2, Reg *r, int f) * The v1->v2 should be eliminated by copy propagation. */ void -constprop(Adr *c1, Adr *v1, Reg *r) +constprop(Adr *c1, Adr *v1, Flow *r) { Prog *p; if(debug['P']) print("constprop %D->%D\n", c1, v1); - for(; r != R; r = r->s1) { + for(; r != nil; r = r->s1) { p = r->prog; if(debug['P']) print("%P", p); - if(uniqp(r) == R) { + if(uniqp(r) == nil) { if(debug['P']) print("; merge; return\n"); return; @@ -586,6 +492,65 @@ constprop(Adr *c1, Adr *v1, Reg *r) } /* + * shortprop eliminates redundant zero/sign extensions. + * + * MOVBS x, R + * <no use R> + * MOVBS R, R' + * + * changed to + * + * MOVBS x, R + * ... + * MOVB R, R' (compiled to mov) + * + * MOVBS above can be a MOVBS, MOVBU, MOVHS or MOVHU. + */ +static int +shortprop(Flow *r) +{ + Prog *p, *p1; + Flow *r1; + + p = r->prog; + r1 = findpre(r, &p->from); + if(r1 == nil) + return 0; + + p1 = r1->prog; + if(p1->as == p->as) { + // Two consecutive extensions. + goto gotit; + } + + if(p1->as == AMOVW && isdconst(&p1->from) + && p1->from.offset >= 0 && p1->from.offset < 128) { + // Loaded an immediate. + goto gotit; + } + + return 0; + +gotit: + if(debug['P']) + print("shortprop\n%P\n%P", p1, p); + switch(p->as) { + case AMOVBS: + case AMOVBU: + p->as = AMOVB; + break; + case AMOVHS: + case AMOVHU: + p->as = AMOVH; + break; + } + if(debug['P']) + print(" => %A\n", p->as); + return 1; +} + +// UNUSED +/* * ASLL x,y,w * .. (not use w, not set x y w) * AXXX w,a,b (a != w) @@ -598,9 +563,9 @@ constprop(Adr *c1, Adr *v1, Reg *r) */ #define FAIL(msg) { if(debug['P']) print("\t%s; FAILURE\n", msg); return 0; } int -shiftprop(Reg *r) +shiftprop(Flow *r) { - Reg *r1; + Flow *r1; Prog *p, *p1, *p2; int n, o; Adr a; @@ -620,9 +585,9 @@ shiftprop(Reg *r) for(;;) { /* find first use of shift result; abort if shift operands or result are changed */ r1 = uniqs(r1); - if(r1 == R) + if(r1 == nil) FAIL("branch"); - if(uniqp(r1) == R) + if(uniqp(r1) == nil) FAIL("merge"); p1 = r1->prog; if(debug['P']) @@ -693,7 +658,7 @@ shiftprop(Reg *r) if(p1->to.reg != n) for (;;) { r1 = uniqs(r1); - if(r1 == R) + if(r1 == nil) FAIL("inconclusive"); p1 = r1->prog; if(debug['P']) @@ -746,40 +711,40 @@ shiftprop(Reg *r) * before r. It must be a set, and there must be * a unique path from that instruction to r. */ -Reg* -findpre(Reg *r, Adr *v) +static Flow* +findpre(Flow *r, Adr *v) { - Reg *r1; + Flow *r1; - for(r1=uniqp(r); r1!=R; r=r1,r1=uniqp(r)) { + for(r1=uniqp(r); r1!=nil; r=r1,r1=uniqp(r)) { if(uniqs(r1) != r) - return R; + return nil; switch(copyu(r1->prog, v, A)) { case 1: /* used */ case 2: /* read-alter-rewrite */ - return R; + return nil; case 3: /* set */ case 4: /* set and used */ return r1; } } - return R; + return nil; } /* * findinc finds ADD instructions with a constant * argument which falls within the immed_12 range. */ -Reg* -findinc(Reg *r, Reg *r2, Adr *v) +static Flow* +findinc(Flow *r, Flow *r2, Adr *v) { - Reg *r1; + Flow *r1; Prog *p; - for(r1=uniqs(r); r1!=R && r1!=r2; r=r1,r1=uniqs(r)) { + for(r1=uniqs(r); r1!=nil && r1!=r2; r=r1,r1=uniqs(r)) { if(uniqp(r1) != r) - return R; + return nil; switch(copyu(r1->prog, v, A)) { case 0: /* not touched */ continue; @@ -790,14 +755,14 @@ findinc(Reg *r, Reg *r2, Adr *v) if(p->from.offset > -4096 && p->from.offset < 4096) return r1; default: - return R; + return nil; } } - return R; + return nil; } -int -nochange(Reg *r, Reg *r2, Prog *p) +static int +nochange(Flow *r, Flow *r2, Prog *p) { Adr a[3]; int i, n; @@ -819,7 +784,7 @@ nochange(Reg *r, Reg *r2, Prog *p) } if(n == 0) return 1; - for(; r!=R && r!=r2; r=uniqs(r)) { + for(; r!=nil && r!=r2; r=uniqs(r)) { p = r->prog; for(i=0; i<n; i++) if(copyu(p, &a[i], A) > 1) @@ -828,10 +793,10 @@ nochange(Reg *r, Reg *r2, Prog *p) return 1; } -int -findu1(Reg *r, Adr *v) +static int +findu1(Flow *r, Adr *v) { - for(; r != R; r = r->s1) { + for(; r != nil; r = r->s1) { if(r->active) return 0; r->active = 1; @@ -850,12 +815,12 @@ findu1(Reg *r, Adr *v) return 0; } -int -finduse(Reg *r, Adr *v) +static int +finduse(Graph *g, Flow *r, Adr *v) { - Reg *r1; + Flow *r1; - for(r1=firstr; r1!=R; r1=r1->link) + for(r1=g->start; r1!=nil; r1=r1->link) r1->active = 0; return findu1(r, v); } @@ -873,10 +838,10 @@ finduse(Reg *r, Adr *v) * into * MOVBU R0<<0(R1),R0 */ -int -xtramodes(Reg *r, Adr *a) +static int +xtramodes(Graph *g, Flow *r, Adr *a) { - Reg *r1, *r2, *r3; + Flow *r1, *r2, *r3; Prog *p, *p1; Adr v; @@ -884,7 +849,7 @@ xtramodes(Reg *r, Adr *a) v = *a; v.type = D_REG; r1 = findpre(r, &v); - if(r1 != R) { + if(r1 != nil) { p1 = r1->prog; if(p1->to.type == D_REG && p1->to.reg == v.reg) switch(p1->as) { @@ -894,12 +859,12 @@ xtramodes(Reg *r, Adr *a) break; if(p1->from.type == D_REG || (p1->from.type == D_SHIFT && (p1->from.offset&(1<<4)) == 0 && - (p->as != AMOVB || (a == &p->from && (p1->from.offset&~0xf) == 0))) || + ((p->as != AMOVB && p->as != AMOVBS) || (a == &p->from && (p1->from.offset&~0xf) == 0))) || (p1->from.type == D_CONST && p1->from.offset > -4096 && p1->from.offset < 4096)) if(nochange(uniqs(r1), r, p1)) { if(a != &p->from || v.reg != p->to.reg) - if (finduse(r->s1, &v)) { + if (finduse(g, r->s1, &v)) { if(p1->reg == NREG || p1->reg == v.reg) /* pre-indexing */ p->scond |= C_WBIT; @@ -927,7 +892,7 @@ xtramodes(Reg *r, Adr *a) break; case AMOVW: if(p1->from.type == D_REG) - if((r2 = findinc(r1, r, &p1->from)) != R) { + if((r2 = findinc(r1, r, &p1->from)) != nil) { for(r3=uniqs(r2); r3->prog->as==ANOP; r3=uniqs(r3)) ; if(r3 == r) { @@ -936,7 +901,7 @@ xtramodes(Reg *r, Adr *a) a->reg = p1->to.reg; a->offset = p1->from.offset; p->scond |= C_PBIT; - if(!finduse(r, &r1->prog->to)) + if(!finduse(g, r, &r1->prog->to)) excise(r1); excise(r2); return 1; @@ -946,7 +911,7 @@ xtramodes(Reg *r, Adr *a) } } if(a != &p->from || a->reg != p->to.reg) - if((r1 = findinc(r, R, &v)) != R) { + if((r1 = findinc(r, nil, &v)) != nil) { /* post-indexing */ p1 = r1->prog; a->offset = p1->from.offset; @@ -971,7 +936,7 @@ copyu(Prog *p, Adr *v, Adr *s) switch(p->as) { default: - print("copyu: cant find %A\n", p->as); + print("copyu: can't find %A\n", p->as); return 2; case AMOVM: @@ -1017,8 +982,10 @@ copyu(Prog *p, Adr *v, Adr *s) case AMOVF: case AMOVD: case AMOVH: + case AMOVHS: case AMOVHU: case AMOVB: + case AMOVBS: case AMOVBU: case AMOVFW: case AMOVWF: @@ -1089,6 +1056,7 @@ copyu(Prog *p, Adr *v, Adr *s) case ADIVF: case ADIVD: + case ACHECKNIL: /* read */ case ACMPF: /* read, read, */ case ACMPD: case ACMP: @@ -1194,7 +1162,8 @@ copyu(Prog *p, Adr *v, Adr *s) return 3; return 0; - case ALOCALS: /* funny */ + case APCDATA: + case AFUNCDATA: return 0; } } @@ -1204,7 +1173,7 @@ copyu(Prog *p, Adr *v, Adr *s) * could be set/use depending on * semantics */ -int +static int copyas(Adr *a, Adr *v) { @@ -1224,10 +1193,24 @@ copyas(Adr *a, Adr *v) return 0; } +int +sameaddr(Adr *a, Adr *v) +{ + if(a->type != v->type) + return 0; + if(regtyp(v) && a->reg == v->reg) + return 1; + if(v->type == D_AUTO || v->type == D_PARAM) { + if(v->offset == a->offset) + return 1; + } + return 0; +} + /* * either direct or indirect */ -int +static int copyau(Adr *a, Adr *v) { @@ -1268,7 +1251,7 @@ copyau(Adr *a, Adr *v) * ADD r,r,r * CMP r,r, */ -int +static int copyau1(Prog *p, Adr *v) { @@ -1284,7 +1267,7 @@ copyau1(Prog *p, Adr *v) return 1; return 0; } - print("copyau1: cant tell %P\n", p); + print("copyau1: can't tell %P\n", p); } return 0; } @@ -1293,7 +1276,7 @@ copyau1(Prog *p, Adr *v) * substitute s for v in a * return failure to substitute */ -int +static int copysub(Adr *a, Adr *v, Adr *s, int f) { @@ -1316,7 +1299,7 @@ copysub(Adr *a, Adr *v, Adr *s, int f) return 0; } -int +static int copysub1(Prog *p1, Adr *v, Adr *s, int f) { @@ -1351,9 +1334,9 @@ struct { }; typedef struct { - Reg *start; - Reg *last; - Reg *end; + Flow *start; + Flow *last; + Flow *end; int len; } Joininfo; @@ -1373,13 +1356,13 @@ enum { Keepbranch }; -int +static int isbranch(Prog *p) { return (ABEQ <= p->as) && (p->as <= ABLE); } -int +static int predicable(Prog *p) { switch(p->as) { @@ -1409,7 +1392,7 @@ predicable(Prog *p) * * C_SBIT may also have been set explicitly in p->scond. */ -int +static int modifiescpsr(Prog *p) { switch(p->as) { @@ -1438,8 +1421,8 @@ modifiescpsr(Prog *p) * Find the maximal chain of instructions starting with r which could * be executed conditionally */ -int -joinsplit(Reg *r, Joininfo *j) +static int +joinsplit(Flow *r, Joininfo *j) { j->start = r; j->last = r; @@ -1474,8 +1457,8 @@ joinsplit(Reg *r, Joininfo *j) return Toolong; } -Reg* -successor(Reg *r) +static Flow* +successor(Flow *r) { if(r->s1) return r->s1; @@ -1483,11 +1466,11 @@ successor(Reg *r) return r->s2; } -void -applypred(Reg *rstart, Joininfo *j, int cond, int branch) +static void +applypred(Flow *rstart, Joininfo *j, int cond, int branch) { int pred; - Reg *r; + Flow *r; if(j->len == 0) return; @@ -1520,13 +1503,13 @@ applypred(Reg *rstart, Joininfo *j, int cond, int branch) } void -predicate(void) +predicate(Graph *g) { - Reg *r; + Flow *r; int t1, t2; Joininfo j1, j2; - for(r=firstr; r!=R; r=r->link) { + for(r=g->start; r!=nil; r=r->link) { if (isbranch(r->prog)) { t1 = joinsplit(r->s1, &j1); t2 = joinsplit(r->s2, &j2); @@ -1549,10 +1532,24 @@ predicate(void) } } -int +static int isdconst(Addr *a) { if(a->type == D_CONST && a->reg == NREG) return 1; return 0; } + +int +stackaddr(Addr *a) +{ + return regtyp(a) && a->reg == REGSP; +} + +int +smallindir(Addr *a, Addr *reg) +{ + return reg->type == D_REG && a->type == D_OREG && + a->reg == reg->reg && + 0 <= a->offset && a->offset < 4096; +} diff --git a/src/cmd/5g/prog.c b/src/cmd/5g/prog.c new file mode 100644 index 000000000..5aa6163d8 --- /dev/null +++ b/src/cmd/5g/prog.c @@ -0,0 +1,143 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include <u.h> +#include <libc.h> +#include "gg.h" +#include "opt.h" + +enum +{ + RightRdwr = RightRead | RightWrite, +}; + +// This table gives the basic information about instruction +// generated by the compiler and processed in the optimizer. +// See opt.h for bit definitions. +// +// Instructions not generated need not be listed. +// As an exception to that rule, we typically write down all the +// size variants of an operation even if we just use a subset. +// +// The table is formatted for 8-space tabs. +static ProgInfo progtable[ALAST] = { + [ATYPE]= {Pseudo | Skip}, + [ATEXT]= {Pseudo}, + [AFUNCDATA]= {Pseudo}, + [APCDATA]= {Pseudo}, + [AUNDEF]= {OK}, + [AUSEFIELD]= {OK}, + [ACHECKNIL]= {LeftRead}, + + // NOP is an internal no-op that also stands + // for USED and SET annotations, not the Intel opcode. + [ANOP]= {LeftRead | RightWrite}, + + // Integer. + [AADC]= {SizeL | LeftRead | RegRead | RightWrite}, + [AADD]= {SizeL | LeftRead | RegRead | RightWrite}, + [AAND]= {SizeL | LeftRead | RegRead | RightWrite}, + [ABIC]= {SizeL | LeftRead | RegRead | RightWrite}, + [ACMN]= {SizeL | LeftRead | RightRead}, + [ACMP]= {SizeL | LeftRead | RightRead}, + [ADIVU]= {SizeL | LeftRead | RegRead | RightWrite}, + [ADIV]= {SizeL | LeftRead | RegRead | RightWrite}, + [AEOR]= {SizeL | LeftRead | RegRead | RightWrite}, + [AMODU]= {SizeL | LeftRead | RegRead | RightWrite}, + [AMOD]= {SizeL | LeftRead | RegRead | RightWrite}, + [AMULALU]= {SizeL | LeftRead | RegRead | RightRdwr}, + [AMULAL]= {SizeL | LeftRead | RegRead | RightRdwr}, + [AMULA]= {SizeL | LeftRead | RegRead | RightRdwr}, + [AMULU]= {SizeL | LeftRead | RegRead | RightWrite}, + [AMUL]= {SizeL | LeftRead | RegRead | RightWrite}, + [AMULL]= {SizeL | LeftRead | RegRead | RightWrite}, + [AMULLU]= {SizeL | LeftRead | RegRead | RightWrite}, + [AMVN]= {SizeL | LeftRead | RightWrite}, + [AORR]= {SizeL | LeftRead | RegRead | RightWrite}, + [ARSB]= {SizeL | LeftRead | RegRead | RightWrite}, + [ARSC]= {SizeL | LeftRead | RegRead | RightWrite}, + [ASBC]= {SizeL | LeftRead | RegRead | RightWrite}, + [ASLL]= {SizeL | LeftRead | RegRead | RightWrite}, + [ASRA]= {SizeL | LeftRead | RegRead | RightWrite}, + [ASRL]= {SizeL | LeftRead | RegRead | RightWrite}, + [ASUB]= {SizeL | LeftRead | RegRead | RightWrite}, + [ATEQ]= {SizeL | LeftRead | RightRead}, + [ATST]= {SizeL | LeftRead | RightRead}, + + // Floating point. + [AADDD]= {SizeD | LeftRead | RightRdwr}, + [AADDF]= {SizeF | LeftRead | RightRdwr}, + [ACMPD]= {SizeD | LeftRead | RightRead}, + [ACMPF]= {SizeF | LeftRead | RightRead}, + [ADIVD]= {SizeD | LeftRead | RightRdwr}, + [ADIVF]= {SizeF | LeftRead | RightRdwr}, + [AMULD]= {SizeD | LeftRead | RightRdwr}, + [AMULF]= {SizeF | LeftRead | RightRdwr}, + [ASUBD]= {SizeD | LeftRead | RightRdwr}, + [ASUBF]= {SizeF | LeftRead | RightRdwr}, + + // Conversions. + [AMOVWD]= {SizeD | LeftRead | RightWrite | Conv}, + [AMOVWF]= {SizeF | LeftRead | RightWrite | Conv}, + [AMOVDF]= {SizeF | LeftRead | RightWrite | Conv}, + [AMOVDW]= {SizeL | LeftRead | RightWrite | Conv}, + [AMOVFD]= {SizeD | LeftRead | RightWrite | Conv}, + [AMOVFW]= {SizeL | LeftRead | RightWrite | Conv}, + + // Moves. + [AMOVB]= {SizeB | LeftRead | RightWrite | Move}, + [AMOVD]= {SizeD | LeftRead | RightWrite | Move}, + [AMOVF]= {SizeF | LeftRead | RightWrite | Move}, + [AMOVH]= {SizeW | LeftRead | RightWrite | Move}, + [AMOVW]= {SizeL | LeftRead | RightWrite | Move}, + + // These should be split into the two different conversions instead + // of overloading the one. + [AMOVBS]= {SizeB | LeftRead | RightWrite | Conv}, + [AMOVBU]= {SizeB | LeftRead | RightWrite | Conv}, + [AMOVHS]= {SizeW | LeftRead | RightWrite | Conv}, + [AMOVHU]= {SizeW | LeftRead | RightWrite | Conv}, + + // Jumps. + [AB]= {Jump | Break}, + [ABL]= {Call}, + [ABEQ]= {Cjmp}, + [ABNE]= {Cjmp}, + [ABCS]= {Cjmp}, + [ABHS]= {Cjmp}, + [ABCC]= {Cjmp}, + [ABLO]= {Cjmp}, + [ABMI]= {Cjmp}, + [ABPL]= {Cjmp}, + [ABVS]= {Cjmp}, + [ABVC]= {Cjmp}, + [ABHI]= {Cjmp}, + [ABLS]= {Cjmp}, + [ABGE]= {Cjmp}, + [ABLT]= {Cjmp}, + [ABGT]= {Cjmp}, + [ABLE]= {Cjmp}, + [ARET]= {Break}, +}; + +void +proginfo(ProgInfo *info, Prog *p) +{ + *info = progtable[p->as]; + if(info->flags == 0) + fatal("unknown instruction %P", p); + + if(p->from.type == D_CONST && p->from.sym != nil && (info->flags & LeftRead)) { + info->flags &= ~LeftRead; + info->flags |= LeftAddr; + } + + if((info->flags & RegRead) && p->reg == NREG) { + info->flags &= ~RegRead; + info->flags |= CanRegRead | RightRead; + } + + if(((p->scond & C_SCOND) != C_SCOND_NONE) && (info->flags & RightWrite)) + info->flags |= RightRead; +} diff --git a/src/cmd/5g/reg.c b/src/cmd/5g/reg.c index eaaaf9be3..d2a8cc488 100644 --- a/src/cmd/5g/reg.c +++ b/src/cmd/5g/reg.c @@ -36,29 +36,10 @@ #define NREGVAR 32 #define REGBITS ((uint32)0xffffffff) -#define P2R(p) (Reg*)(p->reg) void addsplits(void); - int noreturn(Prog *p); -static int first = 0; - -static void fixjmp(Prog*); - - -Reg* -rega(void) -{ - Reg *r; - - r = freer; - if(r == R) { - r = mal(sizeof(*r)); - } else - freer = r->link; - - *r = zreg; - return r; -} +static Reg* firstr; +static int first = 1; int rcmp(const void *a1, const void *a2) @@ -100,7 +81,7 @@ setoutvar(void) } void -excise(Reg *r) +excise(Flow *r) { Prog *p; @@ -177,38 +158,19 @@ regopt(Prog *firstp) { Reg *r, *r1; Prog *p; - int i, z, nr; + Graph *g; + int i, z; uint32 vreg; Bits bit; - - if(first == 0) { + ProgInfo info; + + if(first) { fmtinstall('Q', Qconv); + first = 0; } fixjmp(firstp); - - first++; - if(debug['K']) { - if(first != 13) - return; -// debug['R'] = 2; -// debug['P'] = 2; - print("optimizing %S\n", curfn->nname->sym); - } - - // count instructions - nr = 0; - for(p=firstp; p!=P; p=p->link) - nr++; - - // if too big dont bother - if(nr >= 10000) { -// print("********** %S is too big (%d)\n", curfn->nname->sym, nr); - return; - } - - firstr = R; - lastr = R; + mergetemp(firstp); /* * control flow is more complicated in generated go code @@ -241,178 +203,43 @@ regopt(Prog *firstp) * allocate pcs * find use and set of variables */ - nr = 0; - for(p=firstp; p != P; p = p->link) { - switch(p->as) { - case ADATA: - case AGLOBL: - case ANAME: - case ASIGNAME: - case ALOCALS: - case ATYPE: - continue; - } - r = rega(); - nr++; - if(firstr == R) { - firstr = r; - lastr = r; - } else { - lastr->link = r; - r->p1 = lastr; - lastr->s1 = r; - lastr = r; - } - r->prog = p; - p->regp = r; - - r1 = r->p1; - if(r1 != R) { - switch(r1->prog->as) { - case ARET: - case AB: - case ARFE: - r->p1 = R; - r1->s1 = R; - } - } + g = flowstart(firstp, sizeof(Reg)); + if(g == nil) + return; + firstr = (Reg*)g->start; + + for(r = firstr; r != R; r = (Reg*)r->f.link) { + p = r->f.prog; + proginfo(&info, p); // Avoid making variables for direct-called functions. if(p->as == ABL && p->to.type == D_EXTERN) continue; - /* - * left side always read - */ bit = mkvar(r, &p->from); - for(z=0; z<BITS; z++) - r->use1.b[z] |= bit.b[z]; - - /* - * middle always read when present - */ - if(p->reg != NREG) { + if(info.flags & LeftRead) + for(z=0; z<BITS; z++) + r->use1.b[z] |= bit.b[z]; + if(info.flags & LeftAddr) + setaddrs(bit); + + if(info.flags & RegRead) { if(p->from.type != D_FREG) r->use1.b[0] |= RtoB(p->reg); else r->use1.b[0] |= FtoB(p->reg); } - /* - * right side depends on opcode - */ - bit = mkvar(r, &p->to); - if(bany(&bit)) - switch(p->as) { - default: - yyerror("reg: unknown op: %A", p->as); - break; - - /* - * right side read - */ - case ATST: - case ATEQ: - case ACMP: - case ACMN: - case ACMPD: - case ACMPF: - rightread: - for(z=0; z<BITS; z++) - r->use2.b[z] |= bit.b[z]; - break; - - /* - * right side read or read+write, depending on middle - * ADD x, z => z += x - * ADD x, y, z => z = x + y - */ - case AADD: - case AAND: - case AEOR: - case ASUB: - case ARSB: - case AADC: - case ASBC: - case ARSC: - case AORR: - case ABIC: - case ASLL: - case ASRL: - case ASRA: - case AMUL: - case AMULU: - case ADIV: - case AMOD: - case AMODU: - case ADIVU: - if(p->reg != NREG) - goto rightread; - // fall through - - /* - * right side read+write - */ - case AADDF: - case AADDD: - case ASUBF: - case ASUBD: - case AMULF: - case AMULD: - case ADIVF: - case ADIVD: - case AMULA: - case AMULAL: - case AMULALU: - for(z=0; z<BITS; z++) { - r->use2.b[z] |= bit.b[z]; - r->set.b[z] |= bit.b[z]; - } - break; - - /* - * right side write - */ - case ANOP: - case AMOVB: - case AMOVBU: - case AMOVD: - case AMOVDF: - case AMOVDW: - case AMOVF: - case AMOVFW: - case AMOVH: - case AMOVHU: - case AMOVW: - case AMOVWD: - case AMOVWF: - case AMVN: - case AMULL: - case AMULLU: - if((p->scond & C_SCOND) != C_SCOND_NONE) + if(info.flags & (RightAddr | RightRead | RightWrite)) { + bit = mkvar(r, &p->to); + if(info.flags & RightAddr) + setaddrs(bit); + if(info.flags & RightRead) for(z=0; z<BITS; z++) r->use2.b[z] |= bit.b[z]; - for(z=0; z<BITS; z++) - r->set.b[z] |= bit.b[z]; - break; - - /* - * funny - */ - case ABL: - setaddrs(bit); - break; - } - - if(p->as == AMOVM) { - z = p->to.offset; - if(p->from.type == D_CONST) - z = p->from.offset; - for(i=0; z; i++) { - if(z&1) - regbits |= RtoB(i); - z >>= 1; - } + if(info.flags & RightWrite) + for(z=0; z<BITS; z++) + r->set.b[z] |= bit.b[z]; } } if(firstr == R) @@ -432,50 +259,16 @@ regopt(Prog *firstp) } if(debug['R'] && debug['v']) - dumpit("pass1", firstr); + dumpit("pass1", &firstr->f, 1); /* * pass 2 - * turn branch references to pointers - * build back pointers - */ - for(r=firstr; r!=R; r=r->link) { - p = r->prog; - if(p->to.type == D_BRANCH) { - if(p->to.u.branch == P) - fatal("pnil %P", p); - r1 = p->to.u.branch->regp; - if(r1 == R) - fatal("rnil %P", p); - if(r1 == r) { - //fatal("ref to self %P", p); - continue; - } - r->s2 = r1; - r->p2link = r1->p2; - r1->p2 = r; - } - } - if(debug['R']) { - p = firstr->prog; - print("\n%L %D\n", p->lineno, &p->from); - print(" addr = %Q\n", addrs); - } - - if(debug['R'] && debug['v']) - dumpit("pass2", firstr); - - /* - * pass 2.5 * find looping structure */ - for(r = firstr; r != R; r = r->link) - r->active = 0; - change = 0; - loopit(firstr, nr); + flowrpo(g); if(debug['R'] && debug['v']) - dumpit("pass2.5", firstr); + dumpit("pass2", &firstr->f, 1); /* * pass 3 @@ -484,17 +277,17 @@ regopt(Prog *firstp) */ loop1: change = 0; - for(r = firstr; r != R; r = r->link) - r->active = 0; - for(r = firstr; r != R; r = r->link) - if(r->prog->as == ARET) + for(r = firstr; r != R; r = (Reg*)r->f.link) + r->f.active = 0; + for(r = firstr; r != R; r = (Reg*)r->f.link) + if(r->f.prog->as == ARET) prop(r, zbits, zbits); loop11: /* pick up unreachable code */ i = 0; for(r = firstr; r != R; r = r1) { - r1 = r->link; - if(r1 && r1->active && !r->active) { + r1 = (Reg*)r->f.link; + if(r1 && r1->f.active && !r->f.active) { prop(r, zbits, zbits); i = 1; } @@ -505,7 +298,7 @@ loop11: goto loop1; if(debug['R'] && debug['v']) - dumpit("pass3", firstr); + dumpit("pass3", &firstr->f, 1); /* @@ -515,8 +308,8 @@ loop11: */ loop2: change = 0; - for(r = firstr; r != R; r = r->link) - r->active = 0; + for(r = firstr; r != R; r = (Reg*)r->f.link) + r->f.active = 0; synch(firstr, zbits); if(change) goto loop2; @@ -524,12 +317,12 @@ loop2: addsplits(); if(debug['R'] && debug['v']) - dumpit("pass4", firstr); + dumpit("pass4", &firstr->f, 1); if(debug['R'] > 1) { print("\nprop structure:\n"); - for(r = firstr; r != R; r = r->link) { - print("%d:%P", r->loop, r->prog); + for(r = firstr; r != R; r = (Reg*)r->f.link) { + print("%d:%P", r->f.loop, r->f.prog); for(z=0; z<BITS; z++) { bit.b[z] = r->set.b[z] | r->refahead.b[z] | r->calahead.b[z] | @@ -563,7 +356,7 @@ loop2: * pass 4.5 * move register pseudo-variables into regu. */ - for(r = firstr; r != R; r = r->link) { + for(r = firstr; r != R; r = (Reg*)r->f.link) { r->regu = (r->refbehind.b[0] | r->set.b[0]) & REGBITS; r->set.b[0] &= ~REGBITS; @@ -578,7 +371,7 @@ loop2: } if(debug['R'] && debug['v']) - dumpit("pass4.5", firstr); + dumpit("pass4.5", &firstr->f, 1); /* * pass 5 @@ -590,27 +383,27 @@ loop2: for(z=0; z<BITS; z++) bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) & ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]); - if(bany(&bit) & !r->refset) { + if(bany(&bit) & !r->f.refset) { // should never happen - all variables are preset if(debug['w']) - print("%L: used and not set: %Q\n", r->prog->lineno, bit); - r->refset = 1; + print("%L: used and not set: %Q\n", r->f.prog->lineno, bit); + r->f.refset = 1; } } - for(r = firstr; r != R; r = r->link) + for(r = firstr; r != R; r = (Reg*)r->f.link) r->act = zbits; rgp = region; nregion = 0; - for(r = firstr; r != R; r = r->link) { + for(r = firstr; r != R; r = (Reg*)r->f.link) { for(z=0; z<BITS; z++) bit.b[z] = r->set.b[z] & ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]); - if(bany(&bit) && !r->refset) { + if(bany(&bit) && !r->f.refset) { if(debug['w']) - print("%L: set and not used: %Q\n", r->prog->lineno, bit); - r->refset = 1; - excise(r); + print("%L: set and not used: %Q\n", r->f.prog->lineno, bit); + r->f.refset = 1; + excise(&r->f); } for(z=0; z<BITS; z++) bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]); @@ -626,7 +419,7 @@ loop2: if(change <= 0) { if(debug['R']) print("%L $%d: %Q\n", - r->prog->lineno, change, blsh(i)); + r->f.prog->lineno, change, blsh(i)); continue; } rgp->cost = change; @@ -643,7 +436,7 @@ brk: qsort(region, nregion, sizeof(region[0]), rcmp); if(debug['R'] && debug['v']) - dumpit("pass5", firstr); + dumpit("pass5", &firstr->f, 1); /* * pass 6 @@ -658,13 +451,13 @@ brk: if(debug['R']) { if(rgp->regno >= NREG) print("%L $%d F%d: %Q\n", - rgp->enter->prog->lineno, + rgp->enter->f.prog->lineno, rgp->cost, rgp->regno-NREG, bit); else print("%L $%d R%d: %Q\n", - rgp->enter->prog->lineno, + rgp->enter->f.prog->lineno, rgp->cost, rgp->regno, bit); @@ -675,18 +468,18 @@ brk: } if(debug['R'] && debug['v']) - dumpit("pass6", firstr); + dumpit("pass6", &firstr->f, 1); /* * pass 7 * peep-hole on basic block */ if(!debug['R'] || debug['P']) { - peep(); + peep(firstp); } if(debug['R'] && debug['v']) - dumpit("pass7", firstr); + dumpit("pass7", &firstr->f, 1); /* * last pass @@ -742,11 +535,8 @@ brk: } } } - if(lastr != R) { - lastr->link = freer; - freer = firstr; - } + flowend(g); } void @@ -756,13 +546,13 @@ addsplits(void) int z, i; Bits bit; - for(r = firstr; r != R; r = r->link) { - if(r->loop > 1) + for(r = firstr; r != R; r = (Reg*)r->f.link) { + if(r->f.loop > 1) continue; - if(r->prog->as == ABL) + if(r->f.prog->as == ABL) continue; - for(r1 = r->p2; r1 != R; r1 = r1->p2link) { - if(r1->loop <= 1) + for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) { + if(r1->f.loop <= 1) continue; for(z=0; z<BITS; z++) bit.b[z] = r1->calbehind.b[z] & @@ -789,7 +579,7 @@ addmove(Reg *r, int bn, int rn, int f) p1 = mal(sizeof(*p1)); *p1 = zprog; - p = r->prog; + p = r->f.prog; // If there's a stack fixup coming (after BL newproc or BL deferproc), // delay the load until after the fixup. @@ -814,14 +604,14 @@ addmove(Reg *r, int bn, int rn, int f) a->type = D_CONST; if(v->addr) - fatal("addmove: shouldnt be doing this %A\n", a); + fatal("addmove: shouldn't be doing this %A\n", a); switch(v->etype) { default: print("What is this %E\n", v->etype); case TINT8: - p1->as = AMOVB; + p1->as = AMOVBS; break; case TBOOL: case TUINT8: @@ -829,7 +619,7 @@ addmove(Reg *r, int bn, int rn, int f) p1->as = AMOVBU; break; case TINT16: - p1->as = AMOVH; + p1->as = AMOVHS; break; case TUINT16: p1->as = AMOVHU; @@ -908,9 +698,6 @@ mkvar(Reg *r, Adr *a) case D_BRANCH: break; - case D_CONST: - flag = 1; - goto onereg; case D_REGREG: case D_REGREG2: @@ -921,9 +708,9 @@ mkvar(Reg *r, Adr *a) bit.b[0] |= RtoB(a->reg); return bit; + case D_CONST: case D_REG: case D_SHIFT: - onereg: if(a->reg != NREG) { bit = zbits; bit.b[0] = RtoB(a->reg); @@ -933,11 +720,11 @@ mkvar(Reg *r, Adr *a) case D_OREG: if(a->reg != NREG) { - if(a == &r->prog->from) + if(a == &r->f.prog->from) r->use1.b[0] |= RtoB(a->reg); else r->use2.b[0] |= RtoB(a->reg); - if(r->prog->scond & (C_PBIT|C_WBIT)) + if(r->f.prog->scond & (C_PBIT|C_WBIT)) r->set.b[0] |= RtoB(a->reg); } break; @@ -1040,7 +827,7 @@ prop(Reg *r, Bits ref, Bits cal) Reg *r1, *r2; int z; - for(r1 = r; r1 != R; r1 = r1->p1) { + for(r1 = r; r1 != R; r1 = (Reg*)r1->f.p1) { for(z=0; z<BITS; z++) { ref.b[z] |= r1->refahead.b[z]; if(ref.b[z] != r1->refahead.b[z]) { @@ -1053,9 +840,9 @@ prop(Reg *r, Bits ref, Bits cal) change++; } } - switch(r1->prog->as) { + switch(r1->f.prog->as) { case ABL: - if(noreturn(r1->prog)) + if(noreturn(r1->f.prog)) break; for(z=0; z<BITS; z++) { cal.b[z] |= ref.b[z] | externs.b[z]; @@ -1095,158 +882,22 @@ prop(Reg *r, Bits ref, Bits cal) r1->refbehind.b[z] = ref.b[z]; r1->calbehind.b[z] = cal.b[z]; } - if(r1->active) + if(r1->f.active) break; - r1->active = 1; + r1->f.active = 1; } - for(; r != r1; r = r->p1) - for(r2 = r->p2; r2 != R; r2 = r2->p2link) + for(; r != r1; r = (Reg*)r->f.p1) + for(r2 = (Reg*)r->f.p2; r2 != R; r2 = (Reg*)r2->f.p2link) prop(r2, r->refbehind, r->calbehind); } -/* - * find looping structure - * - * 1) find reverse postordering - * 2) find approximate dominators, - * the actual dominators if the flow graph is reducible - * otherwise, dominators plus some other non-dominators. - * See Matthew S. Hecht and Jeffrey D. Ullman, - * "Analysis of a Simple Algorithm for Global Data Flow Problems", - * Conf. Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts, - * Oct. 1-3, 1973, pp. 207-217. - * 3) find all nodes with a predecessor dominated by the current node. - * such a node is a loop head. - * recursively, all preds with a greater rpo number are in the loop - */ -int32 -postorder(Reg *r, Reg **rpo2r, int32 n) -{ - Reg *r1; - - r->rpo = 1; - r1 = r->s1; - if(r1 && !r1->rpo) - n = postorder(r1, rpo2r, n); - r1 = r->s2; - if(r1 && !r1->rpo) - n = postorder(r1, rpo2r, n); - rpo2r[n] = r; - n++; - return n; -} - -int32 -rpolca(int32 *idom, int32 rpo1, int32 rpo2) -{ - int32 t; - - if(rpo1 == -1) - return rpo2; - while(rpo1 != rpo2){ - if(rpo1 > rpo2){ - t = rpo2; - rpo2 = rpo1; - rpo1 = t; - } - while(rpo1 < rpo2){ - t = idom[rpo2]; - if(t >= rpo2) - fatal("bad idom"); - rpo2 = t; - } - } - return rpo1; -} - -int -doms(int32 *idom, int32 r, int32 s) -{ - while(s > r) - s = idom[s]; - return s == r; -} - -int -loophead(int32 *idom, Reg *r) -{ - int32 src; - - src = r->rpo; - if(r->p1 != R && doms(idom, src, r->p1->rpo)) - return 1; - for(r = r->p2; r != R; r = r->p2link) - if(doms(idom, src, r->rpo)) - return 1; - return 0; -} - -void -loopmark(Reg **rpo2r, int32 head, Reg *r) -{ - if(r->rpo < head || r->active == head) - return; - r->active = head; - r->loop += LOOP; - if(r->p1 != R) - loopmark(rpo2r, head, r->p1); - for(r = r->p2; r != R; r = r->p2link) - loopmark(rpo2r, head, r); -} - -void -loopit(Reg *r, int32 nr) -{ - Reg *r1; - int32 i, d, me; - - if(nr > maxnr) { - rpo2r = mal(nr * sizeof(Reg*)); - idom = mal(nr * sizeof(int32)); - maxnr = nr; - } - d = postorder(r, rpo2r, 0); - if(d > nr) - fatal("too many reg nodes"); - nr = d; - for(i = 0; i < nr / 2; i++){ - r1 = rpo2r[i]; - rpo2r[i] = rpo2r[nr - 1 - i]; - rpo2r[nr - 1 - i] = r1; - } - for(i = 0; i < nr; i++) - rpo2r[i]->rpo = i; - - idom[0] = 0; - for(i = 0; i < nr; i++){ - r1 = rpo2r[i]; - me = r1->rpo; - d = -1; - // rpo2r[r->rpo] == r protects against considering dead code, - // which has r->rpo == 0. - if(r1->p1 != R && rpo2r[r1->p1->rpo] == r1->p1 && r1->p1->rpo < me) - d = r1->p1->rpo; - for(r1 = r1->p2; r1 != nil; r1 = r1->p2link) - if(rpo2r[r1->rpo] == r1 && r1->rpo < me) - d = rpolca(idom, d, r1->rpo); - idom[i] = d; - } - - for(i = 0; i < nr; i++){ - r1 = rpo2r[i]; - r1->loop++; - if(r1->p2 != R && loophead(idom, r1)) - loopmark(rpo2r, i, r1); - } -} - void synch(Reg *r, Bits dif) { Reg *r1; int z; - for(r1 = r; r1 != R; r1 = r1->s1) { + for(r1 = r; r1 != R; r1 = (Reg*)r1->f.s1) { for(z=0; z<BITS; z++) { dif.b[z] = (dif.b[z] & ~(~r1->refbehind.b[z] & r1->refahead.b[z])) | @@ -1256,13 +907,13 @@ synch(Reg *r, Bits dif) change++; } } - if(r1->active) + if(r1->f.active) break; - r1->active = 1; + r1->f.active = 1; for(z=0; z<BITS; z++) dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]); - if(r1->s2 != R) - synch(r1->s2, dif); + if(r1->f.s2 != nil) + synch((Reg*)r1->f.s2, dif); } } @@ -1333,7 +984,7 @@ paint1(Reg *r, int bn) for(;;) { if(!(r->refbehind.b[z] & bb)) break; - r1 = r->p1; + r1 = (Reg*)r->f.p1; if(r1 == R) break; if(!(r1->refahead.b[z] & bb)) @@ -1344,48 +995,48 @@ paint1(Reg *r, int bn) } if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) { - change -= CLOAD * r->loop; + change -= CLOAD * r->f.loop; if(debug['R'] > 1) - print("%d%P\td %Q $%d\n", r->loop, - r->prog, blsh(bn), change); + print("%d%P\td %Q $%d\n", r->f.loop, + r->f.prog, blsh(bn), change); } for(;;) { r->act.b[z] |= bb; - p = r->prog; + p = r->f.prog; if(r->use1.b[z] & bb) { - change += CREF * r->loop; + change += CREF * r->f.loop; if(debug['R'] > 1) - print("%d%P\tu1 %Q $%d\n", r->loop, + print("%d%P\tu1 %Q $%d\n", r->f.loop, p, blsh(bn), change); } if((r->use2.b[z]|r->set.b[z]) & bb) { - change += CREF * r->loop; + change += CREF * r->f.loop; if(debug['R'] > 1) - print("%d%P\tu2 %Q $%d\n", r->loop, + print("%d%P\tu2 %Q $%d\n", r->f.loop, p, blsh(bn), change); } if(STORE(r) & r->regdiff.b[z] & bb) { - change -= CLOAD * r->loop; + change -= CLOAD * r->f.loop; if(debug['R'] > 1) - print("%d%P\tst %Q $%d\n", r->loop, + print("%d%P\tst %Q $%d\n", r->f.loop, p, blsh(bn), change); } if(r->refbehind.b[z] & bb) - for(r1 = r->p2; r1 != R; r1 = r1->p2link) + for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) if(r1->refahead.b[z] & bb) paint1(r1, bn); if(!(r->refahead.b[z] & bb)) break; - r1 = r->s2; + r1 = (Reg*)r->f.s2; if(r1 != R) if(r1->refbehind.b[z] & bb) paint1(r1, bn); - r = r->s1; + r = (Reg*)r->f.s1; if(r == R) break; if(r->act.b[z] & bb) @@ -1410,7 +1061,7 @@ paint2(Reg *r, int bn) for(;;) { if(!(r->refbehind.b[z] & bb)) break; - r1 = r->p1; + r1 = (Reg*)r->f.p1; if(r1 == R) break; if(!(r1->refahead.b[z] & bb)) @@ -1425,17 +1076,17 @@ paint2(Reg *r, int bn) vreg |= r->regu; if(r->refbehind.b[z] & bb) - for(r1 = r->p2; r1 != R; r1 = r1->p2link) + for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) if(r1->refahead.b[z] & bb) vreg |= paint2(r1, bn); if(!(r->refahead.b[z] & bb)) break; - r1 = r->s2; + r1 = (Reg*)r->f.s2; if(r1 != R) if(r1->refbehind.b[z] & bb) vreg |= paint2(r1, bn); - r = r->s1; + r = (Reg*)r->f.s1; if(r == R) break; if(!(r->act.b[z] & bb)) @@ -1461,7 +1112,7 @@ paint3(Reg *r, int bn, int32 rb, int rn) for(;;) { if(!(r->refbehind.b[z] & bb)) break; - r1 = r->p1; + r1 = (Reg*)r->f.p1; if(r1 == R) break; if(!(r1->refahead.b[z] & bb)) @@ -1476,7 +1127,7 @@ paint3(Reg *r, int bn, int32 rb, int rn) for(;;) { r->act.b[z] |= bb; - p = r->prog; + p = r->f.prog; if(r->use1.b[z] & bb) { if(debug['R']) @@ -1498,17 +1149,17 @@ paint3(Reg *r, int bn, int32 rb, int rn) r->regu |= rb; if(r->refbehind.b[z] & bb) - for(r1 = r->p2; r1 != R; r1 = r1->p2link) + for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) if(r1->refahead.b[z] & bb) paint3(r1, bn, rb, rn); if(!(r->refahead.b[z] & bb)) break; - r1 = r->s2; + r1 = (Reg*)r->f.s2; if(r1 != R) if(r1->refbehind.b[z] & bb) paint3(r1, bn, rb, rn); - r = r->s1; + r = (Reg*)r->f.s1; if(r == R) break; if(r->act.b[z] & bb) @@ -1582,91 +1233,74 @@ BtoF(int32 b) return bitno(b) - 16; } -static Sym* symlist[10]; - -int -noreturn(Prog *p) -{ - Sym *s; - int i; - - if(symlist[0] == S) { - symlist[0] = pkglookup("panicindex", runtimepkg); - symlist[1] = pkglookup("panicslice", runtimepkg); - symlist[2] = pkglookup("throwinit", runtimepkg); - symlist[3] = pkglookup("panic", runtimepkg); - symlist[4] = pkglookup("panicwrap", runtimepkg); - } - - s = p->to.sym; - if(s == S) - return 0; - for(i=0; symlist[i]!=S; i++) - if(s == symlist[i]) - return 1; - return 0; -} - void -dumpone(Reg *r) +dumpone(Flow *f, int isreg) { int z; Bits bit; + Reg *r; - print("%d:%P", r->loop, r->prog); - for(z=0; z<BITS; z++) - bit.b[z] = - r->set.b[z] | - r->use1.b[z] | - r->use2.b[z] | - r->refbehind.b[z] | - r->refahead.b[z] | - r->calbehind.b[z] | - r->calahead.b[z] | - r->regdiff.b[z] | - r->act.b[z] | - 0; - if(bany(&bit)) { - print("\t"); - if(bany(&r->set)) - print(" s:%Q", r->set); - if(bany(&r->use1)) - print(" u1:%Q", r->use1); - if(bany(&r->use2)) - print(" u2:%Q", r->use2); - if(bany(&r->refbehind)) - print(" rb:%Q ", r->refbehind); - if(bany(&r->refahead)) - print(" ra:%Q ", r->refahead); - if(bany(&r->calbehind)) - print(" cb:%Q ", r->calbehind); - if(bany(&r->calahead)) - print(" ca:%Q ", r->calahead); - if(bany(&r->regdiff)) - print(" d:%Q ", r->regdiff); - if(bany(&r->act)) - print(" a:%Q ", r->act); + print("%d:%P", f->loop, f->prog); + if(isreg) { + r = (Reg*)f; + for(z=0; z<BITS; z++) + bit.b[z] = + r->set.b[z] | + r->use1.b[z] | + r->use2.b[z] | + r->refbehind.b[z] | + r->refahead.b[z] | + r->calbehind.b[z] | + r->calahead.b[z] | + r->regdiff.b[z] | + r->act.b[z] | + 0; + if(bany(&bit)) { + print("\t"); + if(bany(&r->set)) + print(" s:%Q", r->set); + if(bany(&r->use1)) + print(" u1:%Q", r->use1); + if(bany(&r->use2)) + print(" u2:%Q", r->use2); + if(bany(&r->refbehind)) + print(" rb:%Q ", r->refbehind); + if(bany(&r->refahead)) + print(" ra:%Q ", r->refahead); + if(bany(&r->calbehind)) + print(" cb:%Q ", r->calbehind); + if(bany(&r->calahead)) + print(" ca:%Q ", r->calahead); + if(bany(&r->regdiff)) + print(" d:%Q ", r->regdiff); + if(bany(&r->act)) + print(" a:%Q ", r->act); + } } print("\n"); } void -dumpit(char *str, Reg *r0) +dumpit(char *str, Flow *r0, int isreg) { - Reg *r, *r1; + Flow *r, *r1; print("\n%s\n", str); - for(r = r0; r != R; r = r->link) { - dumpone(r); + for(r = r0; r != nil; r = r->link) { + dumpone(r, isreg); r1 = r->p2; - if(r1 != R) { + if(r1 != nil) { print(" pred:"); - for(; r1 != R; r1 = r1->p2link) + for(; r1 != nil; r1 = r1->p2link) print(" %.4ud", r1->prog->loc); + if(r->p1 != nil) + print(" (and %.4ud)", r->p1->prog->loc); + else + print(" (only)"); print("\n"); } // r1 = r->s1; -// if(r1 != R) { +// if(r1 != nil) { // print(" succ:"); // for(; r1 != R; r1 = r1->s1) // print(" %.4ud", r1->prog->loc); @@ -1674,123 +1308,3 @@ dumpit(char *str, Reg *r0) // } } } - -/* - * the code generator depends on being able to write out JMP (B) - * instructions that it can jump to now but fill in later. - * the linker will resolve them nicely, but they make the code - * longer and more difficult to follow during debugging. - * remove them. - */ - -/* what instruction does a JMP to p eventually land on? */ -static Prog* -chasejmp(Prog *p, int *jmploop) -{ - int n; - - n = 0; - while(p != P && p->as == AB && p->to.type == D_BRANCH) { - if(++n > 10) { - *jmploop = 1; - break; - } - p = p->to.u.branch; - } - return p; -} - -/* - * reuse reg pointer for mark/sweep state. - * leave reg==nil at end because alive==nil. - */ -#define alive ((void*)0) -#define dead ((void*)1) - -/* mark all code reachable from firstp as alive */ -static void -mark(Prog *firstp) -{ - Prog *p; - - for(p=firstp; p; p=p->link) { - if(p->regp != dead) - break; - p->regp = alive; - if(p->as != ABL && p->to.type == D_BRANCH && p->to.u.branch) - mark(p->to.u.branch); - if(p->as == AB || p->as == ARET || (p->as == ABL && noreturn(p))) - break; - } -} - -static void -fixjmp(Prog *firstp) -{ - int jmploop; - Prog *p, *last; - - if(debug['R'] && debug['v']) - print("\nfixjmp\n"); - - // pass 1: resolve jump to B, mark all code as dead. - jmploop = 0; - for(p=firstp; p; p=p->link) { - if(debug['R'] && debug['v']) - print("%P\n", p); - if(p->as != ABL && p->to.type == D_BRANCH && p->to.u.branch && p->to.u.branch->as == AB) { - p->to.u.branch = chasejmp(p->to.u.branch, &jmploop); - if(debug['R'] && debug['v']) - print("->%P\n", p); - } - p->regp = dead; - } - if(debug['R'] && debug['v']) - print("\n"); - - // pass 2: mark all reachable code alive - mark(firstp); - - // pass 3: delete dead code (mostly JMPs). - last = nil; - for(p=firstp; p; p=p->link) { - if(p->regp == dead) { - if(p->link == P && p->as == ARET && last && last->as != ARET) { - // This is the final ARET, and the code so far doesn't have one. - // Let it stay. - } else { - if(debug['R'] && debug['v']) - print("del %P\n", p); - continue; - } - } - if(last) - last->link = p; - last = p; - } - last->link = P; - - // pass 4: elide JMP to next instruction. - // only safe if there are no jumps to JMPs anymore. - if(!jmploop) { - last = nil; - for(p=firstp; p; p=p->link) { - if(p->as == AB && p->to.type == D_BRANCH && p->to.u.branch == p->link) { - if(debug['R'] && debug['v']) - print("del %P\n", p); - continue; - } - if(last) - last->link = p; - last = p; - } - last->link = P; - } - - if(debug['R'] && debug['v']) { - print("\n"); - for(p=firstp; p; p=p->link) - print("%P\n", p); - print("\n"); - } -} diff --git a/src/cmd/5l/5.out.h b/src/cmd/5l/5.out.h index 97a2421b3..e8cf83ddd 100644 --- a/src/cmd/5l/5.out.h +++ b/src/cmd/5l/5.out.h @@ -31,12 +31,7 @@ #define NSNAME 8 #define NSYM 50 #define NREG 16 - -#define NOPROF (1<<0) -#define DUPOK (1<<1) -#define NOSPLIT (1<<2) -#define RODATA (1<<3) -#define NOPTR (1<<4) +#include "../ld/textflag.h" #define REGRET 0 /* -1 disables use of REGARG */ @@ -140,8 +135,10 @@ enum as AMODU, AMOVB, + AMOVBS, AMOVBU, AMOVH, + AMOVHS, AMOVHU, AMOVW, AMOVM, @@ -197,8 +194,10 @@ enum as AMULAWB, AUSEFIELD, - ALOCALS, ATYPE, + AFUNCDATA, + APCDATA, + ACHECKNIL, ALAST, }; @@ -275,7 +274,7 @@ enum as #define D_PLT1 (D_NONE+44) // R_ARM_PLT32, 2nd inst: add ip, ip, #0xNN000 #define D_PLT2 (D_NONE+45) // R_ARM_PLT32, 3rd inst: ldr pc, [ip, #0xNNN]! #define D_CALL (D_NONE+46) // R_ARM_PLT32/R_ARM_CALL/R_ARM_JUMP24, bl xxxxx or b yyyyy -#define D_TLS (D_NONE+47) +#define D_TLS (D_NONE+47) // R_ARM_TLS_LE32 /* * this is the ranlib header diff --git a/src/cmd/5l/asm.c b/src/cmd/5l/asm.c index a1220a38e..33cdf8096 100644 --- a/src/cmd/5l/asm.c +++ b/src/cmd/5l/asm.c @@ -41,6 +41,7 @@ char linuxdynld[] = "/lib/ld-linux.so.3"; // 2 for OABI, 3 for EABI char freebsddynld[] = "/usr/libexec/ld-elf.so.1"; char openbsddynld[] = "XXX"; char netbsddynld[] = "/libexec/ld.elf_so"; +char dragonflydynld[] = "XXX"; int32 entryvalue(void) @@ -93,12 +94,6 @@ braddoff(int32 a, int32 b) return (((uint32)a) & 0xff000000U) | (0x00ffffffU & (uint32)(a + b)); } -Sym * -lookuprel(void) -{ - return lookup(".rel", 0); -} - void adddynrela(Sym *rel, Sym *s, Reloc *r) { @@ -264,6 +259,26 @@ elfreloc1(Reloc *r, vlong sectoff) else return -1; break; + + case D_CALL: + if(r->siz == 4) { + if((r->add & 0xff000000) == 0xeb000000) // BL + LPUT(R_ARM_CALL | elfsym<<8); + else + LPUT(R_ARM_JUMP24 | elfsym<<8); + } else + return -1; + break; + + case D_TLS: + if(r->siz == 4) { + if(flag_shared) + LPUT(R_ARM_TLS_IE32 | elfsym<<8); + else + LPUT(R_ARM_TLS_LE32 | elfsym<<8); + } else + return -1; + break; } return 0; @@ -308,6 +323,34 @@ machoreloc1(Reloc *r, vlong sectoff) int archreloc(Reloc *r, Sym *s, vlong *val) { + Sym *rs; + + if(linkmode == LinkExternal) { + switch(r->type) { + case D_CALL: + r->done = 0; + + // set up addend for eventual relocation via outer symbol. + rs = r->sym; + r->xadd = r->add; + if(r->xadd & 0x800000) + r->xadd |= ~0xffffff; + r->xadd *= 4; + while(rs->outer != nil) { + r->xadd += symaddr(rs) - symaddr(rs->outer); + rs = rs->outer; + } + + if(rs->type != SHOSTOBJ && rs->sect == nil) + diag("missing section for %s", rs->name); + r->xsym = rs; + + *val = braddoff((0xff000000U & (uint32)r->add), + (0xffffff & (uint32)(r->xadd / 4))); + return 0; + } + return -1; + } switch(r->type) { case D_CONST: *val = r->add; @@ -550,13 +593,20 @@ asmb(void) sect = segtext.sect; cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); codeblk(sect->vaddr, sect->len); - - /* output read-only data in text segment (rodata, gosymtab, pclntab, ...) */ for(sect = sect->next; sect != nil; sect = sect->next) { cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); datblk(sect->vaddr, sect->len); } + if(segrodata.filelen > 0) { + if(debug['v']) + Bprint(&bso, "%5.2f rodatblk\n", cputime()); + Bflush(&bso); + + cseek(segrodata.fileoff); + datblk(segrodata.vaddr, segrodata.filelen); + } + if(debug['v']) Bprint(&bso, "%5.2f datblk\n", cputime()); Bflush(&bso); @@ -587,7 +637,7 @@ asmb(void) symo = HEADR+segtext.len+segdata.filelen; break; ElfSym: - symo = rnd(HEADR+segtext.filelen, INITRND)+segdata.filelen; + symo = rnd(HEADR+segtext.filelen, INITRND)+rnd(HEADR+segrodata.filelen, INITRND)+segdata.filelen; symo = rnd(symo, INITRND); break; } @@ -759,7 +809,7 @@ nopstat(char *f, Count *c) } void -asmout(Prog *p, Optab *o, int32 *out) +asmout(Prog *p, Optab *o, int32 *out, Sym *gmsym) { int32 o1, o2, o3, o4, o5, o6, v; int r, rf, rt, rt2; @@ -791,7 +841,7 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- r = p->reg; if(p->to.type == D_NONE) rt = 0; - if(p->as == AMOVW || p->as == AMVN) + if(p->as == AMOVB || p->as == AMOVH || p->as == AMOVW || p->as == AMVN) r = 0; else if(r == NREG) @@ -842,11 +892,19 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- break; case 5: /* bra s */ + o1 = opbra(p->as, p->scond); v = -8; - // TODO: Use addrel. + if(p->to.sym != S && p->to.sym->type != 0) { + rel = addrel(cursym); + rel->off = pc - cursym->value; + rel->siz = 4; + rel->sym = p->to.sym; + rel->add = o1 | ((v >> 2) & 0xffffff); + rel->type = D_CALL; + break; + } if(p->cond != P) v = (p->cond->pc - pc) - 8; - o1 = opbra(p->as, p->scond); o1 |= (v >> 2) & 0xffffff; break; @@ -858,17 +916,12 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- o1 |= REGPC << 12; break; - case 7: /* bl ,O(R) -> mov PC,link; add $O,R,PC */ + case 7: /* bl (R) -> blx R */ aclass(&p->to); - o1 = oprrr(AADD, p->scond); - o1 |= immrot(0); - o1 |= REGPC << 16; - o1 |= REGLINK << 12; - - o2 = oprrr(AADD, p->scond); - o2 |= immrot(instoffset); - o2 |= p->to.reg << 16; - o2 |= REGPC << 12; + if(instoffset != 0) + diag("%P: doesn't support BL offset(REG) where offset != 0", p); + o1 = oprrr(ABL, p->scond); + o1 |= p->to.reg; break; case 8: /* sll $c,[R],R -> mov (R<<$c),R */ @@ -909,7 +962,13 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- rel->siz = 4; rel->sym = p->to.sym; rel->add = p->to.offset; - if(flag_shared) { + if(rel->sym == gmsym) { + rel->type = D_TLS; + if(flag_shared) + rel->add += pc - p->pcrel->pc - 8 - rel->siz; + rel->xadd = rel->add; + rel->xsym = rel->sym; + } else if(flag_shared) { rel->type = D_PCREL; rel->add += pc - p->pcrel->pc - 8; } else @@ -952,7 +1011,7 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- r = p->to.reg; o1 |= (p->from.reg)|(r<<12); o2 |= (r)|(r<<12); - if(p->as == AMOVB || p->as == AMOVBU) { + if(p->as == AMOVB || p->as == AMOVBS || p->as == AMOVBU) { o1 |= (24<<7); o2 |= (24<<7); } else { @@ -1033,7 +1092,7 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- if(r == NREG) r = o->param; o2 = olrr(REGTMP,r, p->to.reg, p->scond); - if(p->as == AMOVBU || p->as == AMOVB) + if(p->as == AMOVBU || p->as == AMOVBS || p->as == AMOVB) o2 |= 1<<22; break; @@ -1222,7 +1281,7 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- if(p->to.reg == NREG) diag("MOV to shifter operand"); o1 = osrr(p->from.reg, p->to.offset, p->to.reg, p->scond); - if(p->as == AMOVB || p->as == AMOVBU) + if(p->as == AMOVB || p->as == AMOVBS || p->as == AMOVBU) o1 |= 1<<22; break; @@ -1240,9 +1299,22 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- case 63: /* bcase */ if(p->cond != P) { - o1 = p->cond->pc; - if(flag_shared) - o1 = o1 - p->pcrel->pc - 16; + rel = addrel(cursym); + rel->off = pc - cursym->value; + rel->siz = 4; + if(p->to.sym != S && p->to.sym->type != 0) { + rel->sym = p->to.sym; + rel->add = p->to.offset; + } else { + rel->sym = cursym; + rel->add = p->cond->pc - cursym->value; + } + if(o->flag & LPCREL) { + rel->type = D_PCREL; + rel->add += pc - p->pcrel->pc - 16 + rel->siz; + } else + rel->type = D_ADDR; + o1 = 0; } break; @@ -1263,7 +1335,7 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- if(!o1) break; o2 = olr(0, REGTMP, p->to.reg, p->scond); - if(p->as == AMOVBU || p->as == AMOVB) + if(p->as == AMOVBU || p->as == AMOVBS || p->as == AMOVB) o2 |= 1<<22; if(o->flag & LPCREL) { o3 = o2; @@ -1307,9 +1379,9 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- if(r == NREG) r = o->param; o1 = olhr(instoffset, r, p->to.reg, p->scond); - if(p->as == AMOVB) + if(p->as == AMOVB || p->as == AMOVBS) o1 ^= (1<<5)|(1<<6); - else if(p->as == AMOVH) + else if(p->as == AMOVH || p->as == AMOVHS) o1 ^= (1<<6); break; case 72: /* movh/movhu R,L(R) -> strh */ @@ -1329,9 +1401,9 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- if(r == NREG) r = o->param; o2 = olhrr(REGTMP, r, p->to.reg, p->scond); - if(p->as == AMOVB) + if(p->as == AMOVB || p->as == AMOVBS) o2 ^= (1<<5)|(1<<6); - else if(p->as == AMOVH) + else if(p->as == AMOVH || p->as == AMOVHS) o2 ^= (1<<6); break; case 74: /* bx $I */ @@ -1483,9 +1555,9 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- if(!o1) break; o2 = olhr(0, REGTMP, p->to.reg, p->scond); - if(p->as == AMOVB) + if(p->as == AMOVB || p->as == AMOVBS) o2 ^= (1<<5)|(1<<6); - else if(p->as == AMOVH) + else if(p->as == AMOVH || p->as == AMOVHS) o2 ^= (1<<6); if(o->flag & LPCREL) { o3 = o2; @@ -1515,11 +1587,9 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- // This is supposed to be something that stops execution. // It's not supposed to be reached, ever, but if it is, we'd // like to be able to tell how we got there. Assemble as - // BL $0 - // TODO: Use addrel. - v = (0 - pc) - 8; - o1 = opbra(ABL, C_SCOND_NONE); - o1 |= (v >> 2) & 0xffffff; + // 0xf7fabcfd which is guranteed to raise undefined instruction + // exception. + o1 = 0xf7fabcfd; break; case 97: /* CLZ Rm, Rd */ o1 = oprrr(p->as, p->scond); @@ -1639,6 +1709,8 @@ oprrr(int a, int sc) case ACMP: return o | (0xa<<21) | (1<<20); case ACMN: return o | (0xb<<21) | (1<<20); case AORR: return o | (0xc<<21); + case AMOVB: + case AMOVH: case AMOVW: return o | (0xd<<21); case ABIC: return o | (0xe<<21); case AMVN: return o | (0xf<<21); @@ -1711,6 +1783,9 @@ oprrr(int a, int sc) return (o & (0xf<<28)) | (0x12 << 20) | (0xc<<4); case AMULAWB: return (o & (0xf<<28)) | (0x12 << 20) | (0x8<<4); + + case ABL: // BLX REG + return (o & (0xf<<28)) | (0x12fff3 << 4); } diag("bad rrr %d", a); prasm(curp); diff --git a/src/cmd/5l/l.h b/src/cmd/5l/l.h index e7794c723..ae4b05ba1 100644 --- a/src/cmd/5l/l.h +++ b/src/cmd/5l/l.h @@ -170,6 +170,7 @@ struct Sym char* dynimplib; char* dynimpvers; struct Section* sect; + struct Hist* hist; // STEXT Auto* autom; @@ -182,7 +183,6 @@ struct Sym Reloc* r; int32 nr; int32 maxr; - int rel_ro; }; #define SIGNINTERN (1729*325*1729) @@ -280,7 +280,7 @@ enum MINSIZ = 64, NENT = 100, MAXIO = 8192, - MAXHIST = 20, /* limit of path elements for history symbols */ + MAXHIST = 40, /* limit of path elements for history symbols */ MINLC = 4, }; @@ -292,7 +292,6 @@ EXTERN int32 INITDAT; /* data location */ EXTERN int32 INITRND; /* data round above text location */ EXTERN int32 INITTEXT; /* text location */ EXTERN char* INITENTRY; /* entry point */ -EXTERN char* LIBINITENTRY; /* shared library entry point */ EXTERN int32 autosize; EXTERN Auto* curauto; EXTERN Auto* curhist; @@ -317,7 +316,6 @@ EXTERN char* rpath; EXTERN uint32 stroffset; EXTERN int32 symsize; EXTERN Sym* textp; -EXTERN int version; EXTERN char xcmp[C_GOK+1][C_GOK+1]; EXTERN Prog zprg; EXTERN int dtype; @@ -364,7 +362,7 @@ int aclass(Adr*); void addhist(int32, int); Prog* appendp(Prog*); void asmb(void); -void asmout(Prog*, Optab*, int32*); +void asmout(Prog*, Optab*, int32*, Sym*); int32 atolwhex(char*); Prog* brloop(Prog*); void buildop(void); @@ -434,7 +432,6 @@ int32 immaddr(int32); int32 opbra(int, int); int brextra(Prog*); int isbranch(Prog*); -void fnptrs(void); void doelf(void); void dozerostk(void); // used by -Z diff --git a/src/cmd/5l/list.c b/src/cmd/5l/list.c index a051774b4..7502a3b81 100644 --- a/src/cmd/5l/list.c +++ b/src/cmd/5l/list.c @@ -473,7 +473,7 @@ Oconv(Fmt *fp) void diag(char *fmt, ...) { - char buf[STRINGSZ], *tn, *sep; + char buf[1024], *tn, *sep; va_list arg; tn = ""; diff --git a/src/cmd/5l/noop.c b/src/cmd/5l/noop.c index 99a096a31..305ed684e 100644 --- a/src/cmd/5l/noop.c +++ b/src/cmd/5l/noop.c @@ -32,18 +32,16 @@ #include "l.h" #include "../ld/lib.h" - -// see ../../runtime/proc.c:/StackGuard -enum -{ - StackBig = 4096, - StackSmall = 128, -}; +#include "../../pkg/runtime/stack.h" static Sym* sym_div; static Sym* sym_divu; static Sym* sym_mod; static Sym* sym_modu; +static Sym* symmorestack; +static Prog* pmorestack; + +static Prog* stacksplit(Prog*, int32); static void linkcase(Prog *casep) @@ -62,16 +60,16 @@ linkcase(Prog *casep) void noops(void) { - Prog *p, *q, *q1; + Prog *p, *q, *q1, *q2; int o; - Prog *pmorestack; - Sym *symmorestack; + Sym *tlsfallback, *gmsym; /* * find leaf subroutines * strip NOPs * expand RET * expand BECOME pseudo + * fixup TLS */ if(debug['v']) @@ -86,6 +84,10 @@ noops(void) pmorestack = symmorestack->text; pmorestack->reg |= NOSPLIT; + tlsfallback = lookup("runtime.read_tls_fallback", 0); + gmsym = S; + if(linkmode == LinkExternal) + gmsym = lookup("runtime.tlsgm", 0); q = P; for(cursym = textp; cursym != nil; cursym = cursym->next) { for(p = cursym->text; p != P; p = p->link) { @@ -150,6 +152,82 @@ noops(void) } } break; + case AWORD: + // Rewrite TLS register fetch: MRC 15, 0, <reg>, C13, C0, 3 + if((p->to.offset & 0xffff0fff) == 0xee1d0f70) { + if(HEADTYPE == Hopenbsd) { + p->as = ARET; + } else if(goarm < 7) { + if(tlsfallback->type != STEXT) { + diag("runtime·read_tls_fallback not defined"); + errorexit(); + } + // BL runtime.read_tls_fallback(SB) + p->as = ABL; + p->to.type = D_BRANCH; + p->to.sym = tlsfallback; + p->cond = tlsfallback->text; + p->to.offset = 0; + cursym->text->mark &= ~LEAF; + } + if(linkmode == LinkExternal) { + // runtime.tlsgm is relocated with R_ARM_TLS_LE32 + // and $runtime.tlsgm will contain the TLS offset. + // + // MOV $runtime.tlsgm+tlsoffset(SB), REGTMP + // ADD REGTMP, <reg> + // + // In shared mode, runtime.tlsgm is relocated with + // R_ARM_TLS_IE32 and runtime.tlsgm(SB) will point + // to the GOT entry containing the TLS offset. + // + // MOV runtime.tlsgm(SB), REGTMP + // ADD REGTMP, <reg> + // SUB -tlsoffset, <reg> + // + // The SUB compensates for tlsoffset + // used in runtime.save_gm and runtime.load_gm. + q = p; + p = appendp(p); + p->as = AMOVW; + p->scond = 14; + p->reg = NREG; + if(flag_shared) { + p->from.type = D_OREG; + p->from.offset = 0; + } else { + p->from.type = D_CONST; + p->from.offset = tlsoffset; + } + p->from.sym = gmsym; + p->from.name = D_EXTERN; + p->to.type = D_REG; + p->to.reg = REGTMP; + p->to.offset = 0; + + p = appendp(p); + p->as = AADD; + p->scond = 14; + p->reg = NREG; + p->from.type = D_REG; + p->from.reg = REGTMP; + p->to.type = D_REG; + p->to.reg = (q->to.offset & 0xf000) >> 12; + p->to.offset = 0; + + if(flag_shared) { + p = appendp(p); + p->as = ASUB; + p->scond = 14; + p->reg = NREG; + p->from.type = D_CONST; + p->from.offset = -tlsoffset; + p->to.type = D_REG; + p->to.reg = (q->to.offset & 0xf000) >> 12; + p->to.offset = 0; + } + } + } } q = p; } @@ -180,160 +258,47 @@ noops(void) break; } - if(p->reg & NOSPLIT) { - q1 = prg(); - q1->as = AMOVW; - q1->scond |= C_WBIT; - q1->line = p->line; - q1->from.type = D_REG; - q1->from.reg = REGLINK; - q1->to.type = D_OREG; - q1->to.offset = -autosize; - q1->to.reg = REGSP; - q1->spadj = autosize; - q1->link = p->link; - p->link = q1; - } else if (autosize < StackBig) { - // split stack check for small functions - // MOVW g_stackguard(g), R1 - // CMP R1, $-autosize(SP) - // MOVW.LO $autosize, R1 - // MOVW.LO $args, R2 - // MOVW.LO R14, R3 - // BL.LO runtime.morestack(SB) // modifies LR - // MOVW.W R14,$-autosize(SP) - - // TODO(kaib): add more trampolines - // TODO(kaib): put stackguard in register - // TODO(kaib): add support for -K and underflow detection - - // MOVW g_stackguard(g), R1 + if(!(p->reg & NOSPLIT)) + p = stacksplit(p, autosize); // emit split check + + // MOVW.W R14,$-autosize(SP) + p = appendp(p); + p->as = AMOVW; + p->scond |= C_WBIT; + p->from.type = D_REG; + p->from.reg = REGLINK; + p->to.type = D_OREG; + p->to.offset = -autosize; + p->to.reg = REGSP; + p->spadj = autosize; + + if(cursym->text->reg & WRAPPER) { + // g->panicwrap += autosize; + // MOVW panicwrap_offset(g), R3 + // ADD $autosize, R3 + // MOVW R3 panicwrap_offset(g) p = appendp(p); p->as = AMOVW; p->from.type = D_OREG; p->from.reg = REGG; - p->to.type = D_REG; - p->to.reg = 1; - - if(autosize < StackSmall) { - // CMP R1, SP - p = appendp(p); - p->as = ACMP; - p->from.type = D_REG; - p->from.reg = 1; - p->reg = REGSP; - } else { - // MOVW $-autosize(SP), R2 - // CMP R1, R2 - p = appendp(p); - p->as = AMOVW; - p->from.type = D_CONST; - p->from.reg = REGSP; - p->from.offset = -autosize; - p->to.type = D_REG; - p->to.reg = 2; - - p = appendp(p); - p->as = ACMP; - p->from.type = D_REG; - p->from.reg = 1; - p->reg = 2; - } - - // MOVW.LO $autosize, R1 - p = appendp(p); - p->as = AMOVW; - p->scond = C_SCOND_LO; - p->from.type = D_CONST; - p->from.offset = autosize; - p->to.type = D_REG; - p->to.reg = 1; - - // MOVW.LO $args, R2 - p = appendp(p); - p->as = AMOVW; - p->scond = C_SCOND_LO; - p->from.type = D_CONST; - p->from.offset = (cursym->text->to.offset2 + 3) & ~3; - p->to.type = D_REG; - p->to.reg = 2; - - // MOVW.LO R14, R3 - p = appendp(p); - p->as = AMOVW; - p->scond = C_SCOND_LO; - p->from.type = D_REG; - p->from.reg = REGLINK; + p->from.offset = 2*PtrSize; p->to.type = D_REG; p->to.reg = 3; - - // BL.LO runtime.morestack(SB) // modifies LR - p = appendp(p); - p->as = ABL; - p->scond = C_SCOND_LO; - p->to.type = D_BRANCH; - p->to.sym = symmorestack; - p->cond = pmorestack; - - // MOVW.W R14,$-autosize(SP) - p = appendp(p); - p->as = AMOVW; - p->scond |= C_WBIT; - p->from.type = D_REG; - p->from.reg = REGLINK; - p->to.type = D_OREG; - p->to.offset = -autosize; - p->to.reg = REGSP; - p->spadj = autosize; - } else { // > StackBig - // MOVW $autosize, R1 - // MOVW $args, R2 - // MOVW R14, R3 - // BL runtime.morestack(SB) // modifies LR - // MOVW.W R14,$-autosize(SP) - - // MOVW $autosize, R1 + p = appendp(p); - p->as = AMOVW; + p->as = AADD; p->from.type = D_CONST; p->from.offset = autosize; p->to.type = D_REG; - p->to.reg = 1; - - // MOVW $args, R2 - // also need to store the extra 4 bytes. - p = appendp(p); - p->as = AMOVW; - p->from.type = D_CONST; - p->from.offset = (cursym->text->to.offset2 + 3) & ~3; - p->to.type = D_REG; - p->to.reg = 2; - - // MOVW R14, R3 - p = appendp(p); - p->as = AMOVW; - p->from.type = D_REG; - p->from.reg = REGLINK; - p->to.type = D_REG; p->to.reg = 3; - - // BL runtime.morestack(SB) // modifies LR - p = appendp(p); - p->as = ABL; - p->to.type = D_BRANCH; - p->to.sym = symmorestack; - p->cond = pmorestack; - - // MOVW.W R14,$-autosize(SP) + p = appendp(p); p->as = AMOVW; - p->scond |= C_WBIT; p->from.type = D_REG; - p->from.reg = REGLINK; + p->from.reg = 3; p->to.type = D_OREG; - p->to.offset = -autosize; - p->to.reg = REGSP; - p->spadj = autosize; + p->to.reg = REGG; + p->to.offset = 2*PtrSize; } break; @@ -343,12 +308,56 @@ noops(void) if(!autosize) { p->as = AB; p->from = zprg.from; - p->to.type = D_OREG; - p->to.offset = 0; - p->to.reg = REGLINK; + if(p->to.sym) { // retjmp + p->to.type = D_BRANCH; + p->cond = p->to.sym->text; + } else { + p->to.type = D_OREG; + p->to.offset = 0; + p->to.reg = REGLINK; + } break; } } + + if(cursym->text->reg & WRAPPER) { + int cond; + + // Preserve original RET's cond, to allow RET.EQ + // in the implementation of reflect.call. + cond = p->scond; + p->scond = C_SCOND_NONE; + + // g->panicwrap -= autosize; + // MOVW panicwrap_offset(g), R3 + // SUB $autosize, R3 + // MOVW R3 panicwrap_offset(g) + p->as = AMOVW; + p->from.type = D_OREG; + p->from.reg = REGG; + p->from.offset = 2*PtrSize; + p->to.type = D_REG; + p->to.reg = 3; + p = appendp(p); + + p->as = ASUB; + p->from.type = D_CONST; + p->from.offset = autosize; + p->to.type = D_REG; + p->to.reg = 3; + p = appendp(p); + + p->as = AMOVW; + p->from.type = D_REG; + p->from.reg = 3; + p->to.type = D_OREG; + p->to.reg = REGG; + p->to.offset = 2*PtrSize; + p = appendp(p); + + p->scond = cond; + } + p->as = AMOVW; p->scond |= C_PBIT; p->from.type = D_OREG; @@ -359,6 +368,17 @@ noops(void) // If there are instructions following // this ARET, they come from a branch // with the same stackframe, so no spadj. + + if(p->to.sym) { // retjmp + p->to.reg = REGLINK; + q2 = appendp(p); + q2->as = AB; + q2->to.type = D_BRANCH; + q2->to.sym = p->to.sym; + q2->cond = p->to.sym->text; + p->to.sym = nil; + p = q2; + } break; case AADD: @@ -452,14 +472,27 @@ noops(void) p->to.reg = REGSP; p->spadj = -8; - /* SUB $8,SP */ - q1->as = ASUB; - q1->from.type = D_CONST; - q1->from.offset = 8; - q1->from.reg = NREG; + /* Keep saved LR at 0(SP) after SP change. */ + /* MOVW 0(SP), REGTMP; MOVW REGTMP, -8!(SP) */ + /* TODO: Remove SP adjustments; see issue 6699. */ + q1->as = AMOVW; + q1->from.type = D_OREG; + q1->from.reg = REGSP; + q1->from.offset = 0; q1->reg = NREG; q1->to.type = D_REG; + q1->to.reg = REGTMP; + + /* SUB $8,SP */ + q1 = appendp(q1); + q1->as = AMOVW; + q1->from.type = D_REG; + q1->from.reg = REGTMP; + q1->reg = NREG; + q1->to.type = D_OREG; q1->to.reg = REGSP; + q1->to.offset = -8; + q1->scond |= C_WBIT; q1->spadj = 8; break; @@ -476,6 +509,143 @@ noops(void) } } +static Prog* +stacksplit(Prog *p, int32 framesize) +{ + int32 arg; + + // MOVW g_stackguard(g), R1 + p = appendp(p); + p->as = AMOVW; + p->from.type = D_OREG; + p->from.reg = REGG; + p->to.type = D_REG; + p->to.reg = 1; + + if(framesize <= StackSmall) { + // small stack: SP < stackguard + // CMP stackguard, SP + p = appendp(p); + p->as = ACMP; + p->from.type = D_REG; + p->from.reg = 1; + p->reg = REGSP; + } else if(framesize <= StackBig) { + // large stack: SP-framesize < stackguard-StackSmall + // MOVW $-framesize(SP), R2 + // CMP stackguard, R2 + p = appendp(p); + p->as = AMOVW; + p->from.type = D_CONST; + p->from.reg = REGSP; + p->from.offset = -framesize; + p->to.type = D_REG; + p->to.reg = 2; + + p = appendp(p); + p->as = ACMP; + p->from.type = D_REG; + p->from.reg = 1; + p->reg = 2; + } else { + // Such a large stack we need to protect against wraparound + // if SP is close to zero. + // SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall) + // The +StackGuard on both sides is required to keep the left side positive: + // SP is allowed to be slightly below stackguard. See stack.h. + // CMP $StackPreempt, R1 + // MOVW.NE $StackGuard(SP), R2 + // SUB.NE R1, R2 + // MOVW.NE $(framesize+(StackGuard-StackSmall)), R3 + // CMP.NE R3, R2 + p = appendp(p); + p->as = ACMP; + p->from.type = D_CONST; + p->from.offset = (uint32)StackPreempt; + p->reg = 1; + + p = appendp(p); + p->as = AMOVW; + p->from.type = D_CONST; + p->from.reg = REGSP; + p->from.offset = StackGuard; + p->to.type = D_REG; + p->to.reg = 2; + p->scond = C_SCOND_NE; + + p = appendp(p); + p->as = ASUB; + p->from.type = D_REG; + p->from.reg = 1; + p->to.type = D_REG; + p->to.reg = 2; + p->scond = C_SCOND_NE; + + p = appendp(p); + p->as = AMOVW; + p->from.type = D_CONST; + p->from.offset = framesize + (StackGuard - StackSmall); + p->to.type = D_REG; + p->to.reg = 3; + p->scond = C_SCOND_NE; + + p = appendp(p); + p->as = ACMP; + p->from.type = D_REG; + p->from.reg = 3; + p->reg = 2; + p->scond = C_SCOND_NE; + } + + // MOVW.LS $framesize, R1 + p = appendp(p); + p->as = AMOVW; + p->scond = C_SCOND_LS; + p->from.type = D_CONST; + p->from.offset = framesize; + p->to.type = D_REG; + p->to.reg = 1; + + // MOVW.LS $args, R2 + p = appendp(p); + p->as = AMOVW; + p->scond = C_SCOND_LS; + p->from.type = D_CONST; + arg = cursym->text->to.offset2; + if(arg == 1) // special marker for known 0 + arg = 0; + if(arg&3) + diag("misaligned argument size in stack split"); + p->from.offset = arg; + p->to.type = D_REG; + p->to.reg = 2; + + // MOVW.LS R14, R3 + p = appendp(p); + p->as = AMOVW; + p->scond = C_SCOND_LS; + p->from.type = D_REG; + p->from.reg = REGLINK; + p->to.type = D_REG; + p->to.reg = 3; + + // BL.LS runtime.morestack(SB) // modifies LR, returns with LO still asserted + p = appendp(p); + p->as = ABL; + p->scond = C_SCOND_LS; + p->to.type = D_BRANCH; + p->to.sym = symmorestack; + p->cond = pmorestack; + + // BLS start + p = appendp(p); + p->as = ABLS; + p->to.type = D_BRANCH; + p->cond = cursym->text->link; + + return p; +} + static void sigdiv(char *n) { diff --git a/src/cmd/5l/obj.c b/src/cmd/5l/obj.c index 24e6294a8..80f5787dc 100644 --- a/src/cmd/5l/obj.c +++ b/src/cmd/5l/obj.c @@ -81,8 +81,7 @@ main(int argc, char *argv[]) INITDAT = -1; INITRND = -1; INITENTRY = 0; - LIBINITENTRY = 0; - linkmode = LinkInternal; // TODO: LinkAuto once everything works. + linkmode = LinkAuto; nuxiinit(); p = getgoarm(); @@ -118,6 +117,7 @@ main(int argc, char *argv[]) flagstr("extldflags", "flags for external linker", &extldflags); flagcount("f", "ignore version mismatch", &debug['f']); flagcount("g", "disable go package data checks", &debug['g']); + flagstr("installsuffix", "pkg directory suffix", &flag_installsuffix); flagstr("k", "sym: set field tracking symbol", &tracksym); flagfn1("linkmode", "mode: set link mode (internal, external, auto)", setlinkmode); flagcount("n", "dump symbol table", &debug['n']); @@ -126,34 +126,43 @@ main(int argc, char *argv[]) flagstr("r", "dir1:dir2:...: set ELF dynamic linker search path", &rpath); flagcount("race", "enable race detector", &flag_race); flagcount("s", "disable symbol table", &debug['s']); + flagcount("shared", "generate shared object (implies -linkmode external)", &flag_shared); flagstr("tmpdir", "leave temporary files in this directory", &tmpdir); flagcount("u", "reject unsafe packages", &debug['u']); flagcount("v", "print link trace", &debug['v']); flagcount("w", "disable DWARF generation", &debug['w']); - flagcount("shared", "generate shared object", &flag_shared); - // TODO: link mode flag flagparse(&argc, &argv, usage); if(argc != 1) usage(); + if(flag_shared) + linkmode = LinkExternal; + + mywhatsys(); + + if(HEADTYPE == -1) + HEADTYPE = headtype(goos); + // getgoextlinkenabled is based on GO_EXTLINK_ENABLED when // Go was built; see ../../make.bash. if(linkmode == LinkAuto && strcmp(getgoextlinkenabled(), "0") == 0) linkmode = LinkInternal; - if(linkmode == LinkExternal) { - diag("only -linkmode=internal is supported"); - errorexit(); - } else if(linkmode == LinkAuto) { - linkmode = LinkInternal; + switch(HEADTYPE) { + default: + if(linkmode == LinkAuto) + linkmode = LinkInternal; + if(linkmode == LinkExternal && strcmp(getgoextlinkenabled(), "1") != 0) + sysfatal("cannot use -linkmode=external with -H %s", headstr(HEADTYPE)); + break; + case Hlinux: + break; } libinit(); - if(HEADTYPE == -1) - HEADTYPE = headtype(goos); switch(HEADTYPE) { default: diag("unknown -H option"); @@ -208,7 +217,7 @@ main(int argc, char *argv[]) case Hnetbsd: debug['d'] = 0; // with dynamic linking tlsoffset = -8; // hardcoded number, first 4-byte word for g, and then 4-byte word for m - // this number is known to ../../pkg/runtime/cgo/gcc_linux_arm.c + // this number is known to ../../pkg/runtime/rt0_*_arm.s elfinit(); HEADR = ELFRESERVE; if(INITTEXT == -1) @@ -253,6 +262,7 @@ main(int argc, char *argv[]) // mark some functions that are only referenced after linker code editing if(debug['F']) mark(rlookup("_sfloat", 0)); + mark(lookup("runtime.read_tls_fallback", 0)); deadcode(); if(textp == nil) { diag("no code"); @@ -325,7 +335,7 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[]) c = BGETC(f); if(c < 0 || c > NSYM){ print("sym out of range: %d\n", c); - Bputc(f, ALAST+1); + BPUTC(f, ALAST+1); return; } a->sym = h[c]; @@ -334,7 +344,7 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[]) if((schar)a->reg < 0 || a->reg > NREG) { print("register out of range %d\n", a->reg); - Bputc(f, ALAST+1); + BPUTC(f, ALAST+1); return; /* force real diagnostic */ } @@ -352,7 +362,7 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[]) switch(a->type) { default: print("unknown type %d\n", a->type); - Bputc(f, ALAST+1); + BPUTC(f, ALAST+1); return; /* force real diagnostic */ case D_NONE: @@ -368,13 +378,13 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[]) break; case D_CONST2: - a->offset2 = Bget4(f); // fall through + a->offset2 = BGETLE4(f); // fall through case D_BRANCH: case D_OREG: case D_CONST: case D_OCONST: case D_SHIFT: - a->offset = Bget4(f); + a->offset = BGETLE4(f); break; case D_SCONST: @@ -383,8 +393,8 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[]) break; case D_FCONST: - a->ieee.l = Bget4(f); - a->ieee.h = Bget4(f); + a->ieee.l = BGETLE4(f); + a->ieee.h = BGETLE4(f); break; } s = a->sym; @@ -467,7 +477,7 @@ loop: if(o == ANAME || o == ASIGNAME) { sig = 0; if(o == ASIGNAME) - sig = Bget4(f); + sig = BGETLE4(f); v = BGETC(f); /* type */ o = BGETC(f); /* sym */ r = 0; @@ -522,7 +532,7 @@ loop: p->as = o; p->scond = BGETC(f); p->reg = BGETC(f); - p->line = Bget4(f); + p->line = BGETLE4(f); zaddr(pn, f, &p->from, h); fromgotype = adrgotype; @@ -549,6 +559,7 @@ loop: addhist(p->line, D_FILE); /* 'z' */ if(p->to.offset) addhist(p->to.offset, D_FILE1); /* 'Z' */ + savehist(p->line, p->to.offset); histfrogp = 0; goto loop; @@ -614,13 +625,6 @@ loop: pc++; break; - case ALOCALS: - if(skip) - goto casedef; - cursym->locals = p->to.offset; - pc++; - break; - case ATYPE: if(skip) goto casedef; @@ -667,6 +671,7 @@ loop: p->to.offset = autosize; autosize += 4; s->type = STEXT; + s->hist = gethist(); s->text = p; s->value = pc; s->args = p->to.offset2; diff --git a/src/cmd/5l/optab.c b/src/cmd/5l/optab.c index 231071f20..3d05d6d09 100644 --- a/src/cmd/5l/optab.c +++ b/src/cmd/5l/optab.c @@ -62,8 +62,8 @@ Optab optab[] = { ABEQ, C_NONE, C_NONE, C_SBRA, 5, 4, 0 }, { AB, C_NONE, C_NONE, C_ROREG, 6, 4, 0, LPOOL }, - { ABL, C_NONE, C_NONE, C_ROREG, 7, 8, 0 }, - { ABL, C_REG, C_NONE, C_ROREG, 7, 8, 0 }, + { ABL, C_NONE, C_NONE, C_ROREG, 7, 4, 0 }, + { ABL, C_REG, C_NONE, C_ROREG, 7, 4, 0 }, { ABX, C_NONE, C_NONE, C_ROREG, 75, 12, 0 }, { ABXRET, C_NONE, C_NONE, C_ROREG, 76, 4, 0 }, @@ -94,9 +94,11 @@ Optab optab[] = { AMVN, C_LCON, C_NONE, C_REG, 13, 8, 0, LFROM }, { ACMP, C_LCON, C_REG, C_NONE, 13, 8, 0, LFROM }, - { AMOVB, C_REG, C_NONE, C_REG, 14, 8, 0 }, + { AMOVB, C_REG, C_NONE, C_REG, 1, 4, 0 }, + { AMOVBS, C_REG, C_NONE, C_REG, 14, 8, 0 }, { AMOVBU, C_REG, C_NONE, C_REG, 58, 4, 0 }, - { AMOVH, C_REG, C_NONE, C_REG, 14, 8, 0 }, + { AMOVH, C_REG, C_NONE, C_REG, 1, 4, 0 }, + { AMOVHS, C_REG, C_NONE, C_REG, 14, 8, 0 }, { AMOVHU, C_REG, C_NONE, C_REG, 14, 8, 0 }, { AMUL, C_REG, C_REG, C_REG, 15, 4, 0 }, @@ -112,6 +114,8 @@ Optab optab[] = { AMOVW, C_REG, C_NONE, C_SOREG, 20, 4, 0 }, { AMOVB, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP }, { AMOVB, C_REG, C_NONE, C_SOREG, 20, 4, 0 }, + { AMOVBS, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP }, + { AMOVBS, C_REG, C_NONE, C_SOREG, 20, 4, 0 }, { AMOVBU, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP }, { AMOVBU, C_REG, C_NONE, C_SOREG, 20, 4, 0 }, @@ -126,6 +130,9 @@ Optab optab[] = { AMOVB, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO }, { AMOVB, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO }, { AMOVB, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4 }, + { AMOVBS, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO }, + { AMOVBS, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO }, + { AMOVBS, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4 }, { AMOVBU, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO }, { AMOVBU, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO }, { AMOVBU, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4 }, @@ -176,29 +183,40 @@ Optab optab[] = { AMOVBU, C_SHIFT,C_NONE, C_REG, 59, 4, 0 }, { AMOVB, C_SHIFT,C_NONE, C_REG, 60, 4, 0 }, + { AMOVBS, C_SHIFT,C_NONE, C_REG, 60, 4, 0 }, { AMOVW, C_REG, C_NONE, C_SHIFT, 61, 4, 0 }, { AMOVB, C_REG, C_NONE, C_SHIFT, 61, 4, 0 }, + { AMOVBS, C_REG, C_NONE, C_SHIFT, 61, 4, 0 }, { AMOVBU, C_REG, C_NONE, C_SHIFT, 61, 4, 0 }, { ACASE, C_REG, C_NONE, C_NONE, 62, 4, 0, LPCREL, 8 }, - { ABCASE, C_NONE, C_NONE, C_SBRA, 63, 4, 0 }, + { ABCASE, C_NONE, C_NONE, C_SBRA, 63, 4, 0, LPCREL, 0 }, { AMOVH, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, 0 }, { AMOVH, C_REG, C_NONE, C_HOREG, 70, 4, 0, 0 }, + { AMOVHS, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, 0 }, + { AMOVHS, C_REG, C_NONE, C_HOREG, 70, 4, 0, 0 }, { AMOVHU, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, 0 }, { AMOVHU, C_REG, C_NONE, C_HOREG, 70, 4, 0, 0 }, { AMOVB, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 }, { AMOVB, C_HOREG,C_NONE, C_REG, 71, 4, 0, 0 }, + { AMOVBS, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 }, + { AMOVBS, C_HOREG,C_NONE, C_REG, 71, 4, 0, 0 }, { AMOVH, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 }, { AMOVH, C_HOREG,C_NONE, C_REG, 71, 4, 0, 0 }, + { AMOVHS, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 }, + { AMOVHS, C_HOREG,C_NONE, C_REG, 71, 4, 0, 0 }, { AMOVHU, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 }, { AMOVHU, C_HOREG,C_NONE, C_REG, 71, 4, 0, 0 }, { AMOVH, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO }, { AMOVH, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO }, { AMOVH, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO | LPCREL, 4 }, + { AMOVHS, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO }, + { AMOVHS, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO }, + { AMOVHS, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO | LPCREL, 4 }, { AMOVHU, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO }, { AMOVHU, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO }, { AMOVHU, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO | LPCREL, 4 }, @@ -206,9 +224,15 @@ Optab optab[] = { AMOVB, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, { AMOVB, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, { AMOVB, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 }, + { AMOVBS, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, + { AMOVBS, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, + { AMOVBS, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 }, { AMOVH, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, { AMOVH, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, { AMOVH, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 }, + { AMOVHS, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, + { AMOVHS, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, + { AMOVHS, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 }, { AMOVHU, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, { AMOVHU, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, { AMOVHU, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 }, @@ -246,6 +270,8 @@ Optab optab[] = { AMULAWT, C_REG, C_REG, C_REGREG2, 99, 4, 0 }, { AUSEFIELD, C_ADDR, C_NONE, C_NONE, 0, 0, 0 }, + { APCDATA, C_LCON, C_NONE, C_LCON, 0, 0, 0 }, + { AFUNCDATA, C_LCON, C_NONE, C_ADDR, 0, 0, 0 }, { AXXX, C_NONE, C_NONE, C_NONE, 0, 4, 0 }, }; diff --git a/src/cmd/5l/pass.c b/src/cmd/5l/pass.c index c22b86085..cd8897989 100644 --- a/src/cmd/5l/pass.c +++ b/src/cmd/5l/pass.c @@ -130,7 +130,7 @@ loop: r = prg(); *r = *p; if(!(r->mark&FOLL)) - print("cant happen 1\n"); + print("can't happen 1\n"); r->mark |= FOLL; if(p != q) { p = p->link; @@ -150,7 +150,7 @@ loop: if(!(r->link->mark&FOLL)) xfol(r->link, last); if(!(r->cond->mark&FOLL)) - print("cant happen 2\n"); + print("can't happen 2\n"); return; } } @@ -246,6 +246,13 @@ patch(void) p->cond = q; } } + if(flag_shared) { + s = lookup("init_array", 0); + s->type = SINITARR; + s->reachable = 1; + s->hide = 1; + addaddr(s, lookup(INITENTRY, 0)); + } for(cursym = textp; cursym != nil; cursym = cursym->next) { for(p = cursym->text; p != P; p = p->link) { diff --git a/src/cmd/5l/span.c b/src/cmd/5l/span.c index a5afa02e7..e7cc0b4b1 100644 --- a/src/cmd/5l/span.c +++ b/src/cmd/5l/span.c @@ -90,7 +90,7 @@ span(void) int32 c, otxt, out[6]; Section *sect; uchar *bp; - Sym *sub; + Sym *sub, *gmsym; if(debug['v']) Bprint(&bso, "%5.2f span\n", cputime()); @@ -141,7 +141,7 @@ span(void) if(checkpool(op, p->as == ACASE ? casesz(p) : m)) c = p->pc = scan(op, p, c); } - if(m == 0) { + if(m == 0 && (p->as != AFUNCDATA && p->as != APCDATA)) { diag("zero-width instruction\n%P", p); continue; } @@ -211,7 +211,7 @@ span(void) } */ m = o->size; - if(m == 0) { + if(m == 0 && (p->as != AFUNCDATA && p->as != APCDATA)) { if(p->as == ATEXT) { autosize = p->to.offset + 4; if(p->from.sym != S) @@ -237,6 +237,9 @@ span(void) * code references to be relocated too, and then * perhaps we'd be able to parallelize the span loop above. */ + gmsym = S; + if(linkmode == LinkExternal) + gmsym = lookup("runtime.tlsgm", 0); for(cursym = textp; cursym != nil; cursym = cursym->next) { p = cursym->text; if(p == P || p->link == P) @@ -249,7 +252,7 @@ span(void) pc = p->pc; curp = p; o = oplook(p); - asmout(p, o, out); + asmout(p, o, out, gmsym); for(i=0; i<o->size/4; i++) { v = out[i]; *bp++ = v; @@ -574,10 +577,7 @@ aclass(Adr *a) if(s == S) break; instoffset = 0; // s.b. unused but just in case - if(flag_shared) - return C_LCONADDR; - else - return C_LCON; + return C_LCONADDR; case D_AUTO: instoffset = autosize + a->offset; @@ -813,8 +813,10 @@ buildop(void) break; case AMOVW: case AMOVB: + case AMOVBS: case AMOVBU: case AMOVH: + case AMOVHS: case AMOVHU: break; case ASWPW: @@ -830,7 +832,6 @@ buildop(void) case ARFE: case ATEXT: case AUSEFIELD: - case ALOCALS: case ACASE: case ABCASE: case ATYPE: @@ -890,6 +891,8 @@ buildop(void) case APLD: case AUNDEF: case ACLZ: + case AFUNCDATA: + case APCDATA: break; } } diff --git a/src/cmd/6a/a.y b/src/cmd/6a/a.y index 42af65e35..ed72916b2 100644 --- a/src/cmd/6a/a.y +++ b/src/cmd/6a/a.y @@ -33,6 +33,7 @@ #include <stdio.h> /* if we don't, bison will, and a.h re-#defines getc */ #include <libc.h> #include "a.h" +#include "../../pkg/runtime/funcdata.h" %} %union { Sym *sym; @@ -49,8 +50,8 @@ %left '+' '-' %left '*' '/' '%' %token <lval> LTYPE0 LTYPE1 LTYPE2 LTYPE3 LTYPE4 -%token <lval> LTYPEC LTYPED LTYPEN LTYPER LTYPET LTYPEG -%token <lval> LTYPES LTYPEM LTYPEI LTYPEXC LTYPEX LTYPERT +%token <lval> LTYPEC LTYPED LTYPEN LTYPER LTYPET LTYPEG LTYPEPC +%token <lval> LTYPES LTYPEM LTYPEI LTYPEXC LTYPEX LTYPERT LTYPEF %token <lval> LCONST LFP LPC LSB %token <lval> LBREG LLREG LSREG LFREG LMREG LXREG %token <dval> LFCONST @@ -58,8 +59,9 @@ %token <sym> LNAME LLAB LVAR %type <lval> con con2 expr pointer offset %type <gen> mem imm imm2 reg nam rel rem rim rom omem nmem -%type <gen2> nonnon nonrel nonrem rimnon rimrem remrim spec10 spec11 +%type <gen2> nonnon nonrel nonrem rimnon rimrem remrim %type <gen2> spec1 spec2 spec3 spec4 spec5 spec6 spec7 spec8 spec9 +%type <gen2> spec10 spec11 spec12 spec13 %% prog: | prog @@ -115,6 +117,8 @@ inst: | LTYPEX spec9 { outcode($1, &$2); } | LTYPERT spec10 { outcode($1, &$2); } | LTYPEG spec11 { outcode($1, &$2); } +| LTYPEPC spec12 { outcode($1, &$2); } +| LTYPEF spec13 { outcode($1, &$2); } nonnon: { @@ -308,6 +312,26 @@ spec11: /* GLOBL */ $$.to = $5; } +spec12: /* PCDATA */ + rim ',' rim + { + if($1.type != D_CONST || $3.type != D_CONST) + yyerror("arguments to PCDATA must be integer constants"); + $$.from = $1; + $$.to = $3; + } + +spec13: /* FUNCDATA */ + rim ',' rim + { + if($1.type != D_CONST) + yyerror("index for FUNCDATA must be integer constant"); + if($3.type != D_EXTERN && $3.type != D_STATIC) + yyerror("value for FUNCDATA must be symbol reference"); + $$.from = $1; + $$.to = $3; + } + rem: reg | mem @@ -494,6 +518,15 @@ omem: $$.scale = $8; checkscale($$.scale); } +| con '(' LLREG ')' '(' LSREG '*' con ')' + { + $$ = nullgen; + $$.type = D_INDIR+$3; + $$.offset = $1; + $$.index = $6; + $$.scale = $8; + checkscale($$.scale); + } | '(' LLREG ')' { $$ = nullgen; @@ -597,11 +630,13 @@ con: con2: LCONST { - $$ = $1 & 0xffffffffLL; + $$ = ($1 & 0xffffffffLL) + + ((vlong)ArgsSizeUnknown << 32); } | '-' LCONST { - $$ = -$2 & 0xffffffffLL; + $$ = (-$2 & 0xffffffffLL) + + ((vlong)ArgsSizeUnknown << 32); } | LCONST '-' LCONST { diff --git a/src/cmd/6a/doc.go b/src/cmd/6a/doc.go index a5f3f87f0..9f14cc0d0 100644 --- a/src/cmd/6a/doc.go +++ b/src/cmd/6a/doc.go @@ -10,6 +10,10 @@ http://plan9.bell-labs.com/magic/man2html/1/8a +Go-specific considerations are documented at + + http://golang.org/doc/asm + Its target architecture is the x86-64, referred to by these tools as amd64. */ diff --git a/src/cmd/6a/lex.c b/src/cmd/6a/lex.c index c969e98e5..ab34e8220 100644 --- a/src/cmd/6a/lex.c +++ b/src/cmd/6a/lex.c @@ -74,7 +74,7 @@ main(int argc, char *argv[]) ARGBEGIN { default: c = ARGC(); - if(c >= 0 || c < sizeof(debug)) + if(c >= 0 && c < sizeof(debug)) debug[c] = 1; break; @@ -1019,7 +1019,9 @@ struct "AESKEYGENASSIST", LTYPEX, AAESKEYGENASSIST, "PSHUFD", LTYPEX, APSHUFD, "USEFIELD", LTYPEN, AUSEFIELD, - + "PCLMULQDQ", LTYPEX, APCLMULQDQ, + "PCDATA", LTYPEPC, APCDATA, + "FUNCDATA", LTYPEF, AFUNCDATA, 0 }; @@ -1099,15 +1101,14 @@ void zname(char *n, int t, int s) { - Bputc(&obuf, ANAME); /* as(2) */ - Bputc(&obuf, ANAME>>8); - Bputc(&obuf, t); /* type */ - Bputc(&obuf, s); /* sym */ + BPUTLE2(&obuf, ANAME); /* as(2) */ + BPUTC(&obuf, t); /* type */ + BPUTC(&obuf, s); /* sym */ while(*n) { - Bputc(&obuf, *n); + BPUTC(&obuf, *n); n++; } - Bputc(&obuf, 0); + BPUTC(&obuf, 0); } void @@ -1143,52 +1144,40 @@ zaddr(Gen *a, int s) case D_NONE: break; } - Bputc(&obuf, t); + BPUTC(&obuf, t); if(t & T_INDEX) { /* implies index, scale */ - Bputc(&obuf, a->index); - Bputc(&obuf, a->scale); + BPUTC(&obuf, a->index); + BPUTC(&obuf, a->scale); } if(t & T_OFFSET) { /* implies offset */ l = a->offset; - Bputc(&obuf, l); - Bputc(&obuf, l>>8); - Bputc(&obuf, l>>16); - Bputc(&obuf, l>>24); + BPUTLE4(&obuf, l); if(t & T_64) { l = a->offset>>32; - Bputc(&obuf, l); - Bputc(&obuf, l>>8); - Bputc(&obuf, l>>16); - Bputc(&obuf, l>>24); + BPUTLE4(&obuf, l); } } if(t & T_SYM) /* implies sym */ - Bputc(&obuf, s); + BPUTC(&obuf, s); if(t & T_FCONST) { ieeedtod(&e, a->dval); l = e.l; - Bputc(&obuf, l); - Bputc(&obuf, l>>8); - Bputc(&obuf, l>>16); - Bputc(&obuf, l>>24); + BPUTLE4(&obuf, l); l = e.h; - Bputc(&obuf, l); - Bputc(&obuf, l>>8); - Bputc(&obuf, l>>16); - Bputc(&obuf, l>>24); + BPUTLE4(&obuf, l); return; } if(t & T_SCONST) { n = a->sval; for(i=0; i<NSNAME; i++) { - Bputc(&obuf, *n); + BPUTC(&obuf, *n); n++; } return; } if(t & T_TYPE) - Bputc(&obuf, a->type); + BPUTC(&obuf, a->type); } void @@ -1247,12 +1236,8 @@ jackpot: goto jackpot; break; } - Bputc(&obuf, a); - Bputc(&obuf, a>>8); - Bputc(&obuf, stmtline); - Bputc(&obuf, stmtline>>8); - Bputc(&obuf, stmtline>>16); - Bputc(&obuf, stmtline>>24); + BPUTLE2(&obuf, a); + BPUTLE4(&obuf, stmtline); zaddr(&g2->from, sf); zaddr(&g2->to, st); @@ -1327,13 +1312,12 @@ outhist(void) q = 0; } if(n) { - Bputc(&obuf, ANAME); - Bputc(&obuf, ANAME>>8); - Bputc(&obuf, D_FILE); /* type */ - Bputc(&obuf, 1); /* sym */ - Bputc(&obuf, '<'); + BPUTLE2(&obuf, ANAME); + BPUTC(&obuf, D_FILE); /* type */ + BPUTC(&obuf, 1); /* sym */ + BPUTC(&obuf, '<'); Bwrite(&obuf, p, n); - Bputc(&obuf, 0); + BPUTC(&obuf, 0); } p = q; if(p == 0 && op) { @@ -1343,12 +1327,8 @@ outhist(void) } g.offset = h->offset; - Bputc(&obuf, AHISTORY); - Bputc(&obuf, AHISTORY>>8); - Bputc(&obuf, h->line); - Bputc(&obuf, h->line>>8); - Bputc(&obuf, h->line>>16); - Bputc(&obuf, h->line>>24); + BPUTLE2(&obuf, AHISTORY); + BPUTLE4(&obuf, h->line); zaddr(&nullgen, 0); zaddr(&g, 0); diff --git a/src/cmd/6a/y.tab.c b/src/cmd/6a/y.tab.c index 75c4ad5ea..3e5058b9d 100644 --- a/src/cmd/6a/y.tab.c +++ b/src/cmd/6a/y.tab.c @@ -1,24 +1,21 @@ -/* A Bison parser, made by GNU Bison 2.3. */ +/* A Bison parser, made by GNU Bison 2.5. */ -/* Skeleton implementation for Bison's Yacc-like parsers in C - - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 - Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ + along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work @@ -29,7 +26,7 @@ special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. - + This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ @@ -47,7 +44,7 @@ #define YYBISON 1 /* Bison version. */ -#define YYBISON_VERSION "2.3" +#define YYBISON_VERSION "2.5" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -55,11 +52,51 @@ /* Pure parsers. */ #define YYPURE 0 +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + /* Using locations. */ #define YYLSP_NEEDED 0 +/* Copy the first part of user declarations. */ + +/* Line 268 of yacc.c */ +#line 31 "a.y" + +#include <u.h> +#include <stdio.h> /* if we don't, bison will, and a.h re-#defines getc */ +#include <libc.h> +#include "a.h" +#include "../../pkg/runtime/funcdata.h" + + +/* Line 268 of yacc.c */ +#line 80 "y.tab.c" + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + + /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE @@ -77,28 +114,30 @@ LTYPER = 266, LTYPET = 267, LTYPEG = 268, - LTYPES = 269, - LTYPEM = 270, - LTYPEI = 271, - LTYPEXC = 272, - LTYPEX = 273, - LTYPERT = 274, - LCONST = 275, - LFP = 276, - LPC = 277, - LSB = 278, - LBREG = 279, - LLREG = 280, - LSREG = 281, - LFREG = 282, - LMREG = 283, - LXREG = 284, - LFCONST = 285, - LSCONST = 286, - LSP = 287, - LNAME = 288, - LLAB = 289, - LVAR = 290 + LTYPEPC = 269, + LTYPES = 270, + LTYPEM = 271, + LTYPEI = 272, + LTYPEXC = 273, + LTYPEX = 274, + LTYPERT = 275, + LTYPEF = 276, + LCONST = 277, + LFP = 278, + LPC = 279, + LSB = 280, + LBREG = 281, + LLREG = 282, + LSREG = 283, + LFREG = 284, + LMREG = 285, + LXREG = 286, + LFCONST = 287, + LSCONST = 288, + LSP = 289, + LNAME = 290, + LLAB = 291, + LVAR = 292 }; #endif /* Tokens. */ @@ -113,85 +152,64 @@ #define LTYPER 266 #define LTYPET 267 #define LTYPEG 268 -#define LTYPES 269 -#define LTYPEM 270 -#define LTYPEI 271 -#define LTYPEXC 272 -#define LTYPEX 273 -#define LTYPERT 274 -#define LCONST 275 -#define LFP 276 -#define LPC 277 -#define LSB 278 -#define LBREG 279 -#define LLREG 280 -#define LSREG 281 -#define LFREG 282 -#define LMREG 283 -#define LXREG 284 -#define LFCONST 285 -#define LSCONST 286 -#define LSP 287 -#define LNAME 288 -#define LLAB 289 -#define LVAR 290 - - - - -/* Copy the first part of user declarations. */ -#line 31 "a.y" - -#include <u.h> -#include <stdio.h> /* if we don't, bison will, and a.h re-#defines getc */ -#include <libc.h> -#include "a.h" - +#define LTYPEPC 269 +#define LTYPES 270 +#define LTYPEM 271 +#define LTYPEI 272 +#define LTYPEXC 273 +#define LTYPEX 274 +#define LTYPERT 275 +#define LTYPEF 276 +#define LCONST 277 +#define LFP 278 +#define LPC 279 +#define LSB 280 +#define LBREG 281 +#define LLREG 282 +#define LSREG 283 +#define LFREG 284 +#define LMREG 285 +#define LXREG 286 +#define LFCONST 287 +#define LSCONST 288 +#define LSP 289 +#define LNAME 290 +#define LLAB 291 +#define LVAR 292 -/* Enabling traces. */ -#ifndef YYDEBUG -# define YYDEBUG 0 -#endif -/* Enabling verbose error messages. */ -#ifdef YYERROR_VERBOSE -# undef YYERROR_VERBOSE -# define YYERROR_VERBOSE 1 -#else -# define YYERROR_VERBOSE 0 -#endif -/* Enabling the token table. */ -#ifndef YYTOKEN_TABLE -# define YYTOKEN_TABLE 0 -#endif #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE -#line 37 "a.y" { + +/* Line 293 of yacc.c */ +#line 38 "a.y" + Sym *sym; vlong lval; double dval; char sval[8]; Gen gen; Gen2 gen2; -} -/* Line 193 of yacc.c. */ -#line 182 "y.tab.c" - YYSTYPE; + + + +/* Line 293 of yacc.c */ +#line 201 "y.tab.c" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 -# define YYSTYPE_IS_TRIVIAL 1 #endif - /* Copy the second part of user declarations. */ -/* Line 216 of yacc.c. */ -#line 195 "y.tab.c" +/* Line 343 of yacc.c */ +#line 213 "y.tab.c" #ifdef short # undef short @@ -266,14 +284,14 @@ typedef short int yytype_int16; #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static int -YYID (int i) +YYID (int yyi) #else static int -YYID (i) - int i; +YYID (yyi) + int yyi; #endif { - return i; + return yyi; } #endif @@ -294,11 +312,11 @@ YYID (i) # define alloca _alloca # else # define YYSTACK_ALLOC alloca -# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 # endif # endif # endif @@ -321,24 +339,24 @@ YYID (i) # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif -# if (defined __cplusplus && ! defined _STDLIB_H \ +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc -# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free -# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif @@ -354,9 +372,9 @@ void free (void *); /* INFRINGES ON USER NAME SPACE */ /* A type that is properly aligned for any stack member. */ union yyalloc { - yytype_int16 yyss; - YYSTYPE yyvs; - }; + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; +}; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) @@ -367,6 +385,27 @@ union yyalloc ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED /* Copy COUNT objects from FROM to TO. The source and destination do not overlap. */ # ifndef YYCOPY @@ -384,42 +423,25 @@ union yyalloc while (YYID (0)) # endif # endif - -/* Relocate STACK from its old location to the new one. The - local variables YYSIZE and YYSTACKSIZE give the old and new number of - elements in the stack, and YYPTR gives the new location of the - stack. Advance YYPTR to a properly aligned location for the next - stack. */ -# define YYSTACK_RELOCATE(Stack) \ - do \ - { \ - YYSIZE_T yynewbytes; \ - YYCOPY (&yyptr->Stack, Stack, yysize); \ - Stack = &yyptr->Stack; \ - yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ - yyptr += yynewbytes / sizeof (*yyptr); \ - } \ - while (YYID (0)) - -#endif +#endif /* !YYCOPY_NEEDED */ /* YYFINAL -- State number of the termination state. */ #define YYFINAL 2 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 554 +#define YYLAST 560 /* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 54 +#define YYNTOKENS 56 /* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 40 +#define YYNNTS 42 /* YYNRULES -- Number of rules. */ -#define YYNRULES 132 +#define YYNRULES 137 /* YYNRULES -- Number of states. */ -#define YYNSTATES 263 +#define YYNSTATES 277 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 -#define YYMAXUTOK 290 +#define YYMAXUTOK 292 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) @@ -430,16 +452,16 @@ static const yytype_uint8 yytranslate[] = 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 52, 12, 5, 2, - 50, 51, 10, 8, 49, 9, 2, 11, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 46, 47, - 6, 48, 7, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 54, 12, 5, 2, + 52, 53, 10, 8, 51, 9, 2, 11, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 48, 49, + 6, 50, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 3, 2, 53, 2, 2, 2, + 2, 2, 2, 2, 3, 2, 55, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -456,7 +478,7 @@ static const yytype_uint8 yytranslate[] = 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, - 45 + 45, 46, 47 }; #if YYDEBUG @@ -467,86 +489,88 @@ static const yytype_uint16 yyprhs[] = 0, 0, 3, 4, 5, 9, 10, 15, 16, 21, 23, 26, 29, 33, 37, 40, 43, 46, 49, 52, 55, 58, 61, 64, 67, 70, 73, 76, 79, 82, - 85, 88, 89, 91, 95, 99, 102, 104, 107, 109, - 112, 114, 118, 124, 128, 134, 137, 139, 141, 143, - 147, 153, 157, 163, 166, 168, 172, 178, 184, 185, - 187, 191, 197, 199, 201, 203, 205, 208, 211, 213, - 215, 217, 219, 224, 227, 230, 232, 234, 236, 238, - 240, 242, 244, 247, 250, 253, 256, 259, 264, 270, - 274, 276, 278, 280, 285, 290, 295, 302, 312, 316, - 320, 326, 335, 337, 344, 350, 358, 359, 362, 365, - 367, 369, 371, 373, 375, 378, 381, 384, 388, 390, - 393, 397, 402, 404, 408, 412, 416, 420, 424, 429, - 434, 438, 442 + 85, 88, 91, 94, 95, 97, 101, 105, 108, 110, + 113, 115, 118, 120, 124, 130, 134, 140, 143, 145, + 147, 149, 153, 159, 163, 169, 172, 174, 178, 184, + 190, 191, 193, 197, 203, 207, 211, 213, 215, 217, + 219, 222, 225, 227, 229, 231, 233, 238, 241, 244, + 246, 248, 250, 252, 254, 256, 258, 261, 264, 267, + 270, 273, 278, 284, 288, 290, 292, 294, 299, 304, + 309, 316, 326, 336, 340, 344, 350, 359, 361, 368, + 374, 382, 383, 386, 389, 391, 393, 395, 397, 399, + 402, 405, 408, 412, 414, 417, 421, 426, 428, 432, + 436, 440, 444, 448, 453, 458, 462, 466 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int8 yyrhs[] = { - 55, 0, -1, -1, -1, 55, 56, 57, -1, -1, - 44, 46, 58, 57, -1, -1, 43, 46, 59, 57, - -1, 47, -1, 60, 47, -1, 1, 47, -1, 43, - 48, 93, -1, 45, 48, 93, -1, 13, 61, -1, - 14, 65, -1, 15, 64, -1, 16, 62, -1, 17, - 63, -1, 21, 66, -1, 19, 67, -1, 22, 68, - -1, 18, 69, -1, 20, 70, -1, 24, 71, -1, - 25, 72, -1, 26, 73, -1, 27, 74, -1, 28, - 75, -1, 29, 76, -1, 23, 77, -1, -1, 49, - -1, 80, 49, 78, -1, 78, 49, 80, -1, 80, - 49, -1, 80, -1, 49, 78, -1, 78, -1, 49, - 81, -1, 81, -1, 84, 49, 81, -1, 88, 11, - 91, 49, 84, -1, 85, 49, 83, -1, 85, 49, - 91, 49, 83, -1, 49, 79, -1, 79, -1, 61, - -1, 65, -1, 80, 49, 78, -1, 80, 49, 78, - 46, 35, -1, 80, 49, 78, -1, 80, 49, 78, - 46, 36, -1, 80, 49, -1, 80, -1, 80, 49, - 78, -1, 82, 49, 78, 49, 91, -1, 84, 49, - 78, 49, 82, -1, -1, 84, -1, 85, 49, 84, - -1, 85, 49, 91, 49, 84, -1, 82, -1, 85, - -1, 81, -1, 87, -1, 10, 82, -1, 10, 86, - -1, 82, -1, 86, -1, 78, -1, 84, -1, 91, - 50, 32, 51, -1, 43, 89, -1, 44, 89, -1, - 34, -1, 37, -1, 35, -1, 38, -1, 42, -1, - 36, -1, 39, -1, 52, 92, -1, 52, 91, -1, - 52, 88, -1, 52, 41, -1, 52, 40, -1, 52, - 50, 40, 51, -1, 52, 50, 9, 40, 51, -1, - 52, 9, 40, -1, 86, -1, 87, -1, 91, -1, - 91, 50, 35, 51, -1, 91, 50, 42, 51, -1, - 91, 50, 36, 51, -1, 91, 50, 35, 10, 91, - 51, -1, 91, 50, 35, 51, 50, 35, 10, 91, - 51, -1, 50, 35, 51, -1, 50, 42, 51, -1, - 50, 35, 10, 91, 51, -1, 50, 35, 51, 50, - 35, 10, 91, 51, -1, 88, -1, 88, 50, 35, - 10, 91, 51, -1, 43, 89, 50, 90, 51, -1, - 43, 6, 7, 89, 50, 33, 51, -1, -1, 8, - 91, -1, 9, 91, -1, 33, -1, 42, -1, 31, - -1, 30, -1, 45, -1, 9, 91, -1, 8, 91, - -1, 53, 91, -1, 50, 93, 51, -1, 30, -1, - 9, 30, -1, 30, 9, 30, -1, 9, 30, 9, - 30, -1, 91, -1, 93, 8, 93, -1, 93, 9, - 93, -1, 93, 10, 93, -1, 93, 11, 93, -1, - 93, 12, 93, -1, 93, 6, 6, 93, -1, 93, - 7, 7, 93, -1, 93, 5, 93, -1, 93, 4, - 93, -1, 93, 3, 93, -1 + 57, 0, -1, -1, -1, 57, 58, 59, -1, -1, + 46, 48, 60, 59, -1, -1, 45, 48, 61, 59, + -1, 49, -1, 62, 49, -1, 1, 49, -1, 45, + 50, 97, -1, 47, 50, 97, -1, 13, 63, -1, + 14, 67, -1, 15, 66, -1, 16, 64, -1, 17, + 65, -1, 21, 68, -1, 19, 69, -1, 22, 70, + -1, 18, 71, -1, 20, 72, -1, 25, 73, -1, + 26, 74, -1, 27, 75, -1, 28, 76, -1, 29, + 77, -1, 30, 78, -1, 23, 79, -1, 24, 80, + -1, 31, 81, -1, -1, 51, -1, 84, 51, 82, + -1, 82, 51, 84, -1, 84, 51, -1, 84, -1, + 51, 82, -1, 82, -1, 51, 85, -1, 85, -1, + 88, 51, 85, -1, 92, 11, 95, 51, 88, -1, + 89, 51, 87, -1, 89, 51, 95, 51, 87, -1, + 51, 83, -1, 83, -1, 63, -1, 67, -1, 84, + 51, 82, -1, 84, 51, 82, 48, 37, -1, 84, + 51, 82, -1, 84, 51, 82, 48, 38, -1, 84, + 51, -1, 84, -1, 84, 51, 82, -1, 86, 51, + 82, 51, 95, -1, 88, 51, 82, 51, 86, -1, + -1, 88, -1, 89, 51, 88, -1, 89, 51, 95, + 51, 88, -1, 84, 51, 84, -1, 84, 51, 84, + -1, 86, -1, 89, -1, 85, -1, 91, -1, 10, + 86, -1, 10, 90, -1, 86, -1, 90, -1, 82, + -1, 88, -1, 95, 52, 34, 53, -1, 45, 93, + -1, 46, 93, -1, 36, -1, 39, -1, 37, -1, + 40, -1, 44, -1, 38, -1, 41, -1, 54, 96, + -1, 54, 95, -1, 54, 92, -1, 54, 43, -1, + 54, 42, -1, 54, 52, 42, 53, -1, 54, 52, + 9, 42, 53, -1, 54, 9, 42, -1, 90, -1, + 91, -1, 95, -1, 95, 52, 37, 53, -1, 95, + 52, 44, 53, -1, 95, 52, 38, 53, -1, 95, + 52, 37, 10, 95, 53, -1, 95, 52, 37, 53, + 52, 37, 10, 95, 53, -1, 95, 52, 37, 53, + 52, 38, 10, 95, 53, -1, 52, 37, 53, -1, + 52, 44, 53, -1, 52, 37, 10, 95, 53, -1, + 52, 37, 53, 52, 37, 10, 95, 53, -1, 92, + -1, 92, 52, 37, 10, 95, 53, -1, 45, 93, + 52, 94, 53, -1, 45, 6, 7, 93, 52, 35, + 53, -1, -1, 8, 95, -1, 9, 95, -1, 35, + -1, 44, -1, 33, -1, 32, -1, 47, -1, 9, + 95, -1, 8, 95, -1, 55, 95, -1, 52, 97, + 53, -1, 32, -1, 9, 32, -1, 32, 9, 32, + -1, 9, 32, 9, 32, -1, 95, -1, 97, 8, + 97, -1, 97, 9, 97, -1, 97, 10, 97, -1, + 97, 11, 97, -1, 97, 12, 97, -1, 97, 6, + 6, 97, -1, 97, 7, 7, 97, -1, 97, 5, + 97, -1, 97, 4, 97, -1, 97, 3, 97, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { - 0, 64, 64, 66, 65, 73, 72, 80, 79, 85, - 86, 87, 90, 95, 101, 102, 103, 104, 105, 106, - 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, - 117, 120, 124, 131, 138, 145, 150, 157, 162, 169, - 174, 179, 186, 194, 199, 207, 212, 219, 220, 223, - 228, 238, 243, 253, 258, 263, 270, 278, 288, 292, - 299, 304, 312, 313, 316, 317, 318, 322, 326, 327, - 330, 331, 334, 340, 349, 358, 363, 368, 373, 378, - 383, 388, 394, 402, 408, 419, 425, 431, 437, 443, - 451, 452, 455, 461, 467, 473, 479, 488, 497, 502, - 507, 515, 525, 529, 538, 545, 554, 557, 561, 567, - 568, 572, 575, 576, 580, 584, 588, 592, 598, 602, - 606, 611, 618, 619, 623, 627, 631, 635, 639, 643, - 647, 651, 655 + 0, 66, 66, 68, 67, 75, 74, 82, 81, 87, + 88, 89, 92, 97, 103, 104, 105, 106, 107, 108, + 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, + 119, 120, 121, 124, 128, 135, 142, 149, 154, 161, + 166, 173, 178, 183, 190, 198, 203, 211, 216, 223, + 224, 227, 232, 242, 247, 257, 262, 267, 274, 282, + 292, 296, 303, 308, 316, 325, 336, 337, 340, 341, + 342, 346, 350, 351, 354, 355, 358, 364, 373, 382, + 387, 392, 397, 402, 407, 412, 418, 426, 432, 443, + 449, 455, 461, 467, 475, 476, 479, 485, 491, 497, + 503, 512, 521, 530, 535, 540, 548, 558, 562, 571, + 578, 587, 590, 594, 600, 601, 605, 608, 609, 613, + 617, 621, 625, 631, 636, 641, 646, 653, 654, 658, + 662, 666, 670, 674, 678, 682, 686, 690 }; #endif @@ -558,15 +582,16 @@ static const char *const yytname[] = "$end", "error", "$undefined", "'|'", "'^'", "'&'", "'<'", "'>'", "'+'", "'-'", "'*'", "'/'", "'%'", "LTYPE0", "LTYPE1", "LTYPE2", "LTYPE3", "LTYPE4", "LTYPEC", "LTYPED", "LTYPEN", "LTYPER", "LTYPET", "LTYPEG", - "LTYPES", "LTYPEM", "LTYPEI", "LTYPEXC", "LTYPEX", "LTYPERT", "LCONST", - "LFP", "LPC", "LSB", "LBREG", "LLREG", "LSREG", "LFREG", "LMREG", - "LXREG", "LFCONST", "LSCONST", "LSP", "LNAME", "LLAB", "LVAR", "':'", - "';'", "'='", "','", "'('", "')'", "'$'", "'~'", "$accept", "prog", "@1", - "line", "@2", "@3", "inst", "nonnon", "rimrem", "remrim", "rimnon", - "nonrem", "nonrel", "spec1", "spec2", "spec3", "spec4", "spec5", "spec6", - "spec7", "spec8", "spec9", "spec10", "spec11", "rem", "rom", "rim", - "rel", "reg", "imm2", "imm", "mem", "omem", "nmem", "nam", "offset", - "pointer", "con", "con2", "expr", 0 + "LTYPEPC", "LTYPES", "LTYPEM", "LTYPEI", "LTYPEXC", "LTYPEX", "LTYPERT", + "LTYPEF", "LCONST", "LFP", "LPC", "LSB", "LBREG", "LLREG", "LSREG", + "LFREG", "LMREG", "LXREG", "LFCONST", "LSCONST", "LSP", "LNAME", "LLAB", + "LVAR", "':'", "';'", "'='", "','", "'('", "')'", "'$'", "'~'", + "$accept", "prog", "$@1", "line", "$@2", "$@3", "inst", "nonnon", + "rimrem", "remrim", "rimnon", "nonrem", "nonrel", "spec1", "spec2", + "spec3", "spec4", "spec5", "spec6", "spec7", "spec8", "spec9", "spec10", + "spec11", "spec12", "spec13", "rem", "rom", "rim", "rel", "reg", "imm2", + "imm", "mem", "omem", "nmem", "nam", "offset", "pointer", "con", "con2", + "expr", 0 }; #endif @@ -579,28 +604,28 @@ static const yytype_uint16 yytoknum[] = 42, 47, 37, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, - 285, 286, 287, 288, 289, 290, 58, 59, 61, 44, - 40, 41, 36, 126 + 285, 286, 287, 288, 289, 290, 291, 292, 58, 59, + 61, 44, 40, 41, 36, 126 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { - 0, 54, 55, 56, 55, 58, 57, 59, 57, 57, - 57, 57, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 61, 61, 62, 63, 64, 64, 65, 65, 66, - 66, 66, 67, 68, 68, 69, 69, 70, 70, 71, - 71, 72, 72, 73, 73, 73, 74, 75, 76, 76, - 77, 77, 78, 78, 79, 79, 79, 79, 79, 79, - 80, 80, 81, 81, 81, 82, 82, 82, 82, 82, - 82, 82, 83, 84, 84, 84, 84, 84, 84, 84, - 85, 85, 86, 86, 86, 86, 86, 86, 86, 86, - 86, 86, 87, 87, 88, 88, 89, 89, 89, 90, - 90, 90, 91, 91, 91, 91, 91, 91, 92, 92, - 92, 92, 93, 93, 93, 93, 93, 93, 93, 93, - 93, 93, 93 + 0, 56, 57, 58, 57, 60, 59, 61, 59, 59, + 59, 59, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 63, 63, 64, 65, 66, 66, 67, + 67, 68, 68, 68, 69, 70, 70, 71, 71, 72, + 72, 73, 73, 74, 74, 75, 75, 75, 76, 77, + 78, 78, 79, 79, 80, 81, 82, 82, 83, 83, + 83, 83, 83, 83, 84, 84, 85, 85, 85, 86, + 86, 86, 86, 86, 86, 86, 87, 88, 88, 88, + 88, 88, 88, 88, 89, 89, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 91, 91, 92, + 92, 93, 93, 93, 94, 94, 94, 95, 95, 95, + 95, 95, 95, 96, 96, 96, 96, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ @@ -609,261 +634,273 @@ static const yytype_uint8 yyr2[] = 0, 2, 0, 0, 3, 0, 4, 0, 4, 1, 2, 2, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 0, 1, 3, 3, 2, 1, 2, 1, 2, - 1, 3, 5, 3, 5, 2, 1, 1, 1, 3, - 5, 3, 5, 2, 1, 3, 5, 5, 0, 1, - 3, 5, 1, 1, 1, 1, 2, 2, 1, 1, - 1, 1, 4, 2, 2, 1, 1, 1, 1, 1, - 1, 1, 2, 2, 2, 2, 2, 4, 5, 3, - 1, 1, 1, 4, 4, 4, 6, 9, 3, 3, - 5, 8, 1, 6, 5, 7, 0, 2, 2, 1, - 1, 1, 1, 1, 2, 2, 2, 3, 1, 2, - 3, 4, 1, 3, 3, 3, 3, 3, 4, 4, - 3, 3, 3 + 2, 2, 2, 0, 1, 3, 3, 2, 1, 2, + 1, 2, 1, 3, 5, 3, 5, 2, 1, 1, + 1, 3, 5, 3, 5, 2, 1, 3, 5, 5, + 0, 1, 3, 5, 3, 3, 1, 1, 1, 1, + 2, 2, 1, 1, 1, 1, 4, 2, 2, 1, + 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 4, 5, 3, 1, 1, 1, 4, 4, 4, + 6, 9, 9, 3, 3, 5, 8, 1, 6, 5, + 7, 0, 2, 2, 1, 1, 1, 1, 1, 2, + 2, 2, 3, 1, 2, 3, 4, 1, 3, 3, + 3, 3, 3, 4, 4, 3, 3, 3 }; -/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state - STATE-NUM when YYTABLE doesn't specify something else to do. Zero +/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { - 2, 3, 1, 0, 0, 31, 0, 0, 0, 0, - 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, - 0, 58, 0, 0, 0, 9, 4, 0, 11, 32, - 14, 0, 0, 112, 75, 77, 80, 76, 78, 81, - 79, 106, 113, 0, 0, 0, 15, 38, 62, 63, - 90, 91, 102, 92, 0, 16, 70, 36, 71, 17, - 0, 18, 0, 0, 106, 106, 0, 22, 46, 64, - 68, 69, 65, 92, 20, 0, 32, 47, 48, 23, - 106, 0, 0, 19, 40, 0, 0, 21, 0, 30, - 0, 24, 0, 25, 0, 26, 54, 27, 0, 28, - 0, 29, 59, 7, 0, 5, 0, 10, 115, 114, - 0, 0, 0, 0, 37, 0, 0, 122, 0, 116, - 0, 0, 0, 86, 85, 0, 84, 83, 35, 0, - 0, 66, 67, 73, 74, 45, 0, 0, 73, 39, - 0, 0, 0, 0, 0, 0, 53, 0, 0, 0, - 12, 0, 13, 106, 107, 108, 0, 0, 98, 99, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 117, 0, 0, 0, 0, 89, 0, 0, 33, 34, - 0, 0, 41, 0, 43, 0, 60, 0, 49, 51, - 55, 0, 0, 8, 6, 0, 111, 109, 110, 0, - 0, 0, 132, 131, 130, 0, 0, 123, 124, 125, - 126, 127, 0, 0, 93, 95, 94, 0, 87, 72, - 0, 0, 118, 82, 0, 0, 0, 0, 0, 0, - 0, 104, 100, 0, 128, 129, 0, 0, 0, 88, - 42, 119, 0, 44, 61, 50, 52, 56, 57, 0, - 0, 103, 96, 0, 0, 120, 105, 0, 0, 121, - 101, 0, 97 + 2, 3, 1, 0, 0, 33, 0, 0, 0, 0, + 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 60, 0, 0, 0, 0, 9, 4, 0, + 11, 34, 14, 0, 0, 117, 79, 81, 84, 80, + 82, 85, 83, 111, 118, 0, 0, 0, 15, 40, + 66, 67, 94, 95, 107, 96, 0, 16, 74, 38, + 75, 17, 0, 18, 0, 0, 111, 111, 0, 22, + 48, 68, 72, 73, 69, 96, 20, 0, 34, 49, + 50, 23, 111, 0, 0, 19, 42, 0, 0, 21, + 0, 30, 0, 31, 0, 24, 0, 25, 0, 26, + 56, 27, 0, 28, 0, 29, 61, 32, 0, 7, + 0, 5, 0, 10, 120, 119, 0, 0, 0, 0, + 39, 0, 0, 127, 0, 121, 0, 0, 0, 90, + 89, 0, 88, 87, 37, 0, 0, 70, 71, 77, + 78, 47, 0, 0, 77, 41, 0, 0, 0, 0, + 0, 0, 0, 55, 0, 0, 0, 0, 12, 0, + 13, 111, 112, 113, 0, 0, 103, 104, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 122, 0, + 0, 0, 0, 93, 0, 0, 35, 36, 0, 0, + 43, 0, 45, 0, 62, 0, 64, 51, 53, 57, + 0, 0, 65, 8, 6, 0, 116, 114, 115, 0, + 0, 0, 137, 136, 135, 0, 0, 128, 129, 130, + 131, 132, 0, 0, 97, 99, 98, 0, 91, 76, + 0, 0, 123, 86, 0, 0, 0, 0, 0, 0, + 0, 109, 105, 0, 133, 134, 0, 0, 0, 92, + 44, 124, 0, 46, 63, 52, 54, 58, 59, 0, + 0, 108, 100, 0, 0, 0, 125, 110, 0, 0, + 0, 126, 106, 0, 0, 101, 102 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { - -1, 1, 3, 26, 151, 149, 27, 30, 59, 61, - 55, 46, 83, 74, 87, 67, 79, 91, 93, 95, - 97, 99, 101, 89, 56, 68, 57, 69, 48, 184, - 58, 49, 50, 51, 52, 113, 199, 53, 223, 118 + -1, 1, 3, 28, 159, 157, 29, 32, 61, 63, + 57, 48, 85, 76, 89, 69, 81, 95, 97, 99, + 101, 103, 105, 91, 93, 107, 58, 70, 59, 71, + 50, 192, 60, 51, 52, 53, 54, 119, 209, 55, + 233, 124 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ -#define YYPACT_NINF -97 +#define YYPACT_NINF -94 static const yytype_int16 yypact[] = { - -97, 40, -97, 208, 5, -4, 140, 295, 295, 343, - 233, 15, 319, 381, 91, 91, 295, 295, 295, 222, - 24, 24, -15, 22, 14, -97, -97, 30, -97, -97, - -97, 486, 486, -97, -97, -97, -97, -97, -97, -97, - -97, 42, -97, 343, 406, 486, -97, -97, -97, -97, - -97, -97, 45, 48, 399, -97, -97, 20, -97, -97, - 67, -97, 68, 367, 42, 19, 271, -97, -97, -97, - -97, -97, -97, 87, -97, 127, 343, -97, -97, -97, - 19, 437, 486, -97, -97, 90, 92, -97, 94, -97, - 96, -97, 104, -97, 105, -97, 111, -97, 118, -97, - 119, -97, -97, -97, 486, -97, 486, -97, -97, -97, - 133, 486, 486, 121, -97, 8, 122, -97, 80, -97, - 134, 78, 413, -97, -97, 446, -97, -97, -97, 343, - 295, -97, -97, 121, -97, -97, 7, 486, -97, -97, - 437, 148, 453, 462, 343, 343, 343, 343, 343, 208, - 284, 208, 284, 19, -97, -97, -1, 486, 131, -97, - 486, 486, 486, 166, 177, 486, 486, 486, 486, 486, - -97, 176, 10, 136, 137, -97, 480, 141, -97, -97, - 143, 142, -97, 16, -97, 149, -97, 150, 151, 154, - -97, 152, 155, -97, -97, 156, -97, -97, -97, 157, - 159, 170, 99, 535, 542, 486, 486, 26, 26, -97, - -97, -97, 486, 486, 161, -97, -97, 162, -97, -97, - 24, 184, 198, -97, 163, 24, 181, 183, 486, 222, - 205, -97, -97, 229, 114, 114, 193, 194, 211, -97, - -97, 238, 219, -97, -97, -97, -97, -97, -97, 199, - 486, -97, -97, 244, 232, -97, -97, 214, 486, -97, - -97, 215, -97 + -94, 15, -94, 218, -28, -25, 264, 285, 285, 340, + 163, 2, 319, 97, 415, 415, 285, 285, 285, 285, + 306, -24, -24, 285, -17, -14, 4, -94, -94, 48, + -94, -94, -94, 481, 481, -94, -94, -94, -94, -94, + -94, -94, -94, 19, -94, 340, 399, 481, -94, -94, + -94, -94, -94, -94, 46, 47, 385, -94, -94, 52, + -94, -94, 59, -94, 60, 374, 19, 56, 243, -94, + -94, -94, -94, -94, -94, 63, -94, 106, 340, -94, + -94, -94, 56, 138, 481, -94, -94, 69, 72, -94, + 74, -94, 76, -94, 77, -94, 79, -94, 80, -94, + 81, -94, 83, -94, 89, -94, -94, -94, 94, -94, + 481, -94, 481, -94, -94, -94, 119, 481, 481, 98, + -94, -1, 100, -94, 84, -94, 117, 23, 426, -94, + -94, 433, -94, -94, -94, 340, 285, -94, -94, 98, + -94, -94, 75, 481, -94, -94, 138, 122, 440, 444, + 285, 340, 340, 340, 340, 340, 285, 218, 393, 218, + 393, 56, -94, -94, -15, 481, 105, -94, 481, 481, + 481, 156, 162, 481, 481, 481, 481, 481, -94, 165, + 0, 123, 133, -94, 474, 134, -94, -94, 136, 140, + -94, 7, -94, 141, -94, 143, -94, 148, 149, -94, + 147, 160, -94, -94, -94, 164, -94, -94, -94, 167, + 168, 180, 533, 541, 548, 481, 481, 58, 58, -94, + -94, -94, 481, 481, 171, -94, -94, 172, -94, -94, + -24, 192, 217, -94, 175, -24, 219, 216, 481, 306, + 220, -94, -94, 247, 33, 33, 205, 208, 41, -94, + -94, 253, 234, -94, -94, -94, -94, -94, -94, 215, + 481, -94, -94, 259, 260, 239, -94, -94, 221, 481, + 481, -94, -94, 223, 224, -94, -94 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { - -97, -97, -97, -96, -97, -97, -97, 261, -97, -97, - -97, 262, -97, -97, -97, -97, -97, -97, -97, -97, - -97, -97, -97, -97, 17, 218, -2, -11, -9, 61, - -8, 51, 1, -3, -7, -56, -97, -10, -97, -87 + -94, -94, -94, -43, -94, -94, -94, 266, -94, -94, + -94, 273, -94, -94, -94, -94, -94, -94, -94, -94, + -94, -94, -94, -94, -94, -94, 26, 229, 32, -11, + -9, 57, -8, 71, -2, -6, 1, -60, -94, -10, + -94, -93 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which - number is the opposite. If zero, do what YYDEFACT says. - If YYTABLE_NINF, syntax error. */ + number is the opposite. If YYTABLE_NINF, syntax error. */ #define YYTABLE_NINF -1 static const yytype_uint16 yytable[] = { - 73, 70, 84, 86, 75, 85, 60, 72, 133, 134, - 98, 71, 100, 102, 92, 94, 96, 150, 157, 152, - 213, 108, 109, 47, 138, 221, 62, 111, 112, 47, - 196, 103, 197, 104, 117, 119, 167, 168, 169, 180, - 2, 198, 172, 173, 127, 29, 222, 126, 110, 174, - 111, 112, 28, 193, 131, 194, 73, 70, 41, 158, - 114, 214, 106, 72, 132, 88, 90, 71, 105, 128, - 139, 86, 117, 202, 203, 204, 54, 107, 207, 208, - 209, 210, 211, 160, 161, 162, 163, 164, 165, 166, - 167, 168, 169, 114, 117, 120, 117, 195, 121, 31, - 32, 154, 155, 161, 162, 163, 164, 165, 166, 167, - 168, 169, 109, 172, 173, 117, 129, 130, 234, 235, - 174, 33, 165, 166, 167, 168, 169, 181, 179, 182, - 86, 170, 185, 187, 41, 186, 42, 136, 137, 140, - 153, 44, 141, 142, 45, 143, 178, 200, 31, 32, - 117, 117, 117, 144, 145, 117, 117, 117, 117, 117, - 146, 188, 189, 190, 191, 192, 109, 147, 148, 171, - 33, 156, 205, 159, 34, 35, 36, 37, 38, 39, - 180, 201, 40, 41, 206, 42, 212, 215, 216, 43, - 44, 220, 218, 45, 219, 117, 117, 226, 224, 225, - 227, 228, 236, 237, 229, 233, 230, 242, 231, 4, - 232, 238, 240, 239, 241, 183, 245, 244, 247, 246, - 248, 5, 6, 7, 8, 9, 10, 11, 12, 13, - 14, 15, 16, 17, 18, 19, 20, 21, 249, 250, - 257, 31, 32, 63, 251, 252, 253, 254, 261, 255, - 256, 22, 23, 24, 258, 25, 34, 35, 36, 37, - 38, 39, 259, 33, 40, 260, 262, 34, 35, 36, - 37, 38, 39, 77, 78, 40, 64, 65, 42, 31, - 32, 63, 66, 44, 135, 243, 45, 160, 161, 162, - 163, 164, 165, 166, 167, 168, 169, 0, 0, 0, - 0, 33, 0, 31, 32, 34, 35, 36, 37, 38, - 39, 0, 0, 40, 64, 65, 42, 0, 0, 0, - 0, 44, 0, 0, 45, 33, 0, 31, 32, 34, - 35, 36, 37, 38, 39, 0, 0, 40, 41, 0, - 42, 0, 0, 0, 0, 44, 0, 54, 45, 33, - 0, 31, 32, 34, 35, 36, 37, 38, 39, 0, - 0, 40, 41, 0, 42, 0, 0, 0, 76, 44, - 0, 0, 45, 33, 0, 31, 32, 34, 35, 36, - 37, 38, 39, 0, 0, 40, 41, 0, 42, 31, - 32, 0, 0, 44, 0, 0, 45, 33, 0, 0, - 0, 34, 35, 36, 37, 38, 39, 31, 122, 40, - 0, 33, 42, 0, 31, 32, 0, 44, 0, 0, - 45, 31, 32, 0, 80, 65, 42, 0, 0, 33, - 81, 82, 0, 54, 45, 0, 33, 0, 0, 123, - 124, 115, 41, 33, 42, 31, 32, 0, 116, 125, - 0, 42, 45, 175, 31, 176, 82, 0, 42, 45, - 0, 31, 32, 82, 0, 0, 45, 33, 0, 0, - 31, 32, 0, 0, 0, 0, 33, 0, 0, 0, - 80, 65, 42, 33, 0, 0, 177, 82, 31, 32, - 45, 42, 33, 0, 31, 32, 82, 0, 42, 45, - 0, 0, 0, 82, 0, 183, 45, 42, 0, 0, - 33, 0, 82, 0, 54, 45, 33, 0, 0, 0, - 217, 0, 0, 0, 0, 42, 0, 0, 0, 0, - 82, 42, 0, 45, 0, 0, 82, 0, 0, 45, - 162, 163, 164, 165, 166, 167, 168, 169, 163, 164, - 165, 166, 167, 168, 169 + 75, 72, 86, 88, 74, 87, 139, 140, 73, 165, + 223, 102, 77, 104, 106, 2, 231, 158, 206, 160, + 207, 30, 144, 114, 115, 116, 31, 117, 118, 208, + 56, 109, 49, 110, 111, 64, 123, 125, 49, 232, + 62, 173, 174, 175, 176, 177, 133, 43, 94, 96, + 98, 100, 166, 224, 112, 108, 137, 132, 75, 72, + 180, 181, 74, 138, 117, 118, 73, 182, 175, 176, + 177, 120, 145, 88, 123, 212, 213, 214, 263, 264, + 217, 218, 219, 220, 221, 90, 92, 168, 169, 170, + 171, 172, 173, 174, 175, 176, 177, 113, 126, 127, + 123, 205, 123, 134, 120, 33, 34, 162, 163, 188, + 135, 136, 180, 181, 203, 142, 204, 143, 115, 182, + 146, 123, 244, 245, 147, 148, 161, 149, 150, 35, + 151, 152, 153, 189, 154, 190, 88, 178, 193, 195, + 155, 194, 82, 67, 44, 156, 33, 34, 83, 84, + 164, 56, 47, 167, 179, 210, 188, 211, 123, 123, + 123, 186, 215, 123, 123, 123, 123, 123, 187, 216, + 35, 33, 34, 65, 115, 222, 225, 197, 198, 199, + 200, 201, 196, 82, 67, 44, 226, 228, 202, 229, + 84, 230, 234, 47, 235, 35, 236, 237, 238, 36, + 37, 38, 39, 40, 41, 123, 123, 42, 66, 67, + 44, 239, 246, 247, 68, 46, 240, 243, 47, 4, + 241, 242, 250, 248, 251, 249, 252, 254, 257, 191, + 258, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 268, 33, 34, 65, 256, 259, 255, 260, 261, 273, + 274, 262, 265, 24, 25, 26, 266, 27, 267, 269, + 270, 271, 33, 34, 272, 35, 275, 276, 79, 36, + 37, 38, 39, 40, 41, 80, 0, 42, 66, 67, + 44, 253, 0, 33, 34, 46, 35, 141, 47, 0, + 36, 37, 38, 39, 40, 41, 0, 0, 42, 43, + 0, 44, 0, 0, 0, 45, 46, 35, 0, 47, + 0, 36, 37, 38, 39, 40, 41, 33, 34, 42, + 43, 0, 44, 0, 0, 0, 0, 46, 0, 56, + 47, 0, 36, 37, 38, 39, 40, 41, 33, 34, + 42, 35, 0, 0, 0, 36, 37, 38, 39, 40, + 41, 0, 0, 42, 43, 0, 44, 0, 0, 0, + 78, 46, 35, 0, 47, 0, 36, 37, 38, 39, + 40, 41, 33, 34, 42, 43, 0, 44, 0, 0, + 0, 0, 46, 33, 128, 47, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 177, 35, 33, 34, 0, + 36, 37, 38, 39, 40, 41, 0, 35, 42, 0, + 0, 44, 0, 33, 34, 0, 46, 129, 130, 47, + 43, 35, 44, 0, 33, 34, 121, 131, 0, 0, + 47, 33, 184, 122, 0, 0, 44, 35, 33, 34, + 0, 84, 33, 34, 47, 0, 0, 0, 35, 0, + 43, 0, 44, 0, 0, 35, 0, 46, 183, 0, + 47, 0, 35, 44, 0, 185, 35, 0, 84, 0, + 44, 47, 33, 34, 0, 84, 0, 44, 47, 33, + 34, 44, 84, 0, 191, 47, 84, 0, 56, 47, + 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, + 0, 0, 0, 35, 0, 0, 227, 0, 0, 0, + 0, 44, 0, 0, 0, 0, 84, 0, 44, 47, + 0, 0, 0, 84, 0, 0, 47, 169, 170, 171, + 172, 173, 174, 175, 176, 177, 170, 171, 172, 173, + 174, 175, 176, 177, 171, 172, 173, 174, 175, 176, + 177 }; +#define yypact_value_is_default(yystate) \ + ((yystate) == (-94)) + +#define yytable_value_is_error(yytable_value) \ + YYID (0) + static const yytype_int16 yycheck[] = { - 10, 10, 13, 13, 11, 13, 8, 10, 64, 65, - 19, 10, 20, 21, 16, 17, 18, 104, 10, 106, - 10, 31, 32, 6, 80, 9, 9, 8, 9, 12, - 31, 46, 33, 48, 44, 45, 10, 11, 12, 32, - 0, 42, 35, 36, 54, 49, 30, 54, 6, 42, - 8, 9, 47, 149, 63, 151, 66, 66, 43, 51, - 43, 51, 48, 66, 63, 14, 15, 66, 46, 49, - 81, 81, 82, 160, 161, 162, 52, 47, 165, 166, - 167, 168, 169, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 76, 104, 50, 106, 153, 50, 8, - 9, 111, 112, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 122, 35, 36, 125, 49, 49, 205, 206, - 42, 30, 8, 9, 10, 11, 12, 137, 130, 140, - 140, 51, 142, 143, 43, 143, 45, 50, 11, 49, - 7, 50, 50, 49, 53, 49, 129, 157, 8, 9, - 160, 161, 162, 49, 49, 165, 166, 167, 168, 169, - 49, 144, 145, 146, 147, 148, 176, 49, 49, 35, - 30, 50, 6, 51, 34, 35, 36, 37, 38, 39, - 32, 50, 42, 43, 7, 45, 10, 51, 51, 49, - 50, 49, 51, 53, 51, 205, 206, 46, 49, 49, - 46, 49, 212, 213, 49, 35, 50, 9, 51, 1, - 51, 50, 220, 51, 30, 52, 35, 225, 228, 36, - 229, 13, 14, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 24, 25, 26, 27, 28, 29, 33, 10, - 250, 8, 9, 10, 51, 51, 35, 9, 258, 30, - 51, 43, 44, 45, 10, 47, 34, 35, 36, 37, - 38, 39, 30, 30, 42, 51, 51, 34, 35, 36, - 37, 38, 39, 12, 12, 42, 43, 44, 45, 8, - 9, 10, 49, 50, 66, 224, 53, 3, 4, 5, - 6, 7, 8, 9, 10, 11, 12, -1, -1, -1, - -1, 30, -1, 8, 9, 34, 35, 36, 37, 38, - 39, -1, -1, 42, 43, 44, 45, -1, -1, -1, - -1, 50, -1, -1, 53, 30, -1, 8, 9, 34, - 35, 36, 37, 38, 39, -1, -1, 42, 43, -1, - 45, -1, -1, -1, -1, 50, -1, 52, 53, 30, - -1, 8, 9, 34, 35, 36, 37, 38, 39, -1, - -1, 42, 43, -1, 45, -1, -1, -1, 49, 50, - -1, -1, 53, 30, -1, 8, 9, 34, 35, 36, - 37, 38, 39, -1, -1, 42, 43, -1, 45, 8, - 9, -1, -1, 50, -1, -1, 53, 30, -1, -1, - -1, 34, 35, 36, 37, 38, 39, 8, 9, 42, - -1, 30, 45, -1, 8, 9, -1, 50, -1, -1, - 53, 8, 9, -1, 43, 44, 45, -1, -1, 30, - 49, 50, -1, 52, 53, -1, 30, -1, -1, 40, - 41, 35, 43, 30, 45, 8, 9, -1, 42, 50, - -1, 45, 53, 40, 8, 9, 50, -1, 45, 53, - -1, 8, 9, 50, -1, -1, 53, 30, -1, -1, - 8, 9, -1, -1, -1, -1, 30, -1, -1, -1, - 43, 44, 45, 30, -1, -1, 40, 50, 8, 9, - 53, 45, 30, -1, 8, 9, 50, -1, 45, 53, - -1, -1, -1, 50, -1, 52, 53, 45, -1, -1, - 30, -1, 50, -1, 52, 53, 30, -1, -1, -1, - 40, -1, -1, -1, -1, 45, -1, -1, -1, -1, - 50, 45, -1, 53, -1, -1, 50, -1, -1, 53, - 5, 6, 7, 8, 9, 10, 11, 12, 6, 7, - 8, 9, 10, 11, 12 + 10, 10, 13, 13, 10, 13, 66, 67, 10, 10, + 10, 20, 11, 21, 22, 0, 9, 110, 33, 112, + 35, 49, 82, 33, 34, 6, 51, 8, 9, 44, + 54, 48, 6, 50, 48, 9, 46, 47, 12, 32, + 8, 8, 9, 10, 11, 12, 56, 45, 16, 17, + 18, 19, 53, 53, 50, 23, 65, 56, 68, 68, + 37, 38, 68, 65, 8, 9, 68, 44, 10, 11, + 12, 45, 83, 83, 84, 168, 169, 170, 37, 38, + 173, 174, 175, 176, 177, 14, 15, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 49, 52, 52, + 110, 161, 112, 51, 78, 8, 9, 117, 118, 34, + 51, 51, 37, 38, 157, 52, 159, 11, 128, 44, + 51, 131, 215, 216, 52, 51, 7, 51, 51, 32, + 51, 51, 51, 143, 51, 146, 146, 53, 148, 149, + 51, 149, 45, 46, 47, 51, 8, 9, 51, 52, + 52, 54, 55, 53, 37, 165, 34, 52, 168, 169, + 170, 135, 6, 173, 174, 175, 176, 177, 136, 7, + 32, 8, 9, 10, 184, 10, 53, 151, 152, 153, + 154, 155, 150, 45, 46, 47, 53, 53, 156, 53, + 52, 51, 51, 55, 51, 32, 48, 48, 51, 36, + 37, 38, 39, 40, 41, 215, 216, 44, 45, 46, + 47, 51, 222, 223, 51, 52, 52, 37, 55, 1, + 53, 53, 230, 52, 32, 53, 9, 235, 238, 54, + 239, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 260, 8, 9, 10, 38, 35, 37, 10, 53, 269, + 270, 53, 9, 45, 46, 47, 32, 49, 53, 10, + 10, 32, 8, 9, 53, 32, 53, 53, 12, 36, + 37, 38, 39, 40, 41, 12, -1, 44, 45, 46, + 47, 234, -1, 8, 9, 52, 32, 68, 55, -1, + 36, 37, 38, 39, 40, 41, -1, -1, 44, 45, + -1, 47, -1, -1, -1, 51, 52, 32, -1, 55, + -1, 36, 37, 38, 39, 40, 41, 8, 9, 44, + 45, -1, 47, -1, -1, -1, -1, 52, -1, 54, + 55, -1, 36, 37, 38, 39, 40, 41, 8, 9, + 44, 32, -1, -1, -1, 36, 37, 38, 39, 40, + 41, -1, -1, 44, 45, -1, 47, -1, -1, -1, + 51, 52, 32, -1, 55, -1, 36, 37, 38, 39, + 40, 41, 8, 9, 44, 45, -1, 47, -1, -1, + -1, -1, 52, 8, 9, 55, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 32, 8, 9, -1, + 36, 37, 38, 39, 40, 41, -1, 32, 44, -1, + -1, 47, -1, 8, 9, -1, 52, 42, 43, 55, + 45, 32, 47, -1, 8, 9, 37, 52, -1, -1, + 55, 8, 9, 44, -1, -1, 47, 32, 8, 9, + -1, 52, 8, 9, 55, -1, -1, -1, 32, -1, + 45, -1, 47, -1, -1, 32, -1, 52, 42, -1, + 55, -1, 32, 47, -1, 42, 32, -1, 52, -1, + 47, 55, 8, 9, -1, 52, -1, 47, 55, 8, + 9, 47, 52, -1, 54, 55, 52, -1, 54, 55, + -1, -1, -1, -1, -1, -1, 32, -1, -1, -1, + -1, -1, -1, 32, -1, -1, 42, -1, -1, -1, + -1, 47, -1, -1, -1, -1, 52, -1, 47, 55, + -1, -1, -1, 52, -1, -1, 55, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 5, 6, 7, 8, + 9, 10, 11, 12, 6, 7, 8, 9, 10, 11, + 12 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { - 0, 55, 0, 56, 1, 13, 14, 15, 16, 17, + 0, 57, 0, 58, 1, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, - 28, 29, 43, 44, 45, 47, 57, 60, 47, 49, - 61, 8, 9, 30, 34, 35, 36, 37, 38, 39, - 42, 43, 45, 49, 50, 53, 65, 78, 82, 85, - 86, 87, 88, 91, 52, 64, 78, 80, 84, 62, - 80, 63, 78, 10, 43, 44, 49, 69, 79, 81, - 82, 86, 87, 91, 67, 88, 49, 61, 65, 70, - 43, 49, 50, 66, 81, 84, 91, 68, 85, 77, - 85, 71, 80, 72, 80, 73, 80, 74, 82, 75, - 84, 76, 84, 46, 48, 46, 48, 47, 91, 91, - 6, 8, 9, 89, 78, 35, 42, 91, 93, 91, - 50, 50, 9, 40, 41, 50, 88, 91, 49, 49, - 49, 82, 86, 89, 89, 79, 50, 11, 89, 81, - 49, 50, 49, 49, 49, 49, 49, 49, 49, 59, - 93, 58, 93, 7, 91, 91, 50, 10, 51, 51, - 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, - 51, 35, 35, 36, 42, 40, 9, 40, 78, 80, - 32, 91, 81, 52, 83, 91, 84, 91, 78, 78, - 78, 78, 78, 57, 57, 89, 31, 33, 42, 90, - 91, 50, 93, 93, 93, 6, 7, 93, 93, 93, - 93, 93, 10, 10, 51, 51, 51, 40, 51, 51, - 49, 9, 30, 92, 49, 49, 46, 46, 49, 49, - 50, 51, 51, 35, 93, 93, 91, 91, 50, 51, - 84, 30, 9, 83, 84, 35, 36, 91, 82, 33, - 10, 51, 51, 35, 9, 30, 51, 91, 10, 30, - 51, 91, 51 + 28, 29, 30, 31, 45, 46, 47, 49, 59, 62, + 49, 51, 63, 8, 9, 32, 36, 37, 38, 39, + 40, 41, 44, 45, 47, 51, 52, 55, 67, 82, + 86, 89, 90, 91, 92, 95, 54, 66, 82, 84, + 88, 64, 84, 65, 82, 10, 45, 46, 51, 71, + 83, 85, 86, 90, 91, 95, 69, 92, 51, 63, + 67, 72, 45, 51, 52, 68, 85, 88, 95, 70, + 89, 79, 89, 80, 84, 73, 84, 74, 84, 75, + 84, 76, 86, 77, 88, 78, 88, 81, 84, 48, + 50, 48, 50, 49, 95, 95, 6, 8, 9, 93, + 82, 37, 44, 95, 97, 95, 52, 52, 9, 42, + 43, 52, 92, 95, 51, 51, 51, 86, 90, 93, + 93, 83, 52, 11, 93, 85, 51, 52, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 61, 97, 60, + 97, 7, 95, 95, 52, 10, 53, 53, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 53, 37, + 37, 38, 44, 42, 9, 42, 82, 84, 34, 95, + 85, 54, 87, 95, 88, 95, 84, 82, 82, 82, + 82, 82, 84, 59, 59, 93, 33, 35, 44, 94, + 95, 52, 97, 97, 97, 6, 7, 97, 97, 97, + 97, 97, 10, 10, 53, 53, 53, 42, 53, 53, + 51, 9, 32, 96, 51, 51, 48, 48, 51, 51, + 52, 53, 53, 37, 97, 97, 95, 95, 52, 53, + 88, 32, 9, 87, 88, 37, 38, 95, 86, 35, + 10, 53, 53, 37, 38, 9, 32, 53, 95, 10, + 10, 32, 53, 95, 95, 53, 53 }; #define yyerrok (yyerrstatus = 0) @@ -878,9 +915,18 @@ static const yytype_uint8 yystos[] = /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. - Once GCC version 2 has supplanted version 1, this can go. */ + Once GCC version 2 has supplanted version 1, this can go. However, + YYFAIL appears to be in use. Nevertheless, it is formally deprecated + in Bison 2.4.2's NEWS entry, where a plan to phase it out is + discussed. */ #define YYFAIL goto yyerrlab +#if defined YYFAIL + /* This is here to suppress warnings from the GCC cpp's + -Wunused-macros. Normally we don't worry about that warning, but + some users do, and we want to make it easy for users to remove + YYFAIL uses, which will produce warnings from Bison 2.5. */ +#endif #define YYRECOVERING() (!!yyerrstatus) @@ -890,7 +936,6 @@ do \ { \ yychar = (Token); \ yylval = (Value); \ - yytoken = YYTRANSLATE (yychar); \ YYPOPSTACK (1); \ goto yybackup; \ } \ @@ -932,19 +977,10 @@ while (YYID (0)) #endif -/* YY_LOCATION_PRINT -- Print the location on the stream. - This macro was not mandated originally: define only if we know - we won't break user code: when these are the locations we know. */ +/* This macro is provided for backward compatibility. */ #ifndef YY_LOCATION_PRINT -# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL -# define YY_LOCATION_PRINT(File, Loc) \ - fprintf (File, "%d.%d-%d.%d", \ - (Loc).first_line, (Loc).first_column, \ - (Loc).last_line, (Loc).last_column) -# else -# define YY_LOCATION_PRINT(File, Loc) ((void) 0) -# endif +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) #endif @@ -1048,17 +1084,20 @@ yy_symbol_print (yyoutput, yytype, yyvaluep) #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void -yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) #else static void -yy_stack_print (bottom, top) - yytype_int16 *bottom; - yytype_int16 *top; +yy_stack_print (yybottom, yytop) + yytype_int16 *yybottom; + yytype_int16 *yytop; #endif { YYFPRINTF (stderr, "Stack now"); - for (; bottom <= top; ++bottom) - YYFPRINTF (stderr, " %d", *bottom); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } YYFPRINTF (stderr, "\n"); } @@ -1092,11 +1131,11 @@ yy_reduce_print (yyvsp, yyrule) /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { - fprintf (stderr, " $%d = ", yyi + 1); + YYFPRINTF (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], &(yyvsp[(yyi + 1) - (yynrhs)]) ); - fprintf (stderr, "\n"); + YYFPRINTF (stderr, "\n"); } } @@ -1133,7 +1172,6 @@ int yydebug; # define YYMAXDEPTH 10000 #endif - #if YYERROR_VERBOSE @@ -1236,115 +1274,142 @@ yytnamerr (char *yyres, const char *yystr) } # endif -/* Copy into YYRESULT an error message about the unexpected token - YYCHAR while in state YYSTATE. Return the number of bytes copied, - including the terminating null byte. If YYRESULT is null, do not - copy anything; just return the number of bytes that would be - copied. As a special case, return 0 if an ordinary "syntax error" - message will do. Return YYSIZE_MAXIMUM if overflow occurs during - size calculation. */ -static YYSIZE_T -yysyntax_error (char *yyresult, int yystate, int yychar) -{ - int yyn = yypact[yystate]; +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. - if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) - return 0; - else - { - int yytype = YYTRANSLATE (yychar); - YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); - YYSIZE_T yysize = yysize0; - YYSIZE_T yysize1; - int yysize_overflow = 0; - enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; - char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; - int yyx; - -# if 0 - /* This is so xgettext sees the translatable formats that are - constructed on the fly. */ - YY_("syntax error, unexpected %s"); - YY_("syntax error, unexpected %s, expecting %s"); - YY_("syntax error, unexpected %s, expecting %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); -# endif - char *yyfmt; - char const *yyf; - static char const yyunexpected[] = "syntax error, unexpected %s"; - static char const yyexpecting[] = ", expecting %s"; - static char const yyor[] = " or %s"; - char yyformat[sizeof yyunexpected - + sizeof yyexpecting - 1 - + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) - * (sizeof yyor - 1))]; - char const *yyprefix = yyexpecting; - - /* Start YYX at -YYN if negative to avoid negative indexes in - YYCHECK. */ - int yyxbegin = yyn < 0 ? -yyn : 0; - - /* Stay within bounds of both yycheck and yytname. */ - int yychecklim = YYLAST - yyn + 1; - int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; - int yycount = 1; - - yyarg[0] = yytname[yytype]; - yyfmt = yystpcpy (yyformat, yyunexpected); - - for (yyx = yyxbegin; yyx < yyxend; ++yyx) - if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) - { - if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) - { - yycount = 1; - yysize = yysize0; - yyformat[sizeof yyunexpected - 1] = '\0'; - break; - } - yyarg[yycount++] = yytname[yyx]; - yysize1 = yysize + yytnamerr (0, yytname[yyx]); - yysize_overflow |= (yysize1 < yysize); - yysize = yysize1; - yyfmt = yystpcpy (yyfmt, yyprefix); - yyprefix = yyor; - } + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) +{ + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = 0; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; + + /* There are many possibilities here to consider: + - Assume YYFAIL is not used. It's too flawed to consider. See + <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html> + for details. YYERROR is fine as it does not invoke this + function. + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } - yyf = YY_(yyformat); - yysize1 = yysize + yystrlen (yyf); - yysize_overflow |= (yysize1 < yysize); - yysize = yysize1; + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } - if (yysize_overflow) - return YYSIZE_MAXIMUM; + yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; - if (yyresult) - { - /* Avoid sprintf, as that infringes on the user's name space. - Don't have undefined behavior even if the translation - produced a string with the wrong number of "%s"s. */ - char *yyp = yyresult; - int yyi = 0; - while ((*yyp = *yyf) != '\0') - { - if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) - { - yyp += yytnamerr (yyp, yyarg[yyi++]); - yyf += 2; - } - else - { - yyp++; - yyf++; - } - } - } - return yysize; + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; } #endif /* YYERROR_VERBOSE */ - /*-----------------------------------------------. | Release the memory associated to this symbol. | @@ -1376,10 +1441,9 @@ yydestruct (yymsg, yytype, yyvaluep) break; } } - -/* Prevent warnings from -Wmissing-prototypes. */ +/* Prevent warnings from -Wmissing-prototypes. */ #ifdef YYPARSE_PARAM #if defined __STDC__ || defined __cplusplus int yyparse (void *YYPARSE_PARAM); @@ -1395,18 +1459,16 @@ int yyparse (); #endif /* ! YYPARSE_PARAM */ - -/* The look-ahead symbol. */ +/* The lookahead symbol. */ int yychar; -/* The semantic value of the look-ahead symbol. */ +/* The semantic value of the lookahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; - /*----------. | yyparse. | `----------*/ @@ -1433,66 +1495,66 @@ yyparse () #endif #endif { - - int yystate; - int yyn; - int yyresult; - /* Number of tokens to shift before error messages enabled. */ - int yyerrstatus; - /* Look-ahead token as an internal (translated) token number. */ - int yytoken = 0; -#if YYERROR_VERBOSE - /* Buffer for error messages, and its allocated size. */ - char yymsgbuf[128]; - char *yymsg = yymsgbuf; - YYSIZE_T yymsg_alloc = sizeof yymsgbuf; -#endif + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; - /* Three stacks and their tools: - `yyss': related to states, - `yyvs': related to semantic values, - `yyls': related to locations. + /* The stacks and their tools: + `yyss': related to states. + `yyvs': related to semantic values. - Refer to the stacks thru separate pointers, to allow yyoverflow - to reallocate them elsewhere. */ + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ - /* The state stack. */ - yytype_int16 yyssa[YYINITDEPTH]; - yytype_int16 *yyss = yyssa; - yytype_int16 *yyssp; + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss; + yytype_int16 *yyssp; - /* The semantic value stack. */ - YYSTYPE yyvsa[YYINITDEPTH]; - YYSTYPE *yyvs = yyvsa; - YYSTYPE *yyvsp; - - - -#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; - YYSIZE_T yystacksize = YYINITDEPTH; + YYSIZE_T yystacksize; + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; + yytoken = 0; + yyss = yyssa; + yyvs = yyvsa; + yystacksize = YYINITDEPTH; + YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; - yychar = YYEMPTY; /* Cause a token to be read. */ + yychar = YYEMPTY; /* Cause a token to be read. */ /* Initialize stack pointers. Waste one element of value and location stack so that they stay on the same level as the state stack. The wasted elements are never initialized. */ - yyssp = yyss; yyvsp = yyvs; @@ -1522,7 +1584,6 @@ yyparse () YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; - /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might @@ -1530,7 +1591,6 @@ yyparse () yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), - &yystacksize); yyss = yyss1; @@ -1553,9 +1613,8 @@ yyparse () (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; - YYSTACK_RELOCATE (yyss); - YYSTACK_RELOCATE (yyvs); - + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); @@ -1566,7 +1625,6 @@ yyparse () yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; - YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); @@ -1576,6 +1634,9 @@ yyparse () YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + if (yystate == YYFINAL) + YYACCEPT; + goto yybackup; /*-----------. @@ -1584,16 +1645,16 @@ yyparse () yybackup: /* Do appropriate processing given the current state. Read a - look-ahead token if we need one and don't already have one. */ + lookahead token if we need one and don't already have one. */ - /* First try to decide what to do without reference to look-ahead token. */ + /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; - if (yyn == YYPACT_NINF) + if (yypact_value_is_default (yyn)) goto yydefault; - /* Not known => get a look-ahead token if don't already have one. */ + /* Not known => get a lookahead token if don't already have one. */ - /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); @@ -1619,26 +1680,22 @@ yybackup: yyn = yytable[yyn]; if (yyn <= 0) { - if (yyn == 0 || yyn == YYTABLE_NINF) - goto yyerrlab; + if (yytable_value_is_error (yyn)) + goto yyerrlab; yyn = -yyn; goto yyreduce; } - if (yyn == YYFINAL) - YYACCEPT; - /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; - /* Shift the look-ahead token. */ + /* Shift the lookahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); - /* Discard the shifted token unless it is eof. */ - if (yychar != YYEOF) - yychar = YYEMPTY; + /* Discard the shifted token. */ + yychar = YYEMPTY; yystate = yyn; *++yyvsp = yylval; @@ -1678,14 +1735,18 @@ yyreduce: switch (yyn) { case 3: -#line 66 "a.y" + +/* Line 1806 of yacc.c */ +#line 68 "a.y" { stmtline = lineno; } break; case 5: -#line 73 "a.y" + +/* Line 1806 of yacc.c */ +#line 75 "a.y" { if((yyvsp[(1) - (2)].sym)->value != pc) yyerror("redeclaration of %s", (yyvsp[(1) - (2)].sym)->name); @@ -1694,7 +1755,9 @@ yyreduce: break; case 7: -#line 80 "a.y" + +/* Line 1806 of yacc.c */ +#line 82 "a.y" { (yyvsp[(1) - (2)].sym)->type = LLAB; (yyvsp[(1) - (2)].sym)->value = pc; @@ -1702,7 +1765,9 @@ yyreduce: break; case 12: -#line 91 "a.y" + +/* Line 1806 of yacc.c */ +#line 93 "a.y" { (yyvsp[(1) - (3)].sym)->type = LVAR; (yyvsp[(1) - (3)].sym)->value = (yyvsp[(3) - (3)].lval); @@ -1710,7 +1775,9 @@ yyreduce: break; case 13: -#line 96 "a.y" + +/* Line 1806 of yacc.c */ +#line 98 "a.y" { if((yyvsp[(1) - (3)].sym)->value != (yyvsp[(3) - (3)].lval)) yyerror("redeclaration of %s", (yyvsp[(1) - (3)].sym)->name); @@ -1719,180 +1786,252 @@ yyreduce: break; case 14: -#line 101 "a.y" + +/* Line 1806 of yacc.c */ +#line 103 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 15: -#line 102 "a.y" + +/* Line 1806 of yacc.c */ +#line 104 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 16: -#line 103 "a.y" + +/* Line 1806 of yacc.c */ +#line 105 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 17: -#line 104 "a.y" + +/* Line 1806 of yacc.c */ +#line 106 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 18: -#line 105 "a.y" + +/* Line 1806 of yacc.c */ +#line 107 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 19: -#line 106 "a.y" + +/* Line 1806 of yacc.c */ +#line 108 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 20: -#line 107 "a.y" + +/* Line 1806 of yacc.c */ +#line 109 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 21: -#line 108 "a.y" + +/* Line 1806 of yacc.c */ +#line 110 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 22: -#line 109 "a.y" + +/* Line 1806 of yacc.c */ +#line 111 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 23: -#line 110 "a.y" + +/* Line 1806 of yacc.c */ +#line 112 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 24: -#line 111 "a.y" + +/* Line 1806 of yacc.c */ +#line 113 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 25: -#line 112 "a.y" + +/* Line 1806 of yacc.c */ +#line 114 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 26: -#line 113 "a.y" + +/* Line 1806 of yacc.c */ +#line 115 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 27: -#line 114 "a.y" + +/* Line 1806 of yacc.c */ +#line 116 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 28: -#line 115 "a.y" + +/* Line 1806 of yacc.c */ +#line 117 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 29: -#line 116 "a.y" + +/* Line 1806 of yacc.c */ +#line 118 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 30: -#line 117 "a.y" + +/* Line 1806 of yacc.c */ +#line 119 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 31: + +/* Line 1806 of yacc.c */ #line 120 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 32: + +/* Line 1806 of yacc.c */ +#line 121 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 33: + +/* Line 1806 of yacc.c */ +#line 124 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = nullgen; } break; - case 32: -#line 125 "a.y" + case 34: + +/* Line 1806 of yacc.c */ +#line 129 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = nullgen; } break; - case 33: -#line 132 "a.y" + case 35: + +/* Line 1806 of yacc.c */ +#line 136 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 34: -#line 139 "a.y" + case 36: + +/* Line 1806 of yacc.c */ +#line 143 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 35: -#line 146 "a.y" + case 37: + +/* Line 1806 of yacc.c */ +#line 150 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (2)].gen); (yyval.gen2).to = nullgen; } break; - case 36: -#line 151 "a.y" + case 38: + +/* Line 1806 of yacc.c */ +#line 155 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (1)].gen); (yyval.gen2).to = nullgen; } break; - case 37: -#line 158 "a.y" + case 39: + +/* Line 1806 of yacc.c */ +#line 162 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(2) - (2)].gen); } break; - case 38: -#line 163 "a.y" + case 40: + +/* Line 1806 of yacc.c */ +#line 167 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(1) - (1)].gen); } break; - case 39: -#line 170 "a.y" + case 41: + +/* Line 1806 of yacc.c */ +#line 174 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(2) - (2)].gen); } break; - case 40: -#line 175 "a.y" + case 42: + +/* Line 1806 of yacc.c */ +#line 179 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(1) - (1)].gen); } break; - case 41: -#line 180 "a.y" + case 43: + +/* Line 1806 of yacc.c */ +#line 184 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 42: -#line 187 "a.y" + case 44: + +/* Line 1806 of yacc.c */ +#line 191 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); @@ -1900,16 +2039,20 @@ yyreduce: } break; - case 43: -#line 195 "a.y" + case 45: + +/* Line 1806 of yacc.c */ +#line 199 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 44: -#line 200 "a.y" + case 46: + +/* Line 1806 of yacc.c */ +#line 204 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); @@ -1917,32 +2060,40 @@ yyreduce: } break; - case 45: -#line 208 "a.y" + case 47: + +/* Line 1806 of yacc.c */ +#line 212 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(2) - (2)].gen); } break; - case 46: -#line 213 "a.y" + case 48: + +/* Line 1806 of yacc.c */ +#line 217 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(1) - (1)].gen); } break; - case 49: -#line 224 "a.y" + case 51: + +/* Line 1806 of yacc.c */ +#line 228 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 50: -#line 229 "a.y" + case 52: + +/* Line 1806 of yacc.c */ +#line 233 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).to = (yyvsp[(3) - (5)].gen); @@ -1952,16 +2103,20 @@ yyreduce: } break; - case 51: -#line 239 "a.y" + case 53: + +/* Line 1806 of yacc.c */ +#line 243 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 52: -#line 244 "a.y" + case 54: + +/* Line 1806 of yacc.c */ +#line 248 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).to = (yyvsp[(3) - (5)].gen); @@ -1971,32 +2126,40 @@ yyreduce: } break; - case 53: -#line 254 "a.y" + case 55: + +/* Line 1806 of yacc.c */ +#line 258 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (2)].gen); (yyval.gen2).to = nullgen; } break; - case 54: -#line 259 "a.y" + case 56: + +/* Line 1806 of yacc.c */ +#line 263 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (1)].gen); (yyval.gen2).to = nullgen; } break; - case 55: -#line 264 "a.y" + case 57: + +/* Line 1806 of yacc.c */ +#line 268 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 56: -#line 271 "a.y" + case 58: + +/* Line 1806 of yacc.c */ +#line 275 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).to = (yyvsp[(3) - (5)].gen); @@ -2004,8 +2167,10 @@ yyreduce: } break; - case 57: -#line 279 "a.y" + case 59: + +/* Line 1806 of yacc.c */ +#line 283 "a.y" { (yyval.gen2).from = (yyvsp[(3) - (5)].gen); (yyval.gen2).to = (yyvsp[(5) - (5)].gen); @@ -2015,32 +2180,40 @@ yyreduce: } break; - case 58: -#line 288 "a.y" + case 60: + +/* Line 1806 of yacc.c */ +#line 292 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = nullgen; } break; - case 59: -#line 293 "a.y" + case 61: + +/* Line 1806 of yacc.c */ +#line 297 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (1)].gen); (yyval.gen2).to = nullgen; } break; - case 60: -#line 300 "a.y" + case 62: + +/* Line 1806 of yacc.c */ +#line 304 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 61: -#line 305 "a.y" + case 63: + +/* Line 1806 of yacc.c */ +#line 309 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); @@ -2048,22 +2221,54 @@ yyreduce: } break; - case 66: -#line 319 "a.y" + case 64: + +/* Line 1806 of yacc.c */ +#line 317 "a.y" + { + if((yyvsp[(1) - (3)].gen).type != D_CONST || (yyvsp[(3) - (3)].gen).type != D_CONST) + yyerror("arguments to PCDATA must be integer constants"); + (yyval.gen2).from = (yyvsp[(1) - (3)].gen); + (yyval.gen2).to = (yyvsp[(3) - (3)].gen); + } + break; + + case 65: + +/* Line 1806 of yacc.c */ +#line 326 "a.y" + { + if((yyvsp[(1) - (3)].gen).type != D_CONST) + yyerror("index for FUNCDATA must be integer constant"); + if((yyvsp[(3) - (3)].gen).type != D_EXTERN && (yyvsp[(3) - (3)].gen).type != D_STATIC) + yyerror("value for FUNCDATA must be symbol reference"); + (yyval.gen2).from = (yyvsp[(1) - (3)].gen); + (yyval.gen2).to = (yyvsp[(3) - (3)].gen); + } + break; + + case 70: + +/* Line 1806 of yacc.c */ +#line 343 "a.y" { (yyval.gen) = (yyvsp[(2) - (2)].gen); } break; - case 67: -#line 323 "a.y" + case 71: + +/* Line 1806 of yacc.c */ +#line 347 "a.y" { (yyval.gen) = (yyvsp[(2) - (2)].gen); } break; - case 72: -#line 335 "a.y" + case 76: + +/* Line 1806 of yacc.c */ +#line 359 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_BRANCH; @@ -2071,8 +2276,10 @@ yyreduce: } break; - case 73: -#line 341 "a.y" + case 77: + +/* Line 1806 of yacc.c */ +#line 365 "a.y" { (yyval.gen) = nullgen; if(pass == 2) @@ -2083,8 +2290,10 @@ yyreduce: } break; - case 74: -#line 350 "a.y" + case 78: + +/* Line 1806 of yacc.c */ +#line 374 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_BRANCH; @@ -2093,64 +2302,80 @@ yyreduce: } break; - case 75: -#line 359 "a.y" + case 79: + +/* Line 1806 of yacc.c */ +#line 383 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(1) - (1)].lval); } break; - case 76: -#line 364 "a.y" + case 80: + +/* Line 1806 of yacc.c */ +#line 388 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(1) - (1)].lval); } break; - case 77: -#line 369 "a.y" + case 81: + +/* Line 1806 of yacc.c */ +#line 393 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(1) - (1)].lval); } break; - case 78: -#line 374 "a.y" + case 82: + +/* Line 1806 of yacc.c */ +#line 398 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(1) - (1)].lval); } break; - case 79: -#line 379 "a.y" + case 83: + +/* Line 1806 of yacc.c */ +#line 403 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SP; } break; - case 80: -#line 384 "a.y" + case 84: + +/* Line 1806 of yacc.c */ +#line 408 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(1) - (1)].lval); } break; - case 81: -#line 389 "a.y" + case 85: + +/* Line 1806 of yacc.c */ +#line 413 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(1) - (1)].lval); } break; - case 82: -#line 395 "a.y" + case 86: + +/* Line 1806 of yacc.c */ +#line 419 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_CONST; @@ -2158,8 +2383,10 @@ yyreduce: } break; - case 83: -#line 403 "a.y" + case 87: + +/* Line 1806 of yacc.c */ +#line 427 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_CONST; @@ -2167,8 +2394,10 @@ yyreduce: } break; - case 84: -#line 409 "a.y" + case 88: + +/* Line 1806 of yacc.c */ +#line 433 "a.y" { (yyval.gen) = (yyvsp[(2) - (2)].gen); (yyval.gen).index = (yyvsp[(2) - (2)].gen).type; @@ -2181,8 +2410,10 @@ yyreduce: } break; - case 85: -#line 420 "a.y" + case 89: + +/* Line 1806 of yacc.c */ +#line 444 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SCONST; @@ -2190,8 +2421,10 @@ yyreduce: } break; - case 86: -#line 426 "a.y" + case 90: + +/* Line 1806 of yacc.c */ +#line 450 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2199,8 +2432,10 @@ yyreduce: } break; - case 87: -#line 432 "a.y" + case 91: + +/* Line 1806 of yacc.c */ +#line 456 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2208,8 +2443,10 @@ yyreduce: } break; - case 88: -#line 438 "a.y" + case 92: + +/* Line 1806 of yacc.c */ +#line 462 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2217,8 +2454,10 @@ yyreduce: } break; - case 89: -#line 444 "a.y" + case 93: + +/* Line 1806 of yacc.c */ +#line 468 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2226,8 +2465,10 @@ yyreduce: } break; - case 92: -#line 456 "a.y" + case 96: + +/* Line 1806 of yacc.c */ +#line 480 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_NONE; @@ -2235,8 +2476,10 @@ yyreduce: } break; - case 93: -#line 462 "a.y" + case 97: + +/* Line 1806 of yacc.c */ +#line 486 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval); @@ -2244,8 +2487,10 @@ yyreduce: } break; - case 94: -#line 468 "a.y" + case 98: + +/* Line 1806 of yacc.c */ +#line 492 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_SP; @@ -2253,8 +2498,10 @@ yyreduce: } break; - case 95: -#line 474 "a.y" + case 99: + +/* Line 1806 of yacc.c */ +#line 498 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval); @@ -2262,8 +2509,10 @@ yyreduce: } break; - case 96: -#line 480 "a.y" + case 100: + +/* Line 1806 of yacc.c */ +#line 504 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_NONE; @@ -2274,8 +2523,10 @@ yyreduce: } break; - case 97: -#line 489 "a.y" + case 101: + +/* Line 1806 of yacc.c */ +#line 513 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(3) - (9)].lval); @@ -2286,24 +2537,44 @@ yyreduce: } break; - case 98: -#line 498 "a.y" + case 102: + +/* Line 1806 of yacc.c */ +#line 522 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_INDIR+(yyvsp[(3) - (9)].lval); + (yyval.gen).offset = (yyvsp[(1) - (9)].lval); + (yyval.gen).index = (yyvsp[(6) - (9)].lval); + (yyval.gen).scale = (yyvsp[(8) - (9)].lval); + checkscale((yyval.gen).scale); + } + break; + + case 103: + +/* Line 1806 of yacc.c */ +#line 531 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(2) - (3)].lval); } break; - case 99: -#line 503 "a.y" + case 104: + +/* Line 1806 of yacc.c */ +#line 536 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_SP; } break; - case 100: -#line 508 "a.y" + case 105: + +/* Line 1806 of yacc.c */ +#line 541 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_NONE; @@ -2313,8 +2584,10 @@ yyreduce: } break; - case 101: -#line 516 "a.y" + case 106: + +/* Line 1806 of yacc.c */ +#line 549 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(2) - (8)].lval); @@ -2324,15 +2597,19 @@ yyreduce: } break; - case 102: -#line 526 "a.y" + case 107: + +/* Line 1806 of yacc.c */ +#line 559 "a.y" { (yyval.gen) = (yyvsp[(1) - (1)].gen); } break; - case 103: -#line 530 "a.y" + case 108: + +/* Line 1806 of yacc.c */ +#line 563 "a.y" { (yyval.gen) = (yyvsp[(1) - (6)].gen); (yyval.gen).index = (yyvsp[(3) - (6)].lval); @@ -2341,8 +2618,10 @@ yyreduce: } break; - case 104: -#line 539 "a.y" + case 109: + +/* Line 1806 of yacc.c */ +#line 572 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(4) - (5)].lval); @@ -2351,8 +2630,10 @@ yyreduce: } break; - case 105: -#line 546 "a.y" + case 110: + +/* Line 1806 of yacc.c */ +#line 579 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_STATIC; @@ -2361,174 +2642,234 @@ yyreduce: } break; - case 106: -#line 554 "a.y" + case 111: + +/* Line 1806 of yacc.c */ +#line 587 "a.y" { (yyval.lval) = 0; } break; - case 107: -#line 558 "a.y" + case 112: + +/* Line 1806 of yacc.c */ +#line 591 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; - case 108: -#line 562 "a.y" + case 113: + +/* Line 1806 of yacc.c */ +#line 595 "a.y" { (yyval.lval) = -(yyvsp[(2) - (2)].lval); } break; - case 110: -#line 569 "a.y" + case 115: + +/* Line 1806 of yacc.c */ +#line 602 "a.y" { (yyval.lval) = D_AUTO; } break; - case 113: -#line 577 "a.y" + case 118: + +/* Line 1806 of yacc.c */ +#line 610 "a.y" { (yyval.lval) = (yyvsp[(1) - (1)].sym)->value; } break; - case 114: -#line 581 "a.y" + case 119: + +/* Line 1806 of yacc.c */ +#line 614 "a.y" { (yyval.lval) = -(yyvsp[(2) - (2)].lval); } break; - case 115: -#line 585 "a.y" + case 120: + +/* Line 1806 of yacc.c */ +#line 618 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; - case 116: -#line 589 "a.y" + case 121: + +/* Line 1806 of yacc.c */ +#line 622 "a.y" { (yyval.lval) = ~(yyvsp[(2) - (2)].lval); } break; - case 117: -#line 593 "a.y" + case 122: + +/* Line 1806 of yacc.c */ +#line 626 "a.y" { (yyval.lval) = (yyvsp[(2) - (3)].lval); } break; - case 118: -#line 599 "a.y" + case 123: + +/* Line 1806 of yacc.c */ +#line 632 "a.y" { - (yyval.lval) = (yyvsp[(1) - (1)].lval) & 0xffffffffLL; + (yyval.lval) = ((yyvsp[(1) - (1)].lval) & 0xffffffffLL) + + ((vlong)ArgsSizeUnknown << 32); } break; - case 119: -#line 603 "a.y" + case 124: + +/* Line 1806 of yacc.c */ +#line 637 "a.y" { - (yyval.lval) = -(yyvsp[(2) - (2)].lval) & 0xffffffffLL; + (yyval.lval) = (-(yyvsp[(2) - (2)].lval) & 0xffffffffLL) + + ((vlong)ArgsSizeUnknown << 32); } break; - case 120: -#line 607 "a.y" + case 125: + +/* Line 1806 of yacc.c */ +#line 642 "a.y" { (yyval.lval) = ((yyvsp[(1) - (3)].lval) & 0xffffffffLL) + (((yyvsp[(3) - (3)].lval) & 0xffffLL) << 32); } break; - case 121: -#line 612 "a.y" + case 126: + +/* Line 1806 of yacc.c */ +#line 647 "a.y" { (yyval.lval) = (-(yyvsp[(2) - (4)].lval) & 0xffffffffLL) + (((yyvsp[(4) - (4)].lval) & 0xffffLL) << 32); } break; - case 123: -#line 620 "a.y" + case 128: + +/* Line 1806 of yacc.c */ +#line 655 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) + (yyvsp[(3) - (3)].lval); } break; - case 124: -#line 624 "a.y" + case 129: + +/* Line 1806 of yacc.c */ +#line 659 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) - (yyvsp[(3) - (3)].lval); } break; - case 125: -#line 628 "a.y" + case 130: + +/* Line 1806 of yacc.c */ +#line 663 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) * (yyvsp[(3) - (3)].lval); } break; - case 126: -#line 632 "a.y" + case 131: + +/* Line 1806 of yacc.c */ +#line 667 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) / (yyvsp[(3) - (3)].lval); } break; - case 127: -#line 636 "a.y" + case 132: + +/* Line 1806 of yacc.c */ +#line 671 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) % (yyvsp[(3) - (3)].lval); } break; - case 128: -#line 640 "a.y" + case 133: + +/* Line 1806 of yacc.c */ +#line 675 "a.y" { (yyval.lval) = (yyvsp[(1) - (4)].lval) << (yyvsp[(4) - (4)].lval); } break; - case 129: -#line 644 "a.y" + case 134: + +/* Line 1806 of yacc.c */ +#line 679 "a.y" { (yyval.lval) = (yyvsp[(1) - (4)].lval) >> (yyvsp[(4) - (4)].lval); } break; - case 130: -#line 648 "a.y" + case 135: + +/* Line 1806 of yacc.c */ +#line 683 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) & (yyvsp[(3) - (3)].lval); } break; - case 131: -#line 652 "a.y" + case 136: + +/* Line 1806 of yacc.c */ +#line 687 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) ^ (yyvsp[(3) - (3)].lval); } break; - case 132: -#line 656 "a.y" + case 137: + +/* Line 1806 of yacc.c */ +#line 691 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) | (yyvsp[(3) - (3)].lval); } break; -/* Line 1267 of yacc.c. */ -#line 2530 "y.tab.c" + +/* Line 1806 of yacc.c */ +#line 2860 "y.tab.c" default: break; } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); @@ -2537,7 +2878,6 @@ yyreduce: *++yyvsp = yyval; - /* Now `shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ @@ -2557,6 +2897,10 @@ yyreduce: | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { @@ -2564,37 +2908,36 @@ yyerrlab: #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) { - YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); - if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) - { - YYSIZE_T yyalloc = 2 * yysize; - if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) - yyalloc = YYSTACK_ALLOC_MAXIMUM; - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); - yymsg = (char *) YYSTACK_ALLOC (yyalloc); - if (yymsg) - yymsg_alloc = yyalloc; - else - { - yymsg = yymsgbuf; - yymsg_alloc = sizeof yymsgbuf; - } - } - - if (0 < yysize && yysize <= yymsg_alloc) - { - (void) yysyntax_error (yymsg, yystate, yychar); - yyerror (yymsg); - } - else - { - yyerror (YY_("syntax error")); - if (yysize != 0) - goto yyexhaustedlab; - } + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; } +# undef YYSYNTAX_ERROR #endif } @@ -2602,7 +2945,7 @@ yyerrlab: if (yyerrstatus == 3) { - /* If just tried and failed to reuse look-ahead token after an + /* If just tried and failed to reuse lookahead token after an error, discard it. */ if (yychar <= YYEOF) @@ -2619,7 +2962,7 @@ yyerrlab: } } - /* Else will try to reuse look-ahead token after shifting the error + /* Else will try to reuse lookahead token after shifting the error token. */ goto yyerrlab1; @@ -2653,7 +2996,7 @@ yyerrlab1: for (;;) { yyn = yypact[yystate]; - if (yyn != YYPACT_NINF) + if (!yypact_value_is_default (yyn)) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) @@ -2676,9 +3019,6 @@ yyerrlab1: YY_STACK_PRINT (yyss, yyssp); } - if (yyn == YYFINAL) - YYACCEPT; - *++yyvsp = yylval; @@ -2703,7 +3043,7 @@ yyabortlab: yyresult = 1; goto yyreturn; -#ifndef yyoverflow +#if !defined(yyoverflow) || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ @@ -2714,9 +3054,14 @@ yyexhaustedlab: #endif yyreturn: - if (yychar != YYEOF && yychar != YYEMPTY) - yydestruct ("Cleanup: discarding lookahead", - yytoken, &yylval); + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + } /* Do not reclaim the symbols of the rule which action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); diff --git a/src/cmd/6a/y.tab.h b/src/cmd/6a/y.tab.h index 3cca766d3..bba6081dc 100644 --- a/src/cmd/6a/y.tab.h +++ b/src/cmd/6a/y.tab.h @@ -1,24 +1,21 @@ -/* A Bison parser, made by GNU Bison 2.3. */ +/* A Bison parser, made by GNU Bison 2.5. */ -/* Skeleton interface for Bison's Yacc-like parsers in C - - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 - Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ + along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work @@ -29,10 +26,11 @@ special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. - + This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ + /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE @@ -50,28 +48,30 @@ LTYPER = 266, LTYPET = 267, LTYPEG = 268, - LTYPES = 269, - LTYPEM = 270, - LTYPEI = 271, - LTYPEXC = 272, - LTYPEX = 273, - LTYPERT = 274, - LCONST = 275, - LFP = 276, - LPC = 277, - LSB = 278, - LBREG = 279, - LLREG = 280, - LSREG = 281, - LFREG = 282, - LMREG = 283, - LXREG = 284, - LFCONST = 285, - LSCONST = 286, - LSP = 287, - LNAME = 288, - LLAB = 289, - LVAR = 290 + LTYPEPC = 269, + LTYPES = 270, + LTYPEM = 271, + LTYPEI = 272, + LTYPEXC = 273, + LTYPEX = 274, + LTYPERT = 275, + LTYPEF = 276, + LCONST = 277, + LFP = 278, + LPC = 279, + LSB = 280, + LBREG = 281, + LLREG = 282, + LSREG = 283, + LFREG = 284, + LMREG = 285, + LXREG = 286, + LFCONST = 287, + LSCONST = 288, + LSP = 289, + LNAME = 290, + LLAB = 291, + LVAR = 292 }; #endif /* Tokens. */ @@ -86,50 +86,58 @@ #define LTYPER 266 #define LTYPET 267 #define LTYPEG 268 -#define LTYPES 269 -#define LTYPEM 270 -#define LTYPEI 271 -#define LTYPEXC 272 -#define LTYPEX 273 -#define LTYPERT 274 -#define LCONST 275 -#define LFP 276 -#define LPC 277 -#define LSB 278 -#define LBREG 279 -#define LLREG 280 -#define LSREG 281 -#define LFREG 282 -#define LMREG 283 -#define LXREG 284 -#define LFCONST 285 -#define LSCONST 286 -#define LSP 287 -#define LNAME 288 -#define LLAB 289 -#define LVAR 290 +#define LTYPEPC 269 +#define LTYPES 270 +#define LTYPEM 271 +#define LTYPEI 272 +#define LTYPEXC 273 +#define LTYPEX 274 +#define LTYPERT 275 +#define LTYPEF 276 +#define LCONST 277 +#define LFP 278 +#define LPC 279 +#define LSB 280 +#define LBREG 281 +#define LLREG 282 +#define LSREG 283 +#define LFREG 284 +#define LMREG 285 +#define LXREG 286 +#define LFCONST 287 +#define LSCONST 288 +#define LSP 289 +#define LNAME 290 +#define LLAB 291 +#define LVAR 292 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE -#line 37 "a.y" { + +/* Line 2068 of yacc.c */ +#line 38 "a.y" + Sym *sym; vlong lval; double dval; char sval[8]; Gen gen; Gen2 gen2; -} -/* Line 1529 of yacc.c. */ -#line 128 "y.tab.h" - YYSTYPE; + + + +/* Line 2068 of yacc.c */ +#line 135 "y.tab.h" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 -# define YYSTYPE_IS_TRIVIAL 1 #endif extern YYSTYPE yylval; + diff --git a/src/cmd/6c/cgen.c b/src/cmd/6c/cgen.c index 95400c445..bdef76ff0 100644 --- a/src/cmd/6c/cgen.c +++ b/src/cmd/6c/cgen.c @@ -29,6 +29,7 @@ // THE SOFTWARE. #include "gc.h" +#include "../../pkg/runtime/funcdata.h" /* ,x/^(print|prtree)\(/i/\/\/ */ int castup(Type*, Type*); @@ -392,13 +393,13 @@ cgen(Node *n, Node *nn) } } - if(o == OMUL) { + if(o == OMUL || o == OLMUL) { if(l->addable >= INDEXED) { t = l; l = r; r = t; } - /* should favour AX */ + reg[D_DX]++; // for gopcode case OMUL regalloc(&nod, l, nn); cgen(l, &nod); if(r->addable < INDEXED || hardconst(r)) { @@ -410,6 +411,7 @@ cgen(Node *n, Node *nn) gopcode(OMUL, n->type, r, &nod); /* addressible */ gmove(&nod, nn); regfree(&nod); + reg[D_DX]--; break; } @@ -943,6 +945,7 @@ cgen(Node *n, Node *nn) return; } gargs(r, &nod, &nod1); + gpcdata(PCDATA_ArgSize, curarg); if(l->addable < INDEXED) { reglcgen(&nod, l, nn); nod.op = OREGISTER; @@ -950,6 +953,7 @@ cgen(Node *n, Node *nn) regfree(&nod); } else gopcode(OFUNC, n->type, Z, l); + gpcdata(PCDATA_ArgSize, -1); if(REGARG >= 0 && reg[REGARG]) reg[REGARG]--; if(nn != Z) { @@ -1678,7 +1682,7 @@ copy: if(n->complex >= FNX && nn != nil && nn->complex >= FNX) { t = nn->type; - nn->type = types[TLONG]; + nn->type = types[TIND]; regialloc(&nod1, nn, Z); lcgen(nn, &nod1); regsalloc(&nod2, nn); @@ -1785,7 +1789,7 @@ copy: c = 0; if(n->complex > nn->complex) { t = n->type; - n->type = types[TLONG]; + n->type = types[TIND]; nodreg(&nod1, n, D_SI); if(reg[D_SI]) { gins(APUSHQ, &nod1, Z); @@ -1796,7 +1800,7 @@ copy: n->type = t; t = nn->type; - nn->type = types[TLONG]; + nn->type = types[TIND]; nodreg(&nod2, nn, D_DI); if(reg[D_DI]) { warn(Z, "DI botch"); @@ -1808,7 +1812,7 @@ warn(Z, "DI botch"); nn->type = t; } else { t = nn->type; - nn->type = types[TLONG]; + nn->type = types[TIND]; nodreg(&nod2, nn, D_DI); if(reg[D_DI]) { warn(Z, "DI botch"); @@ -1820,7 +1824,7 @@ warn(Z, "DI botch"); nn->type = t; t = n->type; - n->type = types[TLONG]; + n->type = types[TIND]; nodreg(&nod1, n, D_SI); if(reg[D_SI]) { gins(APUSHQ, &nod1, Z); diff --git a/src/cmd/6c/gc.h b/src/cmd/6c/gc.h index d1133ee21..c466a3afe 100644 --- a/src/cmd/6c/gc.h +++ b/src/cmd/6c/gc.h @@ -293,6 +293,7 @@ void patch(Prog*, int32); int sconst(Node*); void gpseudo(int, Sym*, Node*); void gprefetch(Node*); +void gpcdata(int, int); /* * swt.c diff --git a/src/cmd/6c/peep.c b/src/cmd/6c/peep.c index c648d8c00..0a3bd84bc 100644 --- a/src/cmd/6c/peep.c +++ b/src/cmd/6c/peep.c @@ -483,7 +483,7 @@ copy1(Adr *v1, Adr *v2, Reg *r, int f) } t = copyu(p, v2, A); switch(t) { - case 2: /* rar, cant split */ + case 2: /* rar, can't split */ if(debug['P']) print("; %D rar; return 0\n", v2); return 0; diff --git a/src/cmd/6c/reg.c b/src/cmd/6c/reg.c index e40e6c8f0..edd93a0a0 100644 --- a/src/cmd/6c/reg.c +++ b/src/cmd/6c/reg.c @@ -111,6 +111,7 @@ regopt(Prog *p) case AGLOBL: case ANAME: case ASIGNAME: + case AFUNCDATA: continue; } r = rega(); @@ -645,6 +646,7 @@ brk: case AGLOBL: case ANAME: case ASIGNAME: + case AFUNCDATA: break; } } diff --git a/src/cmd/6c/sgen.c b/src/cmd/6c/sgen.c index 2402a020d..744a60222 100644 --- a/src/cmd/6c/sgen.c +++ b/src/cmd/6c/sgen.c @@ -29,16 +29,14 @@ // THE SOFTWARE. #include "gc.h" +#include "../../pkg/runtime/funcdata.h" Prog* gtext(Sym *s, int32 stkoff) { vlong v; - - v = 0; - if(!(textflag & NOSPLIT)) - v |= argsize() << 32; - v |= stkoff & 0xffffffff; + + v = ((uvlong)argsize() << 32) | (stkoff & 0xffffffff); if((textflag & NOSPLIT) && stkoff >= 128) yyerror("stack frame too large for NOSPLIT function"); diff --git a/src/cmd/6c/swt.c b/src/cmd/6c/swt.c index 541c7beaa..2496da477 100644 --- a/src/cmd/6c/swt.c +++ b/src/cmd/6c/swt.c @@ -311,12 +311,8 @@ outcode(void) goto jackpot; break; } - Bputc(&b, p->as); - Bputc(&b, p->as>>8); - Bputc(&b, p->lineno); - Bputc(&b, p->lineno>>8); - Bputc(&b, p->lineno>>16); - Bputc(&b, p->lineno>>24); + BPUTLE2(&b, p->as); + BPUTLE4(&b, p->lineno); zaddr(&b, &p->from, sf); zaddr(&b, &p->to, st); } @@ -392,13 +388,12 @@ outhist(Biobuf *b) q = 0; } if(n) { - Bputc(b, ANAME); - Bputc(b, ANAME>>8); - Bputc(b, D_FILE); - Bputc(b, 1); - Bputc(b, '<'); + BPUTLE2(b, ANAME); + BPUTC(b, D_FILE); + BPUTC(b, 1); + BPUTC(b, '<'); Bwrite(b, p, n); - Bputc(b, 0); + BPUTC(b, 0); } p = q; if(p == 0 && op) { @@ -412,12 +407,8 @@ outhist(Biobuf *b) if(h->offset) pg.to.type = D_CONST; - Bputc(b, pg.as); - Bputc(b, pg.as>>8); - Bputc(b, pg.lineno); - Bputc(b, pg.lineno>>8); - Bputc(b, pg.lineno>>16); - Bputc(b, pg.lineno>>24); + BPUTLE2(b, pg.as); + BPUTLE4(b, pg.lineno); zaddr(b, &pg.from, 0); zaddr(b, &pg.to, 0); @@ -436,26 +427,21 @@ zname(Biobuf *b, Sym *s, int t) if(debug['T'] && t == D_EXTERN && s->sig != SIGDONE && s->type != types[TENUM] && s != symrathole){ sig = sign(s); - Bputc(b, ASIGNAME); - Bputc(b, ASIGNAME>>8); - Bputc(b, sig); - Bputc(b, sig>>8); - Bputc(b, sig>>16); - Bputc(b, sig>>24); + BPUTLE2(b, ASIGNAME); + BPUTLE4(b, sig); s->sig = SIGDONE; } else{ - Bputc(b, ANAME); /* as */ - Bputc(b, ANAME>>8); /* as */ + BPUTLE2(b, ANAME); /* as */ } - Bputc(b, t); /* type */ - Bputc(b, s->sym); /* sym */ + BPUTC(b, t); /* type */ + BPUTC(b, s->sym); /* sym */ n = s->name; while(*n) { - Bputc(b, *n); + BPUTC(b, *n); n++; } - Bputc(b, 0); + BPUTC(b, 0); } void @@ -490,52 +476,40 @@ zaddr(Biobuf *b, Adr *a, int s) t |= T_SCONST; break; } - Bputc(b, t); + BPUTC(b, t); if(t & T_INDEX) { /* implies index, scale */ - Bputc(b, a->index); - Bputc(b, a->scale); + BPUTC(b, a->index); + BPUTC(b, a->scale); } if(t & T_OFFSET) { /* implies offset */ l = a->offset; - Bputc(b, l); - Bputc(b, l>>8); - Bputc(b, l>>16); - Bputc(b, l>>24); + BPUTLE4(b, l); if(t & T_64) { l = a->offset>>32; - Bputc(b, l); - Bputc(b, l>>8); - Bputc(b, l>>16); - Bputc(b, l>>24); + BPUTLE4(b, l); } } if(t & T_SYM) /* implies sym */ - Bputc(b, s); + BPUTC(b, s); if(t & T_FCONST) { ieeedtod(&e, a->dval); l = e.l; - Bputc(b, l); - Bputc(b, l>>8); - Bputc(b, l>>16); - Bputc(b, l>>24); + BPUTLE4(b, l); l = e.h; - Bputc(b, l); - Bputc(b, l>>8); - Bputc(b, l>>16); - Bputc(b, l>>24); + BPUTLE4(b, l); return; } if(t & T_SCONST) { n = a->sval; for(i=0; i<NSNAME; i++) { - Bputc(b, *n); + BPUTC(b, *n); n++; } return; } if(t & T_TYPE) - Bputc(b, a->type); + BPUTC(b, a->type); } int32 diff --git a/src/cmd/6c/txt.c b/src/cmd/6c/txt.c index 364b189f2..6f5d42da5 100644 --- a/src/cmd/6c/txt.c +++ b/src/cmd/6c/txt.c @@ -158,9 +158,7 @@ gclean(void) continue; if(s->type == types[TENUM]) continue; - textflag = s->dataflag; gpseudo(AGLOBL, s, nodconst(s->type->width)); - textflag = 0; } nextpc(); p->as = AEND; @@ -1190,7 +1188,7 @@ print("botch in doindex\n"); else if(n->left->op == OREGISTER) idx.ptr = n->left->reg; else if(n->left->op != OADDR) { - reg[D_BP]++; // cant be used as a base + reg[D_BP]++; // can't be used as a base regalloc(&nod1, &qregnode, Z); cgen(n->left, &nod1); idx.ptr = nod1.reg; @@ -1502,8 +1500,16 @@ gpseudo(int a, Sym *s, Node *n) p->as = a; p->from.type = D_EXTERN; p->from.sym = s; - p->from.scale = textflag; - textflag = 0; + + switch(a) { + case ATEXT: + p->from.scale = textflag; + textflag = 0; + break; + case AGLOBL: + p->from.scale = s->dataflag; + break; + } if(s->class == CSTATIC) p->from.type = D_STATIC; @@ -1513,6 +1519,15 @@ gpseudo(int a, Sym *s, Node *n) } void +gpcdata(int index, int value) +{ + Node n1; + + n1 = *nodconst(index); + gins(APCDATA, &n1, nodconst(value)); +} + +void gprefetch(Node *n) { Node n1; diff --git a/src/cmd/6g/cgen.c b/src/cmd/6g/cgen.c index 2eae865f3..ada2baa81 100644 --- a/src/cmd/6g/cgen.c +++ b/src/cmd/6g/cgen.c @@ -37,6 +37,8 @@ cgen(Node *n, Node *res) case OSLICE: case OSLICEARR: case OSLICESTR: + case OSLICE3: + case OSLICE3ARR: if (res->op != ONAME || !res->addable) { tempname(&n1, n->type); cgen_slice(n, &n1); @@ -134,6 +136,7 @@ cgen(Node *n, Node *res) // can't do in walk because n->left->addable // changes if n->left is an escaping local variable. switch(n->op) { + case OSPTR: case OLEN: if(isslice(n->left->type) || istype(n->left->type, TSTRING)) n->addable = n->left->addable; @@ -312,6 +315,22 @@ cgen(Node *n, Node *res) regfree(&n1); break; + case OSPTR: + // pointer is the first word of string or slice. + if(isconst(nl, CTSTR)) { + regalloc(&n1, types[tptr], res); + p1 = gins(ALEAQ, N, &n1); + datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from); + gmove(&n1, res); + regfree(&n1); + break; + } + igen(nl, &n1, res); + n1.type = n->type; + gmove(&n1, res); + regfree(&n1); + break; + case OLEN: if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) { // map and chan have len in the first int-sized word. @@ -382,7 +401,11 @@ cgen(Node *n, Node *res) break; case OADDR: + if(n->bounded) // let race detector avoid nil checks + disable_checknil++; agen(nl, res); + if(n->bounded) + disable_checknil--; break; case OCALLMETH: @@ -516,8 +539,8 @@ ret: } /* - * allocate a register in res and generate - * newreg = &n + * allocate a register (reusing res if possible) and generate + * a = n * The caller must call regfree(a). */ void @@ -558,14 +581,16 @@ cgenr(Node *n, Node *a, Node *res) } /* - * allocate a register in res and generate - * res = &n + * allocate a register (reusing res if possible) and generate + * a = &n + * The caller must call regfree(a). + * The generated code checks that the result is not nil. */ void agenr(Node *n, Node *a, Node *res) { Node *nl, *nr; - Node n1, n2, n3, n4, n5, tmp, tmp2, nlen; + Node n1, n2, n3, n5, tmp, tmp2, nlen; Prog *p1; Type *t; uint64 w; @@ -593,6 +618,7 @@ agenr(Node *n, Node *a, Node *res) case OIND: cgenr(n->left, a, res); + cgen_checknil(a); break; case OINDEX: @@ -654,18 +680,6 @@ agenr(Node *n, Node *a, Node *res) // len(a) is in nlen (if needed) // w is width - // explicit check for nil if array is large enough - // that we might derive too big a pointer. - if(isfixedarray(nl->type) && nl->type->width >= unmappedzero) { - regalloc(&n4, types[tptr], &n3); - gmove(&n3, &n4); - n4.op = OINDREG; - n4.type = types[TUINT8]; - n4.xoffset = 0; - gins(ATESTB, nodintconst(0), &n4); - regfree(&n4); - } - // constant index if(isconst(nr, CTINT)) { if(isconst(nl, CTSTR)) @@ -775,6 +789,7 @@ agenr(Node *n, Node *a, Node *res) /* * generate: * res = &n; + * The generated code checks that the result is not nil. */ void agen(Node *n, Node *res) @@ -841,6 +856,8 @@ agen(Node *n, Node *res) case OSLICE: case OSLICEARR: case OSLICESTR: + case OSLICE3: + case OSLICE3ARR: tempname(&n1, n->type); cgen_slice(n, &n1); agen(&n1, res); @@ -878,43 +895,20 @@ agen(Node *n, Node *res) case OIND: cgen(nl, res); + cgen_checknil(res); break; case ODOT: agen(nl, res); - // explicit check for nil if struct is large enough - // that we might derive too big a pointer. If the left node - // was ODOT we have already done the nil check. - if(nl->op != ODOT) - if(nl->type->width >= unmappedzero) { - regalloc(&n1, types[tptr], res); - gmove(res, &n1); - n1.op = OINDREG; - n1.type = types[TUINT8]; - n1.xoffset = 0; - gins(ATESTB, nodintconst(0), &n1); - regfree(&n1); - } if(n->xoffset != 0) ginscon(optoas(OADD, types[tptr]), n->xoffset, res); break; case ODOTPTR: cgen(nl, res); - // explicit check for nil if struct is large enough - // that we might derive too big a pointer. - if(nl->type->type->width >= unmappedzero) { - regalloc(&n1, types[tptr], res); - gmove(res, &n1); - n1.op = OINDREG; - n1.type = types[TUINT8]; - n1.xoffset = 0; - gins(ATESTB, nodintconst(0), &n1); - regfree(&n1); - } - if(n->xoffset != 0) { + cgen_checknil(res); + if(n->xoffset != 0) ginscon(optoas(OADD, types[tptr]), n->xoffset, res); - } break; } @@ -929,6 +923,7 @@ ret: * * on exit, a has been changed to be *newreg. * caller must regfree(a). + * The generated code checks that the result is not *nil. */ void igen(Node *n, Node *a, Node *res) @@ -959,22 +954,16 @@ igen(Node *n, Node *a, Node *res) igen(n->left, a, res); a->xoffset += n->xoffset; a->type = n->type; + fixlargeoffset(a); return; case ODOTPTR: cgenr(n->left, a, res); - // explicit check for nil if struct is large enough - // that we might derive too big a pointer. - if(n->left->type->type->width >= unmappedzero) { - n1 = *a; - n1.op = OINDREG; - n1.type = types[TUINT8]; - n1.xoffset = 0; - gins(ATESTB, nodintconst(0), &n1); - } + cgen_checknil(a); a->op = OINDREG; a->xoffset += n->xoffset; a->type = n->type; + fixlargeoffset(a); return; case OCALLFUNC: @@ -1013,6 +1002,7 @@ igen(Node *n, Node *a, Node *res) igen(n->left, a, res); else { igen(n->left, &n1, res); + cgen_checknil(&n1); regalloc(a, types[tptr], res); gmove(&n1, a); regfree(&n1); @@ -1022,8 +1012,10 @@ igen(Node *n, Node *a, Node *res) // Compute &a[i] as &a + i*width. a->type = n->type; a->xoffset += mpgetfix(n->right->val.u.xval)*n->type->width; + fixlargeoffset(a); return; } + break; } agenr(n, a, res); @@ -1480,7 +1472,7 @@ cadable(Node *n) * Small structs or arrays with elements of basic type are * also supported. * nr is N when assigning a zero value. - * return 1 if can do, 0 if cant. + * return 1 if can do, 0 if can't. */ int componentgen(Node *nr, Node *nl) diff --git a/src/cmd/6g/gg.h b/src/cmd/6g/gg.h index ceb6a2caa..3ef59c788 100644 --- a/src/cmd/6g/gg.h +++ b/src/cmd/6g/gg.h @@ -41,7 +41,7 @@ struct Prog Addr from; // src address Addr to; // dst address Prog* link; // next instruction in this func - void* reg; // pointer to containing Reg struct + void* opt; // for optimizer passes }; #define TEXTFLAG from.scale @@ -64,7 +64,6 @@ extern vlong unmappedzero; * ggen.c */ void compile(Node*); -void proglist(void); void gen(Node*); Node* lookdot(Node*, Node*, int); void cgen_as(Node*, Node*); @@ -107,7 +106,6 @@ int componentgen(Node*, Node*); * gsubr.c */ void clearp(Prog*); -void proglist(void); Prog* gbranch(int, Type*, int); Prog* prog(int); void gconv(int, int); @@ -133,6 +131,8 @@ int sudoaddable(int, Node*, Addr*); void afunclit(Addr*, Node*); void nodfconst(Node*, Type*, Mpflt*); void gtrack(Sym*); +void gargsize(vlong); +void fixlargeoffset(Node *n); /* * cplx.c diff --git a/src/cmd/6g/ggen.c b/src/cmd/6g/ggen.c index 5e426753c..9fad9f7f1 100644 --- a/src/cmd/6g/ggen.c +++ b/src/cmd/6g/ggen.c @@ -9,15 +9,59 @@ #include "gg.h" #include "opt.h" +static Prog* appendp(Prog*, int, int, vlong, int, vlong); + void -defframe(Prog *ptxt) +defframe(Prog *ptxt, Bvec *bv) { + int i, j; + uint32 frame; + Prog *p; + // fill in argument size ptxt->to.offset = rnd(curfn->type->argwid, widthptr); // fill in final stack size ptxt->to.offset <<= 32; - ptxt->to.offset |= rnd(stksize+maxarg, widthptr); + frame = rnd(stksize+maxarg, widthptr); + ptxt->to.offset |= frame; + + // insert code to clear pointered part of the frame, + // so that garbage collector only sees initialized values + // when it looks for pointers. + p = ptxt; + if(stkzerosize >= 8*widthptr) { + p = appendp(p, AMOVQ, D_CONST, 0, D_AX, 0); + p = appendp(p, AMOVQ, D_CONST, stkzerosize/widthptr, D_CX, 0); + p = appendp(p, ALEAQ, D_SP+D_INDIR, frame-stkzerosize, D_DI, 0); + p = appendp(p, AREP, D_NONE, 0, D_NONE, 0); + appendp(p, ASTOSQ, D_NONE, 0, D_NONE, 0); + } else { + j = (stkptrsize - stkzerosize)/widthptr * 2; + for(i=0; i<stkzerosize; i+=widthptr) { + if(bvget(bv, j) || bvget(bv, j+1)) + p = appendp(p, AMOVQ, D_CONST, 0, D_SP+D_INDIR, frame-stkzerosize+i); + j += 2; + } + } +} + +static Prog* +appendp(Prog *p, int as, int ftype, vlong foffset, int ttype, vlong toffset) +{ + Prog *q; + + q = mal(sizeof(*q)); + clearp(q); + q->as = as; + q->lineno = p->lineno; + q->from.type = ftype; + q->from.offset = foffset; + q->to.type = ttype; + q->to.offset = toffset; + q->link = p->link; + p->link = q; + return q; } // Sweep the prog list to mark any used nodes. @@ -36,7 +80,7 @@ markautoused(Prog* p) } } -// Fixup instructions after compactframe has moved all autos around. +// Fixup instructions after allocauto (formerly compactframe) has moved all autos around. void fixautoused(Prog *p) { @@ -70,10 +114,29 @@ fixautoused(Prog *p) void ginscall(Node *f, int proc) { + int32 arg; Prog *p; Node reg, con; Node r1; + if(f->type != T) + setmaxarg(f->type); + + arg = -1; + // Most functions have a fixed-size argument block, so traceback uses that during unwind. + // Not all, though: there are some variadic functions in package runtime, + // and for those we emit call-specific metadata recorded by caller. + // Reflect generates functions with variable argsize (see reflect.methodValueCall/makeFuncStub), + // so we do this for all indirect calls as well. + if(f->type != T && (f->sym == S || (f->sym != S && f->sym->pkg == runtimepkg) || proc == 1 || proc == 2)) { + arg = f->type->argwid; + if(proc == 1 || proc == 2) + arg += 2*widthptr; + } + + if(arg != -1) + gargsize(arg); + switch(proc) { default: fatal("ginscall: bad proc %d", proc); @@ -82,6 +145,19 @@ ginscall(Node *f, int proc) case 0: // normal call case -1: // normal call but no return if(f->op == ONAME && f->class == PFUNC) { + if(f == deferreturn) { + // Deferred calls will appear to be returning to + // the CALL deferreturn(SB) that we are about to emit. + // However, the stack trace code will show the line + // of the instruction byte before the return PC. + // To avoid that being an unrelated instruction, + // insert an x86 NOP that we will have the right line number. + // x86 NOP 0x90 is really XCHG AX, AX; use that description + // because the NOP pseudo-instruction would be removed by + // the linker. + nodreg(®, types[TINT], D_AX); + gins(AXCHGL, ®, ®); + } p = gins(ACALL, N, f); afunclit(&p->to, f); if(proc == -1 || noreturn(p)) @@ -130,6 +206,9 @@ ginscall(Node *f, int proc) } break; } + + if(arg != -1) + gargsize(-1); } /* @@ -178,6 +257,7 @@ cgen_callinter(Node *n, Node *res, int proc) regalloc(&nodr, types[tptr], &nodo); if(n->left->xoffset == BADWIDTH) fatal("cgen_callinter: badwidth"); + cgen_checknil(&nodo); // in case offset is huge nodo.op = OINDREG; nodo.xoffset = n->left->xoffset + 3*widthptr + 8; if(proc == 0) { @@ -189,14 +269,11 @@ cgen_callinter(Node *n, Node *res, int proc) gins(ALEAQ, &nodo, &nodr); // REG = &(32+offset(REG)) -- i.tab->fun[f] } - // BOTCH nodr.type = fntype; nodr.type = n->left->type; ginscall(&nodr, proc); regfree(&nodr); regfree(&nodo); - - setmaxarg(n->left->type); } /* @@ -224,8 +301,6 @@ cgen_call(Node *n, int proc) genlist(n->list); // assign the args t = n->left->type; - setmaxarg(t); - // call tempname pointer if(n->left->ullman >= UINF) { regalloc(&nod, types[tptr], N); @@ -325,11 +400,18 @@ cgen_aret(Node *n, Node *res) void cgen_ret(Node *n) { + Prog *p; + genlist(n->list); // copy out args - if(hasdefer || curfn->exit) + if(hasdefer || curfn->exit) { gjmp(retpc); - else - gins(ARET, N, N); + return; + } + p = gins(ARET, N, N); + if(n->op == ORETJMP) { + p->to.type = D_EXTERN; + p->to.sym = n->left->sym; + } } /* @@ -514,7 +596,7 @@ dodiv(int op, Node *nl, Node *nr, Node *res) check = 0; if(issigned[t->etype]) { check = 1; - if(isconst(nl, CTINT) && mpgetfix(nl->val.u.xval) != -1LL<<(t->width*8-1)) + if(isconst(nl, CTINT) && mpgetfix(nl->val.u.xval) != -(1ULL<<(t->width*8-1))) check = 0; else if(isconst(nr, CTINT) && mpgetfix(nr->val.u.xval) != -1) check = 0; @@ -987,3 +1069,51 @@ clearfat(Node *nl) restx(&n1, &oldn1); restx(&ax, &oldax); } + +// Called after regopt and peep have run. +// Expand CHECKNIL pseudo-op into actual nil pointer check. +void +expandchecks(Prog *firstp) +{ + Prog *p, *p1, *p2; + + for(p = firstp; p != P; p = p->link) { + if(p->as != ACHECKNIL) + continue; + if(debug_checknil && p->lineno > 1) // p->lineno==1 in generated wrappers + warnl(p->lineno, "generated nil check"); + // check is + // CMP arg, $0 + // JNE 2(PC) (likely) + // MOV AX, 0 + p1 = mal(sizeof *p1); + p2 = mal(sizeof *p2); + clearp(p1); + clearp(p2); + p1->link = p2; + p2->link = p->link; + p->link = p1; + p1->lineno = p->lineno; + p2->lineno = p->lineno; + p1->loc = 9999; + p2->loc = 9999; + p->as = ACMPQ; + p->to.type = D_CONST; + p->to.offset = 0; + p1->as = AJNE; + p1->from.type = D_CONST; + p1->from.offset = 1; // likely + p1->to.type = D_BRANCH; + p1->to.u.branch = p2->link; + // crash by write to memory address 0. + // if possible, since we know arg is 0, use 0(arg), + // which will be shorter to encode than plain 0. + p2->as = AMOVL; + p2->from.type = D_AX; + if(regtyp(&p->from)) + p2->to.type = p->from.type + D_INDIR; + else + p2->to.type = D_INDIR+D_NONE; + p2->to.offset = 0; + } +} diff --git a/src/cmd/6g/gobj.c b/src/cmd/6g/gobj.c index cdbbd5d9d..a9bd5e833 100644 --- a/src/cmd/6g/gobj.c +++ b/src/cmd/6g/gobj.c @@ -35,10 +35,9 @@ void zname(Biobuf *b, Sym *s, int t) { - Bputc(b, ANAME); /* as */ - Bputc(b, ANAME>>8); /* as */ - Bputc(b, t); /* type */ - Bputc(b, s->sym); /* sym */ + BPUTLE2(b, ANAME); /* as */ + BPUTC(b, t); /* type */ + BPUTC(b, s->sym); /* sym */ Bputname(b, s); } @@ -46,13 +45,12 @@ zname(Biobuf *b, Sym *s, int t) void zfile(Biobuf *b, char *p, int n) { - Bputc(b, ANAME); - Bputc(b, ANAME>>8); - Bputc(b, D_FILE); - Bputc(b, 1); - Bputc(b, '<'); + BPUTLE2(b, ANAME); + BPUTC(b, D_FILE); + BPUTC(b, 1); + BPUTC(b, '<'); Bwrite(b, p, n); - Bputc(b, 0); + BPUTC(b, 0); } void @@ -60,12 +58,8 @@ zhist(Biobuf *b, int line, vlong offset) { Addr a; - Bputc(b, AHISTORY); - Bputc(b, AHISTORY>>8); - Bputc(b, line); - Bputc(b, line>>8); - Bputc(b, line>>16); - Bputc(b, line>>24); + BPUTLE2(b, AHISTORY); + BPUTLE4(b, line); zaddr(b, &zprog.from, 0, 0); a = zprog.to; if(offset != 0) { @@ -116,54 +110,40 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype) t |= T_SCONST; break; } - Bputc(b, t); + BPUTC(b, t); if(t & T_INDEX) { /* implies index, scale */ - Bputc(b, a->index); - Bputc(b, a->scale); + BPUTC(b, a->index); + BPUTC(b, a->scale); } if(t & T_OFFSET) { /* implies offset */ l = a->offset; - Bputc(b, l); - Bputc(b, l>>8); - Bputc(b, l>>16); - Bputc(b, l>>24); + BPUTLE4(b, l); if(t & T_64) { l = a->offset>>32; - Bputc(b, l); - Bputc(b, l>>8); - Bputc(b, l>>16); - Bputc(b, l>>24); + BPUTLE4(b, l); } } if(t & T_SYM) /* implies sym */ - Bputc(b, s); + BPUTC(b, s); if(t & T_FCONST) { ieeedtod(&e, a->u.dval); - l = e; - Bputc(b, l); - Bputc(b, l>>8); - Bputc(b, l>>16); - Bputc(b, l>>24); - l = e >> 32; - Bputc(b, l); - Bputc(b, l>>8); - Bputc(b, l>>16); - Bputc(b, l>>24); + BPUTLE4(b, e); + BPUTLE4(b, e >> 32); return; } if(t & T_SCONST) { n = a->u.sval; for(i=0; i<NSNAME; i++) { - Bputc(b, *n); + BPUTC(b, *n); n++; } return; } if(t & T_TYPE) - Bputc(b, a->type); + BPUTC(b, a->type); if(t & T_GOTYPE) - Bputc(b, gotype); + BPUTC(b, gotype); } static struct { @@ -269,12 +249,8 @@ dumpfuncs(void) break; } - Bputc(bout, p->as); - Bputc(bout, p->as>>8); - Bputc(bout, p->lineno); - Bputc(bout, p->lineno>>8); - Bputc(bout, p->lineno>>16); - Bputc(bout, p->lineno>>24); + BPUTLE2(bout, p->as); + BPUTLE4(bout, p->lineno); zaddr(bout, &p->from, sf, gf); zaddr(bout, &p->to, st, gt); } @@ -498,114 +474,6 @@ dsymptr(Sym *s, int off, Sym *x, int xoff) } void -genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface) -{ - Sym *e; - int c, d, mov, add, loaded; - int64 o; - Prog *p; - Type *f; - - USED(iface); - - if(0 && debug['r']) - print("genembedtramp %T %T %S\n", rcvr, method, newnam); - - e = method->sym; - for(d=0; d<nelem(dotlist); d++) { - c = adddot1(e, rcvr, d, nil, 0); - if(c == 1) - goto out; - } - fatal("genembedtramp %T.%S", rcvr, method->sym); - -out: - newplist()->name = newname(newnam); - - //TEXT main·S_test2(SB),7,$0 - p = pc; - gins(ATEXT, N, N); - p->from.type = D_EXTERN; - p->from.sym = newnam; - p->to.type = D_CONST; - p->to.offset = 0; - p->from.scale = 7; -//print("1. %P\n", p); - - mov = AMOVQ; - add = AADDQ; - loaded = 0; - o = 0; - for(c=d-1; c>=0; c--) { - f = dotlist[c].field; - o += f->width; - if(!isptr[f->type->etype]) - continue; - if(!loaded) { - loaded = 1; - //MOVQ 8(SP), AX - p = pc; - gins(mov, N, N); - p->from.type = D_INDIR+D_SP; - p->from.offset = widthptr; - p->to.type = D_AX; -//print("2. %P\n", p); - } - - //MOVQ o(AX), AX - p = pc; - gins(mov, N, N); - p->from.type = D_INDIR+D_AX; - p->from.offset = o; - p->to.type = D_AX; -//print("3. %P\n", p); - o = 0; - } - if(o != 0) { - //ADDQ $XX, AX - p = pc; - gins(add, N, N); - p->from.type = D_CONST; - p->from.offset = o; - if(loaded) - p->to.type = D_AX; - else { - p->to.type = D_INDIR+D_SP; - p->to.offset = widthptr; - } -//print("4. %P\n", p); - } - - //MOVQ AX, 8(SP) - if(loaded) { - p = pc; - gins(mov, N, N); - p->from.type = D_AX; - p->to.type = D_INDIR+D_SP; - p->to.offset = widthptr; -//print("5. %P\n", p); - } else { - // TODO(rsc): obviously this is unnecessary, - // but 6l has a bug, and it can't handle - // JMP instructions too close to the top of - // a new function. - gins(ANOP, N, N); - } - - f = dotlist[0].field; - //JMP main·*Sub_test2(SB) - if(isptr[f->type->etype]) - f = f->type; - p = pc; - gins(AJMP, N, N); - p->to.type = D_EXTERN; - p->to.sym = methodsym(method->sym, ptrto(f->type), 0); -//print("6. %P\n", p); - - pc->as = ARET; // overwrite AEND -} - -void nopout(Prog *p) { p->as = ANOP; diff --git a/src/cmd/6g/gsubr.c b/src/cmd/6g/gsubr.c index 55864c34e..7318909bb 100644 --- a/src/cmd/6g/gsubr.c +++ b/src/cmd/6g/gsubr.c @@ -31,9 +31,11 @@ #include <u.h> #include <libc.h> #include "gg.h" +#include "../../pkg/runtime/funcdata.h" // TODO(rsc): Can make this bigger if we move // the text segment up higher in 6l for all GOOS. +// At the same time, can raise StackBig in ../../pkg/runtime/stack.h. vlong unmappedzero = 4096; void @@ -218,6 +220,16 @@ gtrack(Sym *s) } void +gargsize(vlong size) +{ + Node n1, n2; + + nodconst(&n1, types[TINT32], PCDATA_ArgSize); + nodconst(&n2, types[TINT32], size); + gins(APCDATA, &n1, &n2); +} + +void ggloblsym(Sym *s, int32 width, int dupok, int rodata) { Prog *p; @@ -494,7 +506,7 @@ fp: break; case 2: // offset output arg -fatal("shouldnt be used"); +fatal("shouldn't be used"); n->op = OINDREG; n->val.u.reg = D_SP; n->xoffset += types[tptr]->width; @@ -528,7 +540,7 @@ ginscon(int as, vlong c, Node *n2) nodconst(&n1, types[TINT64], c); - if(as != AMOVQ && (c < -1LL<<31 || c >= 1LL<<31)) { + if(as != AMOVQ && (c < -(1LL<<31) || c >= 1LL<<31)) { // cannot have 64-bit immediokate in ADD, etc. // instead, MOV into register first. regalloc(&ntmp, types[TINT64], N); @@ -550,6 +562,7 @@ ismem(Node *n) { switch(n->op) { case OITAB: + case OSPTR: case OLEN: case OCAP: case OINDREG: @@ -1055,41 +1068,27 @@ gins(int as, Node *f, Node *t) return p; } -// Generate an instruction referencing *n -// to force segv on nil pointer dereference. void -checkref(Node *n, int force) +fixlargeoffset(Node *n) { - Node m; + Node a; - if(!force && isptr[n->type->etype] && n->type->type->width < unmappedzero) + if(n == N) return; - - regalloc(&m, types[TUINTPTR], n); - cgen(n, &m); - m.xoffset = 0; - m.op = OINDREG; - m.type = types[TUINT8]; - gins(ATESTB, nodintconst(0), &m); - regfree(&m); -} - -static void -checkoffset(Addr *a, int canemitcode) -{ - Prog *p; - - if(a->offset < unmappedzero) + if(n->op != OINDREG) return; - if(!canemitcode) - fatal("checkoffset %#llx, cannot emit code", a->offset); - - // cannot rely on unmapped nil page at 0 to catch - // reference with large offset. instead, emit explicit - // test of 0(reg). - p = gins(ATESTB, nodintconst(0), N); - p->to = *a; - p->to.offset = 0; + if(n->val.u.reg == D_SP) // stack offset cannot be large + return; + if(n->xoffset != (int32)n->xoffset) { + // offset too large, add to register instead. + a = *n; + a.op = OREGISTER; + a.type = types[tptr]; + a.xoffset = 0; + cgen_checknil(&a); + ginscon(optoas(OADD, types[tptr]), n->xoffset, &a); + n->xoffset = 0; + } } /* @@ -1149,7 +1148,6 @@ naddr(Node *n, Addr *a, int canemitcode) a->offset = n->xoffset; if(a->offset != (int32)a->offset) yyerror("offset %lld too large for OINDREG", a->offset); - checkoffset(a, canemitcode); break; case OPARAM: @@ -1268,8 +1266,16 @@ naddr(Node *n, Addr *a, int canemitcode) break; // itab(nil) a->etype = tptr; a->width = widthptr; - if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero) - checkoffset(a, canemitcode); + break; + + case OSPTR: + // pointer in a string or slice + naddr(n->left, a, canemitcode); + if(a->type == D_CONST && a->offset == 0) + break; // ptr(nil) + a->etype = simtype[TUINTPTR]; + a->offset += Array_array; + a->width = widthptr; break; case OLEN: @@ -1280,8 +1286,6 @@ naddr(Node *n, Addr *a, int canemitcode) a->etype = simtype[TUINT]; a->offset += Array_nel; a->width = widthint; - if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero) - checkoffset(a, canemitcode); break; case OCAP: @@ -1292,8 +1296,6 @@ naddr(Node *n, Addr *a, int canemitcode) a->etype = simtype[TUINT]; a->offset += Array_cap; a->width = widthint; - if(a->offset >= unmappedzero && a->offset-Array_cap < unmappedzero) - checkoffset(a, canemitcode); break; // case OADD: @@ -2033,18 +2035,21 @@ odot: n1.xoffset = oary[0]; } else { cgen(nn, reg); + cgen_checknil(reg); n1.xoffset = -(oary[0]+1); } for(i=1; i<o; i++) { if(oary[i] >= 0) - fatal("cant happen"); + fatal("can't happen"); gins(AMOVQ, &n1, reg); + cgen_checknil(reg); n1.xoffset = -(oary[i]+1); } a->type = D_NONE; a->index = D_NONE; + fixlargeoffset(&n1); naddr(&n1, a, 1); goto yes; @@ -2105,16 +2110,6 @@ oindex: o |= OAddable; } - if(!(o & ODynam) && l->type->width >= unmappedzero && l->op == OIND) { - // cannot rely on page protections to - // catch array ptr == 0, so dereference. - n2 = *reg; - n2.xoffset = 0; - n2.op = OINDREG; - n2.type = types[TUINT8]; - gins(ATESTB, nodintconst(0), &n2); - } - // check bounds if(!debug['B'] && !n->bounded) { // check bounds @@ -2216,6 +2211,7 @@ oindex_const: n2 = *reg; n2.op = OINDREG; n2.xoffset = v*w; + fixlargeoffset(&n2); a->type = D_NONE; a->index = D_NONE; naddr(&n2, a, 1); @@ -2228,6 +2224,7 @@ oindex_const: reg->op = OREGISTER; } n1.xoffset += v*w; + fixlargeoffset(&n1); a->type = D_NONE; a->index= D_NONE; naddr(&n1, a, 1); @@ -2263,6 +2260,7 @@ oindex_const_sudo: n2 = *reg; n2.op = OINDREG; n2.xoffset = v*w; + fixlargeoffset(&n2); a->type = D_NONE; a->index = D_NONE; naddr(&n2, a, 1); diff --git a/src/cmd/6g/opt.h b/src/cmd/6g/opt.h index 9b0ea1b5a..3dcc3d747 100644 --- a/src/cmd/6g/opt.h +++ b/src/cmd/6g/opt.h @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include "../gc/popt.h" + #define Z N #define Adr Addr @@ -50,9 +52,10 @@ typedef struct Rgn Rgn; // A Reg is a wrapper around a single Prog (one instruction) that holds // register optimization information while the optimizer runs. // r->prog is the instruction. -// r->prog->regp points back to r. +// r->prog->opt points back to r. struct Reg { + Flow f; Bits set; // variables written by this instruction. Bits use1; // variables read by prog->from. @@ -66,19 +69,6 @@ struct Reg Bits act; int32 regu; // register used bitmap - int32 rpo; // reverse post ordering - int32 active; - - uint16 loop; // x5 for every loop - uchar refset; // diagnostic generated - - Reg* p1; // predecessors of this instruction: p1, - Reg* p2; // and then p2 linked though p2link. - Reg* p2link; - Reg* s1; // successors of this instruction (at most two: s1 and s2). - Reg* s2; - Reg* link; // next instruction in function code - Prog* prog; // actual instruction }; #define R ((Reg*)0) @@ -93,11 +83,7 @@ struct Rgn EXTERN int32 exregoffset; // not set EXTERN int32 exfregoffset; // not set -EXTERN Reg* firstr; -EXTERN Reg* lastr; EXTERN Reg zreg; -EXTERN Reg* freer; -EXTERN Reg** rpo2r; EXTERN Rgn region[NRGN]; EXTERN Rgn* rgp; EXTERN int nregion; @@ -111,7 +97,6 @@ EXTERN Bits addrs; EXTERN Bits ovar; EXTERN int change; EXTERN int32 maxnr; -EXTERN int32* idom; EXTERN struct { @@ -126,43 +111,89 @@ EXTERN struct /* * reg.c */ -Reg* rega(void); int rcmp(const void*, const void*); void regopt(Prog*); void addmove(Reg*, int, int, int); Bits mkvar(Reg*, Adr*); void prop(Reg*, Bits, Bits); -void loopit(Reg*, int32); void synch(Reg*, Bits); uint32 allreg(uint32, Rgn*); void paint1(Reg*, int); uint32 paint2(Reg*, int); void paint3(Reg*, int, int32, int); void addreg(Adr*, int); -void dumpone(Reg*); -void dumpit(char*, Reg*); -int noreturn(Prog *p); +void dumpone(Flow*, int); +void dumpit(char*, Flow*, int); /* * peep.c */ -void peep(void); -void excise(Reg*); -Reg* uniqp(Reg*); -Reg* uniqs(Reg*); -int regtyp(Adr*); -int anyvar(Adr*); -int subprop(Reg*); -int copyprop(Reg*); -int copy1(Adr*, Adr*, Reg*, int); +void peep(Prog*); +void excise(Flow*); int copyu(Prog*, Adr*, Adr*); -int copyas(Adr*, Adr*); -int copyau(Adr*, Adr*); -int copysub(Adr*, Adr*, Adr*, int); -int copysub1(Prog*, Adr*, Adr*, int); - int32 RtoB(int); int32 FtoB(int); int BtoR(int32); int BtoF(int32); + +/* + * prog.c + */ +typedef struct ProgInfo ProgInfo; +struct ProgInfo +{ + uint32 flags; // the bits below + uint32 reguse; // required registers used by this instruction + uint32 regset; // required registers set by this instruction + uint32 regindex; // registers used by addressing mode +}; + +enum +{ + // Pseudo-op, like TEXT, GLOBL, TYPE, PCDATA, FUNCDATA. + Pseudo = 1<<1, + + // There's nothing to say about the instruction, + // but it's still okay to see. + OK = 1<<2, + + // Size of right-side write, or right-side read if no write. + SizeB = 1<<3, + SizeW = 1<<4, + SizeL = 1<<5, + SizeQ = 1<<6, + SizeF = 1<<7, // float aka float32 + SizeD = 1<<8, // double aka float64 + + // Left side: address taken, read, write. + LeftAddr = 1<<9, + LeftRead = 1<<10, + LeftWrite = 1<<11, + + // Right side: address taken, read, write. + RightAddr = 1<<12, + RightRead = 1<<13, + RightWrite = 1<<14, + + // Set, use, or kill of carry bit. + // Kill means we never look at the carry bit after this kind of instruction. + SetCarry = 1<<15, + UseCarry = 1<<16, + KillCarry = 1<<17, + + // Instruction kinds + Move = 1<<18, // straight move + Conv = 1<<19, // size conversion + Cjmp = 1<<20, // conditional jump + Break = 1<<21, // breaks control flow (no fallthrough) + Call = 1<<22, // function call + Jump = 1<<23, // jump + Skip = 1<<24, // data instruction + + // Special cases for register use. + ShiftCX = 1<<25, // possible shift by CX + ImulAXDX = 1<<26, // possible multiply into DX:AX +}; + +void proginfo(ProgInfo*, Prog*); diff --git a/src/cmd/6g/peep.c b/src/cmd/6g/peep.c index cd2881ec4..5ccf90103 100644 --- a/src/cmd/6g/peep.c +++ b/src/cmd/6g/peep.c @@ -33,61 +33,50 @@ #include "gg.h" #include "opt.h" -static void conprop(Reg *r); -static void elimshortmov(Reg *r); -static int prevl(Reg *r, int reg); -static void pushback(Reg *r); -static int regconsttyp(Adr*); +static void conprop(Flow *r); +static void elimshortmov(Graph *g); +static int prevl(Flow *r, int reg); +static void pushback(Flow *r); +static int regconsttyp(Adr*); +static int subprop(Flow*); +static int copyprop(Graph*, Flow*); +static int copy1(Adr*, Adr*, Flow*, int); +static int copyas(Adr*, Adr*); +static int copyau(Adr*, Adr*); +static int copysub(Adr*, Adr*, Adr*, int); + +static uint32 gactive; // do we need the carry bit static int needc(Prog *p) { + ProgInfo info; + while(p != P) { - switch(p->as) { - case AADCL: - case AADCQ: - case ASBBL: - case ASBBQ: - case ARCRB: - case ARCRW: - case ARCRL: - case ARCRQ: + proginfo(&info, p); + if(info.flags & UseCarry) return 1; - case AADDB: - case AADDW: - case AADDL: - case AADDQ: - case ASUBB: - case ASUBW: - case ASUBL: - case ASUBQ: - case AJMP: - case ARET: - case ACALL: + if(info.flags & (SetCarry|KillCarry)) return 0; - default: - if(p->to.type == D_BRANCH) - return 0; - } p = p->link; } return 0; } -static Reg* -rnops(Reg *r) +static Flow* +rnops(Flow *r) { Prog *p; - Reg *r1; + Flow *r1; - if(r != R) + if(r != nil) for(;;) { p = r->prog; if(p->as != ANOP || p->from.type != D_NONE || p->to.type != D_NONE) break; r1 = uniqs(r); - if(r1 == R) + if(r1 == nil) break; r = r1; } @@ -95,57 +84,26 @@ rnops(Reg *r) } void -peep(void) +peep(Prog *firstp) { - Reg *r, *r1, *r2; + Flow *r, *r1; + Graph *g; Prog *p, *p1; int t; - /* - * complete R structure - */ - t = 0; - for(r=firstr; r!=R; r=r1) { - r1 = r->link; - if(r1 == R) - break; - p = r->prog->link; - while(p != r1->prog) - switch(p->as) { - default: - r2 = rega(); - r->link = r2; - r2->link = r1; - - r2->prog = p; - p->reg = r2; - - r2->p1 = r; - r->s1 = r2; - r2->s1 = r1; - r1->p1 = r2; - - r = r2; - t++; - - case ADATA: - case AGLOBL: - case ANAME: - case ASIGNAME: - case ALOCALS: - case ATYPE: - p = p->link; - } - } - + g = flowstart(firstp, sizeof(Flow)); + if(g == nil) + return; + gactive = 0; + // byte, word arithmetic elimination. - elimshortmov(r); + elimshortmov(g); // constant propagation // find MOV $con,R followed by // another MOV $con,R without // setting R in the interim - for(r=firstr; r!=R; r=r->link) { + for(r=g->start; r!=nil; r=r->link) { p = r->prog; switch(p->as) { case ALEAL: @@ -171,10 +129,10 @@ peep(void) loop1: if(debug['P'] && debug['v']) - dumpit("loop1", firstr); + dumpit("loop1", g->start, 0); t = 0; - for(r=firstr; r!=R; r=r->link) { + for(r=g->start; r!=nil; r=r->link) { p = r->prog; switch(p->as) { case AMOVL: @@ -183,11 +141,11 @@ loop1: case AMOVSD: if(regtyp(&p->to)) if(regtyp(&p->from)) { - if(copyprop(r)) { + if(copyprop(g, r)) { excise(r); t++; } else - if(subprop(r) && copyprop(r)) { + if(subprop(r) && copyprop(g, r)) { excise(r); t++; } @@ -200,7 +158,7 @@ loop1: case AMOVWLSX: if(regtyp(&p->to)) { r1 = rnops(uniqs(r)); - if(r1 != R) { + if(r1 != nil) { p1 = r1->prog; if(p->as == p1->as && p->to.type == p1->from.type){ p1->as = AMOVL; @@ -219,7 +177,7 @@ loop1: case AMOVQL: if(regtyp(&p->to)) { r1 = rnops(uniqs(r)); - if(r1 != R) { + if(r1 != nil) { p1 = r1->prog; if(p->as == p1->as && p->to.type == p1->from.type){ p1->as = AMOVQ; @@ -302,7 +260,7 @@ loop1: // can be replaced by MOVAPD, which moves the pair of float64s // instead of just the lower one. We only use the lower one, but // the processor can do better if we do moves using both. - for(r=firstr; r!=R; r=r->link) { + for(r=g->start; r!=nil; r=r->link) { p = r->prog; if(p->as == AMOVLQZX) if(regtyp(&p->from)) @@ -319,7 +277,7 @@ loop1: // load pipelining // push any load from memory as early as possible // to give it time to complete before use. - for(r=firstr; r!=R; r=r->link) { + for(r=g->start; r!=nil; r=r->link) { p = r->prog; switch(p->as) { case AMOVB: @@ -331,17 +289,19 @@ loop1: pushback(r); } } + + flowend(g); } static void -pushback(Reg *r0) +pushback(Flow *r0) { - Reg *r, *b; + Flow *r, *b; Prog *p0, *p, t; - b = R; + b = nil; p0 = r0->prog; - for(r=uniqp(r0); r!=R && uniqs(r)!=R; r=uniqp(r)) { + for(r=uniqp(r0); r!=nil && uniqs(r)!=nil; r=uniqp(r)) { p = r->prog; if(p->as != ANOP) { if(!regconsttyp(&p->from) || !regtyp(&p->to)) @@ -354,11 +314,11 @@ pushback(Reg *r0) b = r; } - if(b == R) { + if(b == nil) { if(debug['v']) { print("no pushback: %P\n", r0->prog); if(r) - print("\t%P [%d]\n", r->prog, uniqs(r)!=R); + print("\t%P [%d]\n", r->prog, uniqs(r)!=nil); } return; } @@ -401,7 +361,7 @@ pushback(Reg *r0) } void -excise(Reg *r) +excise(Flow *r) { Prog *p; @@ -416,38 +376,6 @@ excise(Reg *r) ostats.ndelmov++; } -Reg* -uniqp(Reg *r) -{ - Reg *r1; - - r1 = r->p1; - if(r1 == R) { - r1 = r->p2; - if(r1 == R || r1->p2link != R) - return R; - } else - if(r->p2 != R) - return R; - return r1; -} - -Reg* -uniqs(Reg *r) -{ - Reg *r1; - - r1 = r->s1; - if(r1 == R) { - r1 = r->s2; - if(r1 == R) - return R; - } else - if(r->s2 != R) - return R; - return r1; -} - int regtyp(Adr *a) { @@ -468,13 +396,16 @@ regtyp(Adr *a) // when possible. a movb into a register // can smash the entire 32-bit register without // causing any trouble. +// +// TODO: Using the Q forms here instead of the L forms +// seems unnecessary, and it makes the instructions longer. static void -elimshortmov(Reg *r) +elimshortmov(Graph *g) { Prog *p; + Flow *r; - USED(r); - for(r=firstr; r!=R; r=r->link) { + for(r=g->start; r!=nil; r=r->link) { p = r->prog; if(regtyp(&p->to)) { switch(p->as) { @@ -557,6 +488,7 @@ elimshortmov(Reg *r) } } +// is 'a' a register or constant? static int regconsttyp(Adr *a) { @@ -574,38 +506,21 @@ regconsttyp(Adr *a) // is reg guaranteed to be truncated by a previous L instruction? static int -prevl(Reg *r0, int reg) +prevl(Flow *r0, int reg) { Prog *p; - Reg *r; + Flow *r; + ProgInfo info; - for(r=uniqp(r0); r!=R; r=uniqp(r)) { + for(r=uniqp(r0); r!=nil; r=uniqp(r)) { p = r->prog; if(p->to.type == reg) { - switch(p->as) { - case AADDL: - case AANDL: - case ADECL: - case ADIVL: - case AIDIVL: - case AIMULL: - case AINCL: - case AMOVL: - case AMULL: - case AORL: - case ARCLL: - case ARCRL: - case AROLL: - case ARORL: - case ASALL: - case ASARL: - case ASHLL: - case ASHRL: - case ASUBL: - case AXORL: - return 1; + proginfo(&info, p); + if(info.flags & RightWrite) { + if(info.flags & SizeL) + return 1; + return 0; } - return 0; } } return 0; @@ -625,12 +540,13 @@ prevl(Reg *r0, int reg) * hopefully, then the former or latter MOV * will be eliminated by copy propagation. */ -int -subprop(Reg *r0) +static int +subprop(Flow *r0) { Prog *p; + ProgInfo info; Adr *v1, *v2; - Reg *r; + Flow *r; int t; if(debug['P'] && debug['v']) @@ -648,104 +564,31 @@ subprop(Reg *r0) print("\tnot regtype %D; return 0\n", v2); return 0; } - for(r=uniqp(r0); r!=R; r=uniqp(r)) { + for(r=uniqp(r0); r!=nil; r=uniqp(r)) { if(debug['P'] && debug['v']) print("\t? %P\n", r->prog); - if(uniqs(r) == R) { + if(uniqs(r) == nil) { if(debug['P'] && debug['v']) print("\tno unique successor\n"); break; } p = r->prog; - switch(p->as) { - case ACALL: + proginfo(&info, p); + if(info.flags & Call) { if(debug['P'] && debug['v']) print("\tfound %P; return 0\n", p); return 0; + } - case AIMULL: - case AIMULQ: - case AIMULW: - if(p->to.type != D_NONE) - break; - goto giveup; - - case ARCLB: - case ARCLL: - case ARCLQ: - case ARCLW: - case ARCRB: - case ARCRL: - case ARCRQ: - case ARCRW: - case AROLB: - case AROLL: - case AROLQ: - case AROLW: - case ARORB: - case ARORL: - case ARORQ: - case ARORW: - case ASALB: - case ASALL: - case ASALQ: - case ASALW: - case ASARB: - case ASARL: - case ASARQ: - case ASARW: - case ASHLB: - case ASHLL: - case ASHLQ: - case ASHLW: - case ASHRB: - case ASHRL: - case ASHRQ: - case ASHRW: - if(p->from.type == D_CONST) - break; - goto giveup; - - case ADIVB: - case ADIVL: - case ADIVQ: - case ADIVW: - case AIDIVB: - case AIDIVL: - case AIDIVQ: - case AIDIVW: - case AIMULB: - case AMULB: - case AMULL: - case AMULQ: - case AMULW: - - case AREP: - case AREPN: - - case ACWD: - case ACDQ: - case ACQO: - - case ASTOSB: - case ASTOSL: - case ASTOSQ: - case AMOVSB: - case AMOVSL: - case AMOVSQ: - giveup: + if(info.reguse | info.regset) { if(debug['P'] && debug['v']) print("\tfound %P; return 0\n", p); return 0; - - case AMOVL: - case AMOVQ: - case AMOVSS: - case AMOVSD: - if(p->to.type == v1->type) - goto gotit; - break; } + + if((info.flags & Move) && (info.flags & (SizeL|SizeQ|SizeF|SizeD)) && p->to.type == v1->type) + goto gotit; + if(copyau(&p->from, v2) || copyau(&p->to, v2)) { if(debug['P'] && debug['v']) @@ -798,13 +641,13 @@ gotit: * set v1 F=1 * set v2 return success */ -int -copyprop(Reg *r0) +static int +copyprop(Graph *g, Flow *r0) { Prog *p; Adr *v1, *v2; - Reg *r; + USED(g); if(debug['P'] && debug['v']) print("copyprop %P\n", r0->prog); p = r0->prog; @@ -812,37 +655,36 @@ copyprop(Reg *r0) v2 = &p->to; if(copyas(v1, v2)) return 1; - for(r=firstr; r!=R; r=r->link) - r->active = 0; + gactive++; return copy1(v1, v2, r0->s1, 0); } -int -copy1(Adr *v1, Adr *v2, Reg *r, int f) +static int +copy1(Adr *v1, Adr *v2, Flow *r, int f) { int t; Prog *p; - if(r->active) { + if(r->active == gactive) { if(debug['P']) print("act set; return 1\n"); return 1; } - r->active = 1; + r->active = gactive; if(debug['P']) print("copy %D->%D f=%d\n", v1, v2, f); - for(; r != R; r = r->s1) { + for(; r != nil; r = r->s1) { p = r->prog; if(debug['P']) print("%P", p); - if(!f && uniqp(r) == R) { + if(!f && uniqp(r) == nil) { f = 1; if(debug['P']) print("; merge; f=%d", f); } t = copyu(p, v2, A); switch(t) { - case 2: /* rar, cant split */ + case 2: /* rar, can't split */ if(debug['P']) print("; %D rar; return 0\n", v2); return 0; @@ -905,255 +747,10 @@ copy1(Adr *v1, Adr *v2, Reg *r, int f) int copyu(Prog *p, Adr *v, Adr *s) { + ProgInfo info; switch(p->as) { - - default: - if(debug['P']) - print("unknown op %A\n", p->as); - /* SBBL; ADCL; FLD1; SAHF */ - return 2; - - - case ANEGB: - case ANEGW: - case ANEGL: - case ANEGQ: - case ANOTB: - case ANOTW: - case ANOTL: - case ANOTQ: - if(copyas(&p->to, v)) - return 2; - break; - - case ALEAL: /* lhs addr, rhs store */ - case ALEAQ: - if(copyas(&p->from, v)) - return 2; - - - case ANOP: /* rhs store */ - case AMOVL: - case AMOVQ: - case AMOVBLSX: - case AMOVBLZX: - case AMOVBQSX: - case AMOVBQZX: - case AMOVLQSX: - case AMOVLQZX: - case AMOVWLSX: - case AMOVWLZX: - case AMOVWQSX: - case AMOVWQZX: - case AMOVQL: - - case AMOVSS: - case AMOVSD: - case ACVTSD2SL: - case ACVTSD2SQ: - case ACVTSD2SS: - case ACVTSL2SD: - case ACVTSL2SS: - case ACVTSQ2SD: - case ACVTSQ2SS: - case ACVTSS2SD: - case ACVTSS2SL: - case ACVTSS2SQ: - case ACVTTSD2SL: - case ACVTTSD2SQ: - case ACVTTSS2SL: - case ACVTTSS2SQ: - if(copyas(&p->to, v)) { - if(s != A) - return copysub(&p->from, v, s, 1); - if(copyau(&p->from, v)) - return 4; - return 3; - } - goto caseread; - - case ARCLB: - case ARCLL: - case ARCLQ: - case ARCLW: - case ARCRB: - case ARCRL: - case ARCRQ: - case ARCRW: - case AROLB: - case AROLL: - case AROLQ: - case AROLW: - case ARORB: - case ARORL: - case ARORQ: - case ARORW: - case ASALB: - case ASALL: - case ASALQ: - case ASALW: - case ASARB: - case ASARL: - case ASARQ: - case ASARW: - case ASHLB: - case ASHLL: - case ASHLQ: - case ASHLW: - case ASHRB: - case ASHRL: - case ASHRQ: - case ASHRW: - if(copyas(&p->to, v)) - return 2; - if(copyas(&p->from, v)) - if(p->from.type == D_CX) - return 2; - goto caseread; - - case AADDB: /* rhs rar */ - case AADDL: - case AADDQ: - case AADDW: - case AANDB: - case AANDL: - case AANDQ: - case AANDW: - case ADECL: - case ADECQ: - case ADECW: - case AINCL: - case AINCQ: - case AINCW: - case ASUBB: - case ASUBL: - case ASUBQ: - case ASUBW: - case AORB: - case AORL: - case AORQ: - case AORW: - case AXORB: - case AXORL: - case AXORQ: - case AXORW: - case AMOVB: - case AMOVW: - - case AADDSD: - case AADDSS: - case ACMPSD: - case ACMPSS: - case ADIVSD: - case ADIVSS: - case AMAXSD: - case AMAXSS: - case AMINSD: - case AMINSS: - case AMULSD: - case AMULSS: - case ARCPSS: - case ARSQRTSS: - case ASQRTSD: - case ASQRTSS: - case ASUBSD: - case ASUBSS: - case AXORPD: - if(copyas(&p->to, v)) - return 2; - goto caseread; - - case ACMPL: /* read only */ - case ACMPW: - case ACMPB: - case ACMPQ: - - case ACOMISD: - case ACOMISS: - case AUCOMISD: - case AUCOMISS: - caseread: - if(s != A) { - if(copysub(&p->from, v, s, 1)) - return 1; - return copysub(&p->to, v, s, 1); - } - if(copyau(&p->from, v)) - return 1; - if(copyau(&p->to, v)) - return 1; - break; - - case AJGE: /* no reference */ - case AJNE: - case AJLE: - case AJEQ: - case AJHI: - case AJLS: - case AJMI: - case AJPL: - case AJGT: - case AJLT: - case AJCC: - case AJCS: - - case AADJSP: - case AWAIT: - case ACLD: - break; - - case AIMULL: - case AIMULQ: - case AIMULW: - if(p->to.type != D_NONE) { - if(copyas(&p->to, v)) - return 2; - goto caseread; - } - - case ADIVB: - case ADIVL: - case ADIVQ: - case ADIVW: - case AIDIVB: - case AIDIVL: - case AIDIVQ: - case AIDIVW: - case AIMULB: - case AMULB: - case AMULL: - case AMULQ: - case AMULW: - - case ACWD: - case ACDQ: - case ACQO: - if(v->type == D_AX || v->type == D_DX) - return 2; - goto caseread; - - case AREP: - case AREPN: - if(v->type == D_CX) - return 2; - goto caseread; - - case AMOVSB: - case AMOVSL: - case AMOVSQ: - if(v->type == D_DI || v->type == D_SI) - return 2; - goto caseread; - - case ASTOSB: - case ASTOSL: - case ASTOSQ: - if(v->type == D_AX || v->type == D_DI) - return 2; - goto caseread; - - case AJMP: /* funny */ + case AJMP: if(s != A) { if(copysub(&p->to, v, s, 1)) return 1; @@ -1163,12 +760,12 @@ copyu(Prog *p, Adr *v, Adr *s) return 1; return 0; - case ARET: /* funny */ + case ARET: if(s != A) return 1; return 3; - case ACALL: /* funny */ + case ACALL: if(REGEXT && v->type <= REGEXT && v->type > exregoffset) return 2; if(REGARG >= 0 && v->type == (uchar)REGARG) @@ -1185,11 +782,47 @@ copyu(Prog *p, Adr *v, Adr *s) return 4; return 3; - case ATEXT: /* funny */ + case ATEXT: if(REGARG >= 0 && v->type == (uchar)REGARG) return 3; return 0; } + + proginfo(&info, p); + + if((info.reguse|info.regset) & RtoB(v->type)) + return 2; + + if(info.flags & LeftAddr) + if(copyas(&p->from, v)) + return 2; + + if((info.flags & (RightRead|RightWrite)) == (RightRead|RightWrite)) + if(copyas(&p->to, v)) + return 2; + + if(info.flags & RightWrite) { + if(copyas(&p->to, v)) { + if(s != A) + return copysub(&p->from, v, s, 1); + if(copyau(&p->from, v)) + return 4; + return 3; + } + } + + if(info.flags & (LeftAddr|LeftRead|LeftWrite|RightAddr|RightRead|RightWrite)) { + if(s != A) { + if(copysub(&p->from, v, s, 1)) + return 1; + return copysub(&p->to, v, s, 1); + } + if(copyau(&p->from, v)) + return 1; + if(copyau(&p->to, v)) + return 1; + } + return 0; } @@ -1198,7 +831,7 @@ copyu(Prog *p, Adr *v, Adr *s) * could be set/use depending on * semantics */ -int +static int copyas(Adr *a, Adr *v) { if(a->type != v->type) @@ -1211,10 +844,23 @@ copyas(Adr *a, Adr *v) return 0; } +int +sameaddr(Addr *a, Addr *v) +{ + if(a->type != v->type) + return 0; + if(regtyp(v)) + return 1; + if(v->type == D_AUTO || v->type == D_PARAM) + if(v->offset == a->offset) + return 1; + return 0; +} + /* * either direct or indirect */ -int +static int copyau(Adr *a, Adr *v) { @@ -1242,7 +888,7 @@ copyau(Adr *a, Adr *v) * substitute s for v in a * return failure to substitute */ -int +static int copysub(Adr *a, Adr *v, Adr *s, int f) { int t; @@ -1275,9 +921,9 @@ copysub(Adr *a, Adr *v, Adr *s, int f) } static void -conprop(Reg *r0) +conprop(Flow *r0) { - Reg *r; + Flow *r; Prog *p, *p0; int t; Adr *v0; @@ -1288,9 +934,9 @@ conprop(Reg *r0) loop: r = uniqs(r); - if(r == R || r == r0) + if(r == nil || r == r0) return; - if(uniqp(r) == R) + if(uniqp(r) == nil) return; p = r->prog; @@ -1318,3 +964,18 @@ loop: break; } } + +int +smallindir(Addr *a, Addr *reg) +{ + return regtyp(reg) && + a->type == D_INDIR + reg->type && + a->index == D_NONE && + 0 <= a->offset && a->offset < 4096; +} + +int +stackaddr(Addr *a) +{ + return regtyp(a) && a->type == D_SP; +} diff --git a/src/cmd/6g/prog.c b/src/cmd/6g/prog.c new file mode 100644 index 000000000..90571a21a --- /dev/null +++ b/src/cmd/6g/prog.c @@ -0,0 +1,313 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include <u.h> +#include <libc.h> +#include "gg.h" +#include "opt.h" + +// Matches real RtoB but can be used in global initializer. +#define RtoB(r) (1<<((r)-D_AX)) + +enum { + AX = RtoB(D_AX), + BX = RtoB(D_BX), + CX = RtoB(D_CX), + DX = RtoB(D_DX), + DI = RtoB(D_DI), + SI = RtoB(D_SI), + + LeftRdwr = LeftRead | LeftWrite, + RightRdwr = RightRead | RightWrite, +}; + +#undef RtoB + +// This table gives the basic information about instruction +// generated by the compiler and processed in the optimizer. +// See opt.h for bit definitions. +// +// Instructions not generated need not be listed. +// As an exception to that rule, we typically write down all the +// size variants of an operation even if we just use a subset. +// +// The table is formatted for 8-space tabs. +static ProgInfo progtable[ALAST] = { + [ATYPE]= {Pseudo | Skip}, + [ATEXT]= {Pseudo}, + [AFUNCDATA]= {Pseudo}, + [APCDATA]= {Pseudo}, + [AUNDEF]= {OK}, + [AUSEFIELD]= {OK}, + [ACHECKNIL]= {LeftRead}, + + // NOP is an internal no-op that also stands + // for USED and SET annotations, not the Intel opcode. + [ANOP]= {LeftRead | RightWrite}, + + [AADCL]= {SizeL | LeftRead | RightRdwr | SetCarry | UseCarry}, + [AADCQ]= {SizeQ | LeftRead | RightRdwr | SetCarry | UseCarry}, + [AADCW]= {SizeW | LeftRead | RightRdwr | SetCarry | UseCarry}, + + [AADDB]= {SizeB | LeftRead | RightRdwr | SetCarry}, + [AADDL]= {SizeL | LeftRead | RightRdwr | SetCarry}, + [AADDW]= {SizeW | LeftRead | RightRdwr | SetCarry}, + [AADDQ]= {SizeQ | LeftRead | RightRdwr | SetCarry}, + + [AADDSD]= {SizeD | LeftRead | RightRdwr}, + [AADDSS]= {SizeF | LeftRead | RightRdwr}, + + [AANDB]= {SizeB | LeftRead | RightRdwr | SetCarry}, + [AANDL]= {SizeL | LeftRead | RightRdwr | SetCarry}, + [AANDQ]= {SizeQ | LeftRead | RightRdwr | SetCarry}, + [AANDW]= {SizeW | LeftRead | RightRdwr | SetCarry}, + + [ACALL]= {RightAddr | Call | KillCarry}, + + [ACDQ]= {OK, AX, AX | DX}, + [ACQO]= {OK, AX, AX | DX}, + [ACWD]= {OK, AX, AX | DX}, + + [ACLD]= {OK}, + [ASTD]= {OK}, + + [ACMPB]= {SizeB | LeftRead | RightRead | SetCarry}, + [ACMPL]= {SizeL | LeftRead | RightRead | SetCarry}, + [ACMPQ]= {SizeQ | LeftRead | RightRead | SetCarry}, + [ACMPW]= {SizeW | LeftRead | RightRead | SetCarry}, + + [ACOMISD]= {SizeD | LeftRead | RightRead | SetCarry}, + [ACOMISS]= {SizeF | LeftRead | RightRead | SetCarry}, + + [ACVTSD2SL]= {SizeL | LeftRead | RightWrite | Conv}, + [ACVTSD2SQ]= {SizeQ | LeftRead | RightWrite | Conv}, + [ACVTSD2SS]= {SizeF | LeftRead | RightWrite | Conv}, + [ACVTSL2SD]= {SizeD | LeftRead | RightWrite | Conv}, + [ACVTSL2SS]= {SizeF | LeftRead | RightWrite | Conv}, + [ACVTSQ2SD]= {SizeD | LeftRead | RightWrite | Conv}, + [ACVTSQ2SS]= {SizeF | LeftRead | RightWrite | Conv}, + [ACVTSS2SD]= {SizeD | LeftRead | RightWrite | Conv}, + [ACVTSS2SL]= {SizeL | LeftRead | RightWrite | Conv}, + [ACVTSS2SQ]= {SizeQ | LeftRead | RightWrite | Conv}, + [ACVTTSD2SL]= {SizeL | LeftRead | RightWrite | Conv}, + [ACVTTSD2SQ]= {SizeQ | LeftRead | RightWrite | Conv}, + [ACVTTSS2SL]= {SizeL | LeftRead | RightWrite | Conv}, + [ACVTTSS2SQ]= {SizeQ | LeftRead | RightWrite | Conv}, + + [ADECB]= {SizeB | RightRdwr}, + [ADECL]= {SizeL | RightRdwr}, + [ADECQ]= {SizeQ | RightRdwr}, + [ADECW]= {SizeW | RightRdwr}, + + [ADIVB]= {SizeB | LeftRead | SetCarry, AX, AX}, + [ADIVL]= {SizeL | LeftRead | SetCarry, AX|DX, AX|DX}, + [ADIVQ]= {SizeQ | LeftRead | SetCarry, AX|DX, AX|DX}, + [ADIVW]= {SizeW | LeftRead | SetCarry, AX|DX, AX|DX}, + + [ADIVSD]= {SizeD | LeftRead | RightRdwr}, + [ADIVSS]= {SizeF | LeftRead | RightRdwr}, + + [AIDIVB]= {SizeB | LeftRead | SetCarry, AX, AX}, + [AIDIVL]= {SizeL | LeftRead | SetCarry, AX|DX, AX|DX}, + [AIDIVQ]= {SizeQ | LeftRead | SetCarry, AX|DX, AX|DX}, + [AIDIVW]= {SizeW | LeftRead | SetCarry, AX|DX, AX|DX}, + + [AIMULB]= {SizeB | LeftRead | SetCarry, AX, AX}, + [AIMULL]= {SizeL | LeftRead | ImulAXDX | SetCarry}, + [AIMULQ]= {SizeQ | LeftRead | ImulAXDX | SetCarry}, + [AIMULW]= {SizeW | LeftRead | ImulAXDX | SetCarry}, + + [AINCB]= {SizeB | RightRdwr}, + [AINCL]= {SizeL | RightRdwr}, + [AINCQ]= {SizeQ | RightRdwr}, + [AINCW]= {SizeW | RightRdwr}, + + [AJCC]= {Cjmp | UseCarry}, + [AJCS]= {Cjmp | UseCarry}, + [AJEQ]= {Cjmp | UseCarry}, + [AJGE]= {Cjmp | UseCarry}, + [AJGT]= {Cjmp | UseCarry}, + [AJHI]= {Cjmp | UseCarry}, + [AJLE]= {Cjmp | UseCarry}, + [AJLS]= {Cjmp | UseCarry}, + [AJLT]= {Cjmp | UseCarry}, + [AJMI]= {Cjmp | UseCarry}, + [AJNE]= {Cjmp | UseCarry}, + [AJOC]= {Cjmp | UseCarry}, + [AJOS]= {Cjmp | UseCarry}, + [AJPC]= {Cjmp | UseCarry}, + [AJPL]= {Cjmp | UseCarry}, + [AJPS]= {Cjmp | UseCarry}, + + [AJMP]= {Jump | Break | KillCarry}, + + [ALEAQ]= {LeftAddr | RightWrite}, + + [AMOVBLSX]= {SizeL | LeftRead | RightWrite | Conv}, + [AMOVBLZX]= {SizeL | LeftRead | RightWrite | Conv}, + [AMOVBQSX]= {SizeQ | LeftRead | RightWrite | Conv}, + [AMOVBQZX]= {SizeQ | LeftRead | RightWrite | Conv}, + [AMOVBWSX]= {SizeW | LeftRead | RightWrite | Conv}, + [AMOVBWZX]= {SizeW | LeftRead | RightWrite | Conv}, + [AMOVLQSX]= {SizeQ | LeftRead | RightWrite | Conv}, + [AMOVLQZX]= {SizeQ | LeftRead | RightWrite | Conv}, + [AMOVWLSX]= {SizeL | LeftRead | RightWrite | Conv}, + [AMOVWLZX]= {SizeL | LeftRead | RightWrite | Conv}, + [AMOVWQSX]= {SizeQ | LeftRead | RightWrite | Conv}, + [AMOVWQZX]= {SizeQ | LeftRead | RightWrite | Conv}, + [AMOVQL]= {SizeL | LeftRead | RightWrite | Conv}, + + [AMOVB]= {SizeB | LeftRead | RightWrite | Move}, + [AMOVL]= {SizeL | LeftRead | RightWrite | Move}, + [AMOVQ]= {SizeQ | LeftRead | RightWrite | Move}, + [AMOVW]= {SizeW | LeftRead | RightWrite | Move}, + + [AMOVSB]= {OK, DI|SI, DI|SI}, + [AMOVSL]= {OK, DI|SI, DI|SI}, + [AMOVSQ]= {OK, DI|SI, DI|SI}, + [AMOVSW]= {OK, DI|SI, DI|SI}, + + [AMOVSD]= {SizeD | LeftRead | RightWrite | Move}, + [AMOVSS]= {SizeF | LeftRead | RightWrite | Move}, + + // We use MOVAPD as a faster synonym for MOVSD. + [AMOVAPD]= {SizeD | LeftRead | RightWrite | Move}, + + [AMULB]= {SizeB | LeftRead | SetCarry, AX, AX}, + [AMULL]= {SizeL | LeftRead | SetCarry, AX, AX|DX}, + [AMULQ]= {SizeQ | LeftRead | SetCarry, AX, AX|DX}, + [AMULW]= {SizeW | LeftRead | SetCarry, AX, AX|DX}, + + [AMULSD]= {SizeD | LeftRead | RightRdwr}, + [AMULSS]= {SizeF | LeftRead | RightRdwr}, + + [ANEGB]= {SizeB | RightRdwr | SetCarry}, + [ANEGL]= {SizeL | RightRdwr | SetCarry}, + [ANEGQ]= {SizeQ | RightRdwr | SetCarry}, + [ANEGW]= {SizeW | RightRdwr | SetCarry}, + + [ANOTB]= {SizeB | RightRdwr}, + [ANOTL]= {SizeL | RightRdwr}, + [ANOTQ]= {SizeQ | RightRdwr}, + [ANOTW]= {SizeW | RightRdwr}, + + [AORB]= {SizeB | LeftRead | RightRdwr | SetCarry}, + [AORL]= {SizeL | LeftRead | RightRdwr | SetCarry}, + [AORQ]= {SizeQ | LeftRead | RightRdwr | SetCarry}, + [AORW]= {SizeW | LeftRead | RightRdwr | SetCarry}, + + [APOPQ]= {SizeQ | RightWrite}, + [APUSHQ]= {SizeQ | LeftRead}, + + [ARCLB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry}, + [ARCLL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry}, + [ARCLQ]= {SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry}, + [ARCLW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry}, + + [ARCRB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry}, + [ARCRL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry}, + [ARCRQ]= {SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry}, + [ARCRW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry}, + + [AREP]= {OK, CX, CX}, + [AREPN]= {OK, CX, CX}, + + [ARET]= {Break | KillCarry}, + + [AROLB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [AROLL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [AROLQ]= {SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [AROLW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry}, + + [ARORB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ARORL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ARORQ]= {SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ARORW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry}, + + [ASALB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ASALL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ASALQ]= {SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ASALW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry}, + + [ASARB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ASARL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ASARQ]= {SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ASARW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry}, + + [ASBBB]= {SizeB | LeftRead | RightRdwr | SetCarry | UseCarry}, + [ASBBL]= {SizeL | LeftRead | RightRdwr | SetCarry | UseCarry}, + [ASBBQ]= {SizeQ | LeftRead | RightRdwr | SetCarry | UseCarry}, + [ASBBW]= {SizeW | LeftRead | RightRdwr | SetCarry | UseCarry}, + + [ASHLB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ASHLL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ASHLQ]= {SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ASHLW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry}, + + [ASHRB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ASHRL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ASHRQ]= {SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ASHRW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry}, + + [ASTOSB]= {OK, AX|DI, DI}, + [ASTOSL]= {OK, AX|DI, DI}, + [ASTOSQ]= {OK, AX|DI, DI}, + [ASTOSW]= {OK, AX|DI, DI}, + + [ASUBB]= {SizeB | LeftRead | RightRdwr | SetCarry}, + [ASUBL]= {SizeL | LeftRead | RightRdwr | SetCarry}, + [ASUBQ]= {SizeQ | LeftRead | RightRdwr | SetCarry}, + [ASUBW]= {SizeW | LeftRead | RightRdwr | SetCarry}, + + [ASUBSD]= {SizeD | LeftRead | RightRdwr}, + [ASUBSS]= {SizeF | LeftRead | RightRdwr}, + + [ATESTB]= {SizeB | LeftRead | RightRead | SetCarry}, + [ATESTL]= {SizeL | LeftRead | RightRead | SetCarry}, + [ATESTQ]= {SizeQ | LeftRead | RightRead | SetCarry}, + [ATESTW]= {SizeW | LeftRead | RightRead | SetCarry}, + + [AUCOMISD]= {SizeD | LeftRead | RightRead}, + [AUCOMISS]= {SizeF | LeftRead | RightRead}, + + [AXCHGB]= {SizeB | LeftRdwr | RightRdwr}, + [AXCHGL]= {SizeL | LeftRdwr | RightRdwr}, + [AXCHGQ]= {SizeQ | LeftRdwr | RightRdwr}, + [AXCHGW]= {SizeW | LeftRdwr | RightRdwr}, + + [AXORB]= {SizeB | LeftRead | RightRdwr | SetCarry}, + [AXORL]= {SizeL | LeftRead | RightRdwr | SetCarry}, + [AXORQ]= {SizeQ | LeftRead | RightRdwr | SetCarry}, + [AXORW]= {SizeW | LeftRead | RightRdwr | SetCarry}, +}; + +void +proginfo(ProgInfo *info, Prog *p) +{ + *info = progtable[p->as]; + if(info->flags == 0) + fatal("unknown instruction %P", p); + + if((info->flags & ShiftCX) && p->from.type != D_CONST) + info->reguse |= CX; + + if(info->flags & ImulAXDX) { + if(p->to.type == D_NONE) { + info->reguse |= AX; + info->regset |= AX | DX; + } else { + info->flags |= RightRdwr; + } + } + + // Addressing makes some registers used. + if(p->from.type >= D_INDIR) + info->regindex |= RtoB(p->from.type-D_INDIR); + if(p->from.index != D_NONE) + info->regindex |= RtoB(p->from.index); + if(p->to.type >= D_INDIR) + info->regindex |= RtoB(p->to.type-D_INDIR); + if(p->to.index != D_NONE) + info->regindex |= RtoB(p->to.index); +} diff --git a/src/cmd/6g/reg.c b/src/cmd/6g/reg.c index ab826d431..63fd0deca 100644 --- a/src/cmd/6g/reg.c +++ b/src/cmd/6g/reg.c @@ -35,25 +35,10 @@ #define NREGVAR 32 /* 16 general + 16 floating */ #define REGBITS ((uint32)0xffffffff) -#define P2R(p) (Reg*)(p->reg) +static Reg* firstr; static int first = 1; -Reg* -rega(void) -{ - Reg *r; - - r = freer; - if(r == R) { - r = mal(sizeof(*r)); - } else - freer = r->link; - - *r = zreg; - return r; -} - int rcmp(const void *a1, const void *a2) { @@ -153,14 +138,14 @@ static char* regname[] = { static Node* regnodes[NREGVAR]; -static void fixjmp(Prog*); - void regopt(Prog *firstp) { Reg *r, *r1; Prog *p; - int i, z, nr; + Graph *g; + ProgInfo info; + int i, z; uint32 vreg; Bits bit; @@ -171,20 +156,8 @@ regopt(Prog *firstp) } fixjmp(firstp); - - // count instructions - nr = 0; - for(p=firstp; p!=P; p=p->link) - nr++; - // if too big dont bother - if(nr >= 10000) { -// print("********** %S is too big (%d)\n", curfn->nname->sym, nr); - return; - } - - firstr = R; - lastr = R; - + mergetemp(firstp); + /* * control flow is more complicated in generated go code * than in generated c code. define pseudo-variables for @@ -216,380 +189,46 @@ regopt(Prog *firstp) * allocate pcs * find use and set of variables */ - nr = 0; - for(p=firstp; p!=P; p=p->link) { - switch(p->as) { - case ADATA: - case AGLOBL: - case ANAME: - case ASIGNAME: - case ALOCALS: - case ATYPE: - continue; - } - r = rega(); - nr++; - if(firstr == R) { - firstr = r; - lastr = r; - } else { - lastr->link = r; - r->p1 = lastr; - lastr->s1 = r; - lastr = r; - } - r->prog = p; - p->reg = r; - - r1 = r->p1; - if(r1 != R) { - switch(r1->prog->as) { - case ARET: - case AJMP: - case AIRETL: - case AIRETQ: - r->p1 = R; - r1->s1 = R; - } - } + g = flowstart(firstp, sizeof(Reg)); + if(g == nil) + return; + firstr = (Reg*)g->start; + + for(r = firstr; r != R; r = (Reg*)r->f.link) { + p = r->f.prog; + proginfo(&info, p); // Avoid making variables for direct-called functions. if(p->as == ACALL && p->to.type == D_EXTERN) continue; - // Addressing makes some registers used. - if(p->from.type >= D_INDIR) - r->use1.b[0] |= RtoB(p->from.type-D_INDIR); - if(p->from.index != D_NONE) - r->use1.b[0] |= RtoB(p->from.index); - if(p->to.type >= D_INDIR) - r->use2.b[0] |= RtoB(p->to.type-D_INDIR); - if(p->to.index != D_NONE) - r->use2.b[0] |= RtoB(p->to.index); + r->use1.b[0] |= info.reguse | info.regindex; + r->set.b[0] |= info.regset; bit = mkvar(r, &p->from); - if(bany(&bit)) - switch(p->as) { - /* - * funny - */ - case ALEAL: - case ALEAQ: - setaddrs(bit); - break; - - /* - * left side read - */ - default: - for(z=0; z<BITS; z++) - r->use1.b[z] |= bit.b[z]; - break; - - /* - * left side read+write - */ - case AXCHGB: - case AXCHGW: - case AXCHGL: - case AXCHGQ: - for(z=0; z<BITS; z++) { - r->use1.b[z] |= bit.b[z]; - r->set.b[z] |= bit.b[z]; - } - break; + if(bany(&bit)) { + if(info.flags & LeftAddr) + setaddrs(bit); + if(info.flags & LeftRead) + for(z=0; z<BITS; z++) + r->use1.b[z] |= bit.b[z]; + if(info.flags & LeftWrite) + for(z=0; z<BITS; z++) + r->set.b[z] |= bit.b[z]; } bit = mkvar(r, &p->to); - if(bany(&bit)) - switch(p->as) { - default: - yyerror("reg: unknown op: %A", p->as); - break; - - /* - * right side read - */ - case ACMPB: - case ACMPL: - case ACMPQ: - case ACMPW: - case ACOMISS: - case ACOMISD: - case AUCOMISS: - case AUCOMISD: - case ATESTB: - case ATESTL: - case ATESTQ: - for(z=0; z<BITS; z++) - r->use2.b[z] |= bit.b[z]; - break; - - /* - * right side write - */ - case ALEAQ: - case ANOP: - case AMOVL: - case AMOVQ: - case AMOVB: - case AMOVW: - case AMOVBLSX: - case AMOVBLZX: - case AMOVBWSX: - case AMOVBWZX: - case AMOVBQSX: - case AMOVBQZX: - case AMOVLQSX: - case AMOVLQZX: - case AMOVWLSX: - case AMOVWLZX: - case AMOVWQSX: - case AMOVWQZX: - case AMOVQL: - case APOPQ: - - case AMOVSS: - case AMOVSD: - case ACVTSD2SL: - case ACVTSD2SQ: - case ACVTSD2SS: - case ACVTSL2SD: - case ACVTSL2SS: - case ACVTSQ2SD: - case ACVTSQ2SS: - case ACVTSS2SD: - case ACVTSS2SL: - case ACVTSS2SQ: - case ACVTTSD2SL: - case ACVTTSD2SQ: - case ACVTTSS2SL: - case ACVTTSS2SQ: - for(z=0; z<BITS; z++) - r->set.b[z] |= bit.b[z]; - break; - - /* - * right side read+write - */ - case AINCB: - case AINCL: - case AINCQ: - case AINCW: - case ADECB: - case ADECL: - case ADECQ: - case ADECW: - - case AADDB: - case AADDL: - case AADDQ: - case AADDW: - case AANDB: - case AANDL: - case AANDQ: - case AANDW: - case ASUBB: - case ASUBL: - case ASUBQ: - case ASUBW: - case AORB: - case AORL: - case AORQ: - case AORW: - case AXORB: - case AXORL: - case AXORQ: - case AXORW: - case ASALB: - case ASALL: - case ASALQ: - case ASALW: - case ASARB: - case ASARL: - case ASARQ: - case ASARW: - case ARCLB: - case ARCLL: - case ARCLQ: - case ARCLW: - case ARCRB: - case ARCRL: - case ARCRQ: - case ARCRW: - case AROLB: - case AROLL: - case AROLQ: - case AROLW: - case ARORB: - case ARORL: - case ARORQ: - case ARORW: - case ASHLB: - case ASHLL: - case ASHLQ: - case ASHLW: - case ASHRB: - case ASHRL: - case ASHRQ: - case ASHRW: - case AIMULL: - case AIMULQ: - case AIMULW: - case ANEGB: - case ANEGW: - case ANEGL: - case ANEGQ: - case ANOTL: - case ANOTQ: - case AADCL: - case AADCQ: - case ASBBL: - case ASBBQ: - - case ASETCC: - case ASETCS: - case ASETEQ: - case ASETGE: - case ASETGT: - case ASETHI: - case ASETLE: - case ASETLS: - case ASETLT: - case ASETMI: - case ASETNE: - case ASETOC: - case ASETOS: - case ASETPC: - case ASETPL: - case ASETPS: - - case AXCHGB: - case AXCHGW: - case AXCHGL: - case AXCHGQ: - - case AADDSD: - case AADDSS: - case ACMPSD: - case ACMPSS: - case ADIVSD: - case ADIVSS: - case AMAXSD: - case AMAXSS: - case AMINSD: - case AMINSS: - case AMULSD: - case AMULSS: - case ARCPSS: - case ARSQRTSS: - case ASQRTSD: - case ASQRTSS: - case ASUBSD: - case ASUBSS: - case AXORPD: - for(z=0; z<BITS; z++) { - r->set.b[z] |= bit.b[z]; - r->use2.b[z] |= bit.b[z]; - } - break; - - /* - * funny - */ - case ACALL: - setaddrs(bit); - break; - } - - switch(p->as) { - case AIMULL: - case AIMULQ: - case AIMULW: - if(p->to.type != D_NONE) - break; - - case AIDIVL: - case AIDIVW: - case AIDIVQ: - case ADIVL: - case ADIVW: - case ADIVQ: - case AMULL: - case AMULW: - case AMULQ: - r->set.b[0] |= RtoB(D_AX) | RtoB(D_DX); - r->use1.b[0] |= RtoB(D_AX) | RtoB(D_DX); - break; - - case AIDIVB: - case AIMULB: - case ADIVB: - case AMULB: - r->set.b[0] |= RtoB(D_AX); - r->use1.b[0] |= RtoB(D_AX); - break; - - case ACWD: - r->set.b[0] |= RtoB(D_AX) | RtoB(D_DX); - r->use1.b[0] |= RtoB(D_AX); - break; - - case ACDQ: - r->set.b[0] |= RtoB(D_DX); - r->use1.b[0] |= RtoB(D_AX); - break; - - case AREP: - case AREPN: - case ALOOP: - case ALOOPEQ: - case ALOOPNE: - r->set.b[0] |= RtoB(D_CX); - r->use1.b[0] |= RtoB(D_CX); - break; - - case AMOVSB: - case AMOVSL: - case AMOVSQ: - case AMOVSW: - case ACMPSB: - case ACMPSL: - case ACMPSQ: - case ACMPSW: - r->set.b[0] |= RtoB(D_SI) | RtoB(D_DI); - r->use1.b[0] |= RtoB(D_SI) | RtoB(D_DI); - break; - - case ASTOSB: - case ASTOSL: - case ASTOSQ: - case ASTOSW: - case ASCASB: - case ASCASL: - case ASCASQ: - case ASCASW: - r->set.b[0] |= RtoB(D_DI); - r->use1.b[0] |= RtoB(D_AX) | RtoB(D_DI); - break; - - case AINSB: - case AINSL: - case AINSW: - r->set.b[0] |= RtoB(D_DX) | RtoB(D_DI); - r->use1.b[0] |= RtoB(D_DI); - break; - - case AOUTSB: - case AOUTSL: - case AOUTSW: - r->set.b[0] |= RtoB(D_DI); - r->use1.b[0] |= RtoB(D_DX) | RtoB(D_DI); - break; + if(bany(&bit)) { + if(info.flags & RightAddr) + setaddrs(bit); + if(info.flags & RightRead) + for(z=0; z<BITS; z++) + r->use2.b[z] |= bit.b[z]; + if(info.flags & RightWrite) + for(z=0; z<BITS; z++) + r->set.b[z] |= bit.b[z]; } } - if(firstr == R) - return; for(i=0; i<nvar; i++) { Var *v = var+i; @@ -605,45 +244,16 @@ regopt(Prog *firstp) } if(debug['R'] && debug['v']) - dumpit("pass1", firstr); + dumpit("pass1", &firstr->f, 1); /* * pass 2 - * turn branch references to pointers - * build back pointers - */ - for(r=firstr; r!=R; r=r->link) { - p = r->prog; - if(p->to.type == D_BRANCH) { - if(p->to.u.branch == P) - fatal("pnil %P", p); - r1 = p->to.u.branch->reg; - if(r1 == R) - fatal("rnil %P", p); - if(r1 == r) { - //fatal("ref to self %P", p); - continue; - } - r->s2 = r1; - r->p2link = r1->p2; - r1->p2 = r; - } - } - - if(debug['R'] && debug['v']) - dumpit("pass2", firstr); - - /* - * pass 2.5 * find looping structure */ - for(r = firstr; r != R; r = r->link) - r->active = 0; - change = 0; - loopit(firstr, nr); + flowrpo(g); if(debug['R'] && debug['v']) - dumpit("pass2.5", firstr); + dumpit("pass2", &firstr->f, 1); /* * pass 3 @@ -652,17 +262,17 @@ regopt(Prog *firstp) */ loop1: change = 0; - for(r = firstr; r != R; r = r->link) - r->active = 0; - for(r = firstr; r != R; r = r->link) - if(r->prog->as == ARET) + for(r = firstr; r != R; r = (Reg*)r->f.link) + r->f.active = 0; + for(r = firstr; r != R; r = (Reg*)r->f.link) + if(r->f.prog->as == ARET) prop(r, zbits, zbits); loop11: /* pick up unreachable code */ i = 0; for(r = firstr; r != R; r = r1) { - r1 = r->link; - if(r1 && r1->active && !r->active) { + r1 = (Reg*)r->f.link; + if(r1 && r1->f.active && !r->f.active) { prop(r, zbits, zbits); i = 1; } @@ -673,7 +283,7 @@ loop11: goto loop1; if(debug['R'] && debug['v']) - dumpit("pass3", firstr); + dumpit("pass3", &firstr->f, 1); /* * pass 4 @@ -682,20 +292,20 @@ loop11: */ loop2: change = 0; - for(r = firstr; r != R; r = r->link) - r->active = 0; + for(r = firstr; r != R; r = (Reg*)r->f.link) + r->f.active = 0; synch(firstr, zbits); if(change) goto loop2; if(debug['R'] && debug['v']) - dumpit("pass4", firstr); + dumpit("pass4", &firstr->f, 1); /* * pass 4.5 * move register pseudo-variables into regu. */ - for(r = firstr; r != R; r = r->link) { + for(r = firstr; r != R; r = (Reg*)r->f.link) { r->regu = (r->refbehind.b[0] | r->set.b[0]) & REGBITS; r->set.b[0] &= ~REGBITS; @@ -719,26 +329,26 @@ loop2: for(z=0; z<BITS; z++) bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) & ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]); - if(bany(&bit) && !r->refset) { + if(bany(&bit) && !r->f.refset) { // should never happen - all variables are preset if(debug['w']) - print("%L: used and not set: %Q\n", r->prog->lineno, bit); - r->refset = 1; + print("%L: used and not set: %Q\n", r->f.prog->lineno, bit); + r->f.refset = 1; } } - for(r = firstr; r != R; r = r->link) + for(r = firstr; r != R; r = (Reg*)r->f.link) r->act = zbits; rgp = region; nregion = 0; - for(r = firstr; r != R; r = r->link) { + for(r = firstr; r != R; r = (Reg*)r->f.link) { for(z=0; z<BITS; z++) bit.b[z] = r->set.b[z] & ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]); - if(bany(&bit) && !r->refset) { + if(bany(&bit) && !r->f.refset) { if(debug['w']) - print("%L: set and not used: %Q\n", r->prog->lineno, bit); - r->refset = 1; - excise(r); + print("%L: set and not used: %Q\n", r->f.prog->lineno, bit); + r->f.refset = 1; + excise(&r->f); } for(z=0; z<BITS; z++) bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]); @@ -765,7 +375,7 @@ brk: qsort(region, nregion, sizeof(region[0]), rcmp); if(debug['R'] && debug['v']) - dumpit("pass5", firstr); + dumpit("pass5", &firstr->f, 1); /* * pass 6 @@ -791,19 +401,23 @@ brk: } if(debug['R'] && debug['v']) - dumpit("pass6", firstr); + dumpit("pass6", &firstr->f, 1); + + /* + * free aux structures. peep allocates new ones. + */ + flowend(g); + firstr = R; /* * pass 7 * peep-hole on basic block */ - if(!debug['R'] || debug['P']) { - peep(); - } + if(!debug['R'] || debug['P']) + peep(firstp); /* * eliminate nops - * free aux structures */ for(p=firstp; p!=P; p=p->link) { while(p->link != P && p->link->as == ANOP) @@ -813,11 +427,6 @@ brk: p->to.u.branch = p->to.u.branch->link; } - if(lastr != R) { - lastr->link = freer; - freer = firstr; - } - if(debug['R']) { if(ostats.ncvtreg || ostats.nspill || @@ -860,7 +469,7 @@ addmove(Reg *r, int bn, int rn, int f) clearp(p1); p1->loc = 9999; - p = r->prog; + p = r->f.prog; p1->link = p->link; p->link = p1; p1->lineno = p->lineno; @@ -1084,7 +693,7 @@ prop(Reg *r, Bits ref, Bits cal) Reg *r1, *r2; int z; - for(r1 = r; r1 != R; r1 = r1->p1) { + for(r1 = r; r1 != R; r1 = (Reg*)r1->f.p1) { for(z=0; z<BITS; z++) { ref.b[z] |= r1->refahead.b[z]; if(ref.b[z] != r1->refahead.b[z]) { @@ -1097,9 +706,9 @@ prop(Reg *r, Bits ref, Bits cal) change++; } } - switch(r1->prog->as) { + switch(r1->f.prog->as) { case ACALL: - if(noreturn(r1->prog)) + if(noreturn(r1->f.prog)) break; for(z=0; z<BITS; z++) { cal.b[z] |= ref.b[z] | externs.b[z]; @@ -1139,159 +748,22 @@ prop(Reg *r, Bits ref, Bits cal) r1->refbehind.b[z] = ref.b[z]; r1->calbehind.b[z] = cal.b[z]; } - if(r1->active) + if(r1->f.active) break; - r1->active = 1; + r1->f.active = 1; } - for(; r != r1; r = r->p1) - for(r2 = r->p2; r2 != R; r2 = r2->p2link) + for(; r != r1; r = (Reg*)r->f.p1) + for(r2 = (Reg*)r->f.p2; r2 != R; r2 = (Reg*)r2->f.p2link) prop(r2, r->refbehind, r->calbehind); } -/* - * find looping structure - * - * 1) find reverse postordering - * 2) find approximate dominators, - * the actual dominators if the flow graph is reducible - * otherwise, dominators plus some other non-dominators. - * See Matthew S. Hecht and Jeffrey D. Ullman, - * "Analysis of a Simple Algorithm for Global Data Flow Problems", - * Conf. Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts, - * Oct. 1-3, 1973, pp. 207-217. - * 3) find all nodes with a predecessor dominated by the current node. - * such a node is a loop head. - * recursively, all preds with a greater rpo number are in the loop - */ -int32 -postorder(Reg *r, Reg **rpo2r, int32 n) -{ - Reg *r1; - - r->rpo = 1; - r1 = r->s1; - if(r1 && !r1->rpo) - n = postorder(r1, rpo2r, n); - r1 = r->s2; - if(r1 && !r1->rpo) - n = postorder(r1, rpo2r, n); - rpo2r[n] = r; - n++; - return n; -} - -int32 -rpolca(int32 *idom, int32 rpo1, int32 rpo2) -{ - int32 t; - - if(rpo1 == -1) - return rpo2; - while(rpo1 != rpo2){ - if(rpo1 > rpo2){ - t = rpo2; - rpo2 = rpo1; - rpo1 = t; - } - while(rpo1 < rpo2){ - t = idom[rpo2]; - if(t >= rpo2) - fatal("bad idom"); - rpo2 = t; - } - } - return rpo1; -} - -int -doms(int32 *idom, int32 r, int32 s) -{ - while(s > r) - s = idom[s]; - return s == r; -} - -int -loophead(int32 *idom, Reg *r) -{ - int32 src; - - src = r->rpo; - if(r->p1 != R && doms(idom, src, r->p1->rpo)) - return 1; - for(r = r->p2; r != R; r = r->p2link) - if(doms(idom, src, r->rpo)) - return 1; - return 0; -} - -void -loopmark(Reg **rpo2r, int32 head, Reg *r) -{ - if(r->rpo < head || r->active == head) - return; - r->active = head; - r->loop += LOOP; - if(r->p1 != R) - loopmark(rpo2r, head, r->p1); - for(r = r->p2; r != R; r = r->p2link) - loopmark(rpo2r, head, r); -} - -void -loopit(Reg *r, int32 nr) -{ - Reg *r1; - int32 i, d, me; - - if(nr > maxnr) { - rpo2r = mal(nr * sizeof(Reg*)); - idom = mal(nr * sizeof(int32)); - maxnr = nr; - } - - d = postorder(r, rpo2r, 0); - if(d > nr) - fatal("too many reg nodes %d %d", d, nr); - nr = d; - for(i = 0; i < nr / 2; i++) { - r1 = rpo2r[i]; - rpo2r[i] = rpo2r[nr - 1 - i]; - rpo2r[nr - 1 - i] = r1; - } - for(i = 0; i < nr; i++) - rpo2r[i]->rpo = i; - - idom[0] = 0; - for(i = 0; i < nr; i++) { - r1 = rpo2r[i]; - me = r1->rpo; - d = -1; - // rpo2r[r->rpo] == r protects against considering dead code, - // which has r->rpo == 0. - if(r1->p1 != R && rpo2r[r1->p1->rpo] == r1->p1 && r1->p1->rpo < me) - d = r1->p1->rpo; - for(r1 = r1->p2; r1 != nil; r1 = r1->p2link) - if(rpo2r[r1->rpo] == r1 && r1->rpo < me) - d = rpolca(idom, d, r1->rpo); - idom[i] = d; - } - - for(i = 0; i < nr; i++) { - r1 = rpo2r[i]; - r1->loop++; - if(r1->p2 != R && loophead(idom, r1)) - loopmark(rpo2r, i, r1); - } -} - void synch(Reg *r, Bits dif) { Reg *r1; int z; - for(r1 = r; r1 != R; r1 = r1->s1) { + for(r1 = r; r1 != R; r1 = (Reg*)r1->f.s1) { for(z=0; z<BITS; z++) { dif.b[z] = (dif.b[z] & ~(~r1->refbehind.b[z] & r1->refahead.b[z])) | @@ -1301,13 +773,13 @@ synch(Reg *r, Bits dif) change++; } } - if(r1->active) + if(r1->f.active) break; - r1->active = 1; + r1->f.active = 1; for(z=0; z<BITS; z++) dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]); - if(r1->s2 != R) - synch(r1->s2, dif); + if(r1->f.s2 != nil) + synch((Reg*)r1->f.s2, dif); } } @@ -1372,7 +844,7 @@ paint1(Reg *r, int bn) for(;;) { if(!(r->refbehind.b[z] & bb)) break; - r1 = r->p1; + r1 = (Reg*)r->f.p1; if(r1 == R) break; if(!(r1->refahead.b[z] & bb)) @@ -1383,35 +855,35 @@ paint1(Reg *r, int bn) } if(LOAD(r) & ~(r->set.b[z]&~(r->use1.b[z]|r->use2.b[z])) & bb) { - change -= CLOAD * r->loop; + change -= CLOAD * r->f.loop; } for(;;) { r->act.b[z] |= bb; if(r->use1.b[z] & bb) { - change += CREF * r->loop; + change += CREF * r->f.loop; } if((r->use2.b[z]|r->set.b[z]) & bb) { - change += CREF * r->loop; + change += CREF * r->f.loop; } if(STORE(r) & r->regdiff.b[z] & bb) { - change -= CLOAD * r->loop; + change -= CLOAD * r->f.loop; } if(r->refbehind.b[z] & bb) - for(r1 = r->p2; r1 != R; r1 = r1->p2link) + for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) if(r1->refahead.b[z] & bb) paint1(r1, bn); if(!(r->refahead.b[z] & bb)) break; - r1 = r->s2; + r1 = (Reg*)r->f.s2; if(r1 != R) if(r1->refbehind.b[z] & bb) paint1(r1, bn); - r = r->s1; + r = (Reg*)r->f.s1; if(r == R) break; if(r->act.b[z] & bb) @@ -1434,7 +906,7 @@ regset(Reg *r, uint32 bb) v.type = b & 0xFFFF? BtoR(b): BtoF(b); if(v.type == 0) fatal("zero v.type for %#ux", b); - c = copyu(r->prog, &v, A); + c = copyu(r->f.prog, &v, A); if(c == 3) set |= b; bb &= ~b; @@ -1453,7 +925,7 @@ reguse(Reg *r, uint32 bb) v = zprog.from; while(b = bb & ~(bb-1)) { v.type = b & 0xFFFF? BtoR(b): BtoF(b); - c = copyu(r->prog, &v, A); + c = copyu(r->f.prog, &v, A); if(c == 1 || c == 2 || c == 4) set |= b; bb &= ~b; @@ -1476,7 +948,7 @@ paint2(Reg *r, int bn) for(;;) { if(!(r->refbehind.b[z] & bb)) break; - r1 = r->p1; + r1 = (Reg*)r->f.p1; if(r1 == R) break; if(!(r1->refahead.b[z] & bb)) @@ -1491,17 +963,17 @@ paint2(Reg *r, int bn) vreg |= r->regu; if(r->refbehind.b[z] & bb) - for(r1 = r->p2; r1 != R; r1 = r1->p2link) + for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) if(r1->refahead.b[z] & bb) vreg |= paint2(r1, bn); if(!(r->refahead.b[z] & bb)) break; - r1 = r->s2; + r1 = (Reg*)r->f.s2; if(r1 != R) if(r1->refbehind.b[z] & bb) vreg |= paint2(r1, bn); - r = r->s1; + r = (Reg*)r->f.s1; if(r == R) break; if(!(r->act.b[z] & bb)) @@ -1511,7 +983,7 @@ paint2(Reg *r, int bn) } bb = vreg; - for(; r; r=r->s1) { + for(; r; r=(Reg*)r->f.s1) { x = r->regu & ~bb; if(x) { vreg |= reguse(r, x); @@ -1536,7 +1008,7 @@ paint3(Reg *r, int bn, int32 rb, int rn) for(;;) { if(!(r->refbehind.b[z] & bb)) break; - r1 = r->p1; + r1 = (Reg*)r->f.p1; if(r1 == R) break; if(!(r1->refahead.b[z] & bb)) @@ -1550,7 +1022,7 @@ paint3(Reg *r, int bn, int32 rb, int rn) addmove(r, bn, rn, 0); for(;;) { r->act.b[z] |= bb; - p = r->prog; + p = r->f.prog; if(r->use1.b[z] & bb) { if(debug['R'] && debug['v']) @@ -1572,17 +1044,17 @@ paint3(Reg *r, int bn, int32 rb, int rn) r->regu |= rb; if(r->refbehind.b[z] & bb) - for(r1 = r->p2; r1 != R; r1 = r1->p2link) + for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) if(r1->refahead.b[z] & bb) paint3(r1, bn, rb, rn); if(!(r->refahead.b[z] & bb)) break; - r1 = r->s2; + r1 = (Reg*)r->f.s2; if(r1 != R) if(r1->refbehind.b[z] & bb) paint3(r1, bn, rb, rn); - r = r->s1; + r = (Reg*)r->f.s1; if(r == R) break; if(r->act.b[z] & bb) @@ -1646,60 +1118,64 @@ BtoF(int32 b) } void -dumpone(Reg *r) +dumpone(Flow *f, int isreg) { int z; Bits bit; + Reg *r; - print("%d:%P", r->loop, r->prog); - for(z=0; z<BITS; z++) - bit.b[z] = - r->set.b[z] | - r->use1.b[z] | - r->use2.b[z] | - r->refbehind.b[z] | - r->refahead.b[z] | - r->calbehind.b[z] | - r->calahead.b[z] | - r->regdiff.b[z] | - r->act.b[z] | - 0; - if(bany(&bit)) { - print("\t"); - if(bany(&r->set)) - print(" s:%Q", r->set); - if(bany(&r->use1)) - print(" u1:%Q", r->use1); - if(bany(&r->use2)) - print(" u2:%Q", r->use2); - if(bany(&r->refbehind)) - print(" rb:%Q ", r->refbehind); - if(bany(&r->refahead)) - print(" ra:%Q ", r->refahead); - if(bany(&r->calbehind)) - print(" cb:%Q ", r->calbehind); - if(bany(&r->calahead)) - print(" ca:%Q ", r->calahead); - if(bany(&r->regdiff)) - print(" d:%Q ", r->regdiff); - if(bany(&r->act)) - print(" a:%Q ", r->act); + print("%d:%P", f->loop, f->prog); + if(isreg) { + r = (Reg*)f; + for(z=0; z<BITS; z++) + bit.b[z] = + r->set.b[z] | + r->use1.b[z] | + r->use2.b[z] | + r->refbehind.b[z] | + r->refahead.b[z] | + r->calbehind.b[z] | + r->calahead.b[z] | + r->regdiff.b[z] | + r->act.b[z] | + 0; + if(bany(&bit)) { + print("\t"); + if(bany(&r->set)) + print(" s:%Q", r->set); + if(bany(&r->use1)) + print(" u1:%Q", r->use1); + if(bany(&r->use2)) + print(" u2:%Q", r->use2); + if(bany(&r->refbehind)) + print(" rb:%Q ", r->refbehind); + if(bany(&r->refahead)) + print(" ra:%Q ", r->refahead); + if(bany(&r->calbehind)) + print(" cb:%Q ", r->calbehind); + if(bany(&r->calahead)) + print(" ca:%Q ", r->calahead); + if(bany(&r->regdiff)) + print(" d:%Q ", r->regdiff); + if(bany(&r->act)) + print(" a:%Q ", r->act); + } } print("\n"); } void -dumpit(char *str, Reg *r0) +dumpit(char *str, Flow *r0, int isreg) { - Reg *r, *r1; + Flow *r, *r1; print("\n%s\n", str); - for(r = r0; r != R; r = r->link) { - dumpone(r); + for(r = r0; r != nil; r = r->link) { + dumpone(r, isreg); r1 = r->p2; - if(r1 != R) { + if(r1 != nil) { print(" pred:"); - for(; r1 != R; r1 = r1->p2link) + for(; r1 != nil; r1 = r1->p2link) print(" %.4ud", r1->prog->loc); print("\n"); } @@ -1712,148 +1188,3 @@ dumpit(char *str, Reg *r0) // } } } - -static Sym* symlist[10]; - -int -noreturn(Prog *p) -{ - Sym *s; - int i; - - if(symlist[0] == S) { - symlist[0] = pkglookup("panicindex", runtimepkg); - symlist[1] = pkglookup("panicslice", runtimepkg); - symlist[2] = pkglookup("throwinit", runtimepkg); - symlist[3] = pkglookup("panic", runtimepkg); - symlist[4] = pkglookup("panicwrap", runtimepkg); - } - - s = p->to.sym; - if(s == S) - return 0; - for(i=0; symlist[i]!=S; i++) - if(s == symlist[i]) - return 1; - return 0; -} - -/* - * the code generator depends on being able to write out JMP - * instructions that it can jump to now but fill in later. - * the linker will resolve them nicely, but they make the code - * longer and more difficult to follow during debugging. - * remove them. - */ - -/* what instruction does a JMP to p eventually land on? */ -static Prog* -chasejmp(Prog *p, int *jmploop) -{ - int n; - - n = 0; - while(p != P && p->as == AJMP && p->to.type == D_BRANCH) { - if(++n > 10) { - *jmploop = 1; - break; - } - p = p->to.u.branch; - } - return p; -} - -/* - * reuse reg pointer for mark/sweep state. - * leave reg==nil at end because alive==nil. - */ -#define alive ((void*)0) -#define dead ((void*)1) - -/* mark all code reachable from firstp as alive */ -static void -mark(Prog *firstp) -{ - Prog *p; - - for(p=firstp; p; p=p->link) { - if(p->reg != dead) - break; - p->reg = alive; - if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch) - mark(p->to.u.branch); - if(p->as == AJMP || p->as == ARET || p->as == AUNDEF) - break; - } -} - -static void -fixjmp(Prog *firstp) -{ - int jmploop; - Prog *p, *last; - - if(debug['R'] && debug['v']) - print("\nfixjmp\n"); - - // pass 1: resolve jump to AJMP, mark all code as dead. - jmploop = 0; - for(p=firstp; p; p=p->link) { - if(debug['R'] && debug['v']) - print("%P\n", p); - if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch && p->to.u.branch->as == AJMP) { - p->to.u.branch = chasejmp(p->to.u.branch, &jmploop); - if(debug['R'] && debug['v']) - print("->%P\n", p); - } - p->reg = dead; - } - if(debug['R'] && debug['v']) - print("\n"); - - // pass 2: mark all reachable code alive - mark(firstp); - - // pass 3: delete dead code (mostly JMPs). - last = nil; - for(p=firstp; p; p=p->link) { - if(p->reg == dead) { - if(p->link == P && p->as == ARET && last && last->as != ARET) { - // This is the final ARET, and the code so far doesn't have one. - // Let it stay. - } else { - if(debug['R'] && debug['v']) - print("del %P\n", p); - continue; - } - } - if(last) - last->link = p; - last = p; - } - last->link = P; - - // pass 4: elide JMP to next instruction. - // only safe if there are no jumps to JMPs anymore. - if(!jmploop) { - last = nil; - for(p=firstp; p; p=p->link) { - if(p->as == AJMP && p->to.type == D_BRANCH && p->to.u.branch == p->link) { - if(debug['R'] && debug['v']) - print("del %P\n", p); - continue; - } - if(last) - last->link = p; - last = p; - } - last->link = P; - } - - if(debug['R'] && debug['v']) { - print("\n"); - for(p=firstp; p; p=p->link) - print("%P\n", p); - print("\n"); - } -} diff --git a/src/cmd/6l/6.out.h b/src/cmd/6l/6.out.h index 237a802cd..5fa73a65b 100644 --- a/src/cmd/6l/6.out.h +++ b/src/cmd/6l/6.out.h @@ -30,11 +30,7 @@ #define NSYM 50 #define NSNAME 8 -#define NOPROF (1<<0) -#define DUPOK (1<<1) -#define NOSPLIT (1<<2) -#define RODATA (1<<3) -#define NOPTR (1<<4) +#include "../ld/textflag.h" /* * amd64 @@ -759,11 +755,14 @@ enum as AAESKEYGENASSIST, APSHUFD, + APCLMULQDQ, AUSEFIELD, - ALOCALS, ATYPE, - + AFUNCDATA, + APCDATA, + ACHECKNIL, + ALAST }; diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c index 8807a6ed5..a09cc9727 100644 --- a/src/cmd/6l/asm.c +++ b/src/cmd/6l/asm.c @@ -43,6 +43,7 @@ char linuxdynld[] = "/lib64/ld-linux-x86-64.so.2"; char freebsddynld[] = "/libexec/ld-elf.so.1"; char openbsddynld[] = "/usr/libexec/ld.so"; char netbsddynld[] = "/libexec/ld.elf_so"; +char dragonflydynld[] = "/usr/libexec/ld-elf.so.2"; char zeroes[32]; @@ -99,12 +100,6 @@ int nelfsym = 1; static void addpltsym(Sym*); static void addgotsym(Sym*); -Sym * -lookuprel(void) -{ - return lookup(".rela", 0); -} - void adddynrela(Sym *rela, Sym *s, Reloc *r) { @@ -312,9 +307,12 @@ elfreloc1(Reloc *r, vlong sectoff) break; case D_TLS: - if(r->siz == 4) - VPUT(R_X86_64_TPOFF32 | (uint64)elfsym<<32); - else + if(r->siz == 4) { + if(flag_shared) + VPUT(R_X86_64_GOTTPOFF | (uint64)elfsym<<32); + else + VPUT(R_X86_64_TPOFF32 | (uint64)elfsym<<32); + } else return -1; break; } @@ -625,13 +623,20 @@ asmb(void) sect = segtext.sect; cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); codeblk(sect->vaddr, sect->len); - - /* output read-only data in text segment (rodata, gosymtab, pclntab, ...) */ for(sect = sect->next; sect != nil; sect = sect->next) { cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); datblk(sect->vaddr, sect->len); } + if(segrodata.filelen > 0) { + if(debug['v']) + Bprint(&bso, "%5.2f rodatblk\n", cputime()); + Bflush(&bso); + + cseek(segrodata.fileoff); + datblk(segrodata.vaddr, segrodata.filelen); + } + if(debug['v']) Bprint(&bso, "%5.2f datblk\n", cputime()); Bflush(&bso); @@ -668,6 +673,7 @@ asmb(void) case Hfreebsd: case Hnetbsd: case Hopenbsd: + case Hdragonfly: debug['8'] = 1; /* 64-bit addresses */ break; case Hwindows: @@ -696,7 +702,8 @@ asmb(void) case Hfreebsd: case Hnetbsd: case Hopenbsd: - symo = rnd(HEADR+segtext.len, INITRND)+segdata.filelen; + case Hdragonfly: + symo = rnd(HEADR+segtext.len, INITRND)+rnd(segrodata.len, INITRND)+segdata.filelen; symo = rnd(symo, INITRND); break; case Hwindows: @@ -786,6 +793,7 @@ asmb(void) case Hfreebsd: case Hnetbsd: case Hopenbsd: + case Hdragonfly: asmbelf(symo); break; case Hwindows: diff --git a/src/cmd/6l/l.h b/src/cmd/6l/l.h index 4d481c69d..ecab867e4 100644 --- a/src/cmd/6l/l.h +++ b/src/cmd/6l/l.h @@ -159,7 +159,6 @@ struct Sym int32 got; int32 align; // if non-zero, required alignment in bytes int32 elfsym; - int32 locals; // size of stack frame locals area int32 args; // size of stack frame incoming arguments area Sym* hash; // in hash table Sym* allsym; // in all symbol list @@ -175,6 +174,7 @@ struct Sym char* dynimplib; char* dynimpvers; struct Section* sect; + struct Hist* hist; // for ATEXT // STEXT Auto* autom; @@ -187,14 +187,13 @@ struct Sym Reloc* r; int32 nr; int32 maxr; - int rel_ro; }; struct Optab { short as; uchar* ytab; uchar prefix; - uchar op[22]; + uchar op[23]; }; struct Movtab { @@ -211,7 +210,7 @@ enum STRINGSZ = 200, MINLC = 1, MAXIO = 8192, - MAXHIST = 20, /* limit of path elements for history symbols */ + MAXHIST = 40, /* limit of path elements for history symbols */ Yxxx = 0, Ynone, @@ -329,7 +328,6 @@ EXTERN int32 INITRND; EXTERN int64 INITTEXT; EXTERN int64 INITDAT; EXTERN char* INITENTRY; /* entry point */ -EXTERN char* LIBINITENTRY; /* shared library entry point */ EXTERN char* pcstr; EXTERN Auto* curauto; EXTERN Auto* curhist; @@ -357,7 +355,6 @@ EXTERN int32 spsize; EXTERN Sym* symlist; EXTERN int32 symsize; EXTERN int tlsoffset; -EXTERN int version; EXTERN Prog zprg; EXTERN int dtype; EXTERN char* paramspace; diff --git a/src/cmd/6l/list.c b/src/cmd/6l/list.c index f39efa2e8..5040e4327 100644 --- a/src/cmd/6l/list.c +++ b/src/cmd/6l/list.c @@ -57,10 +57,13 @@ Pconv(Fmt *fp) switch(p->as) { case ATEXT: if(p->from.scale) { - fmtprint(fp, "(%d) %A %D,%d,%D", + fmtprint(fp, "(%d) %A %D,%d,%lD", p->line, p->as, &p->from, p->from.scale, &p->to); break; } + fmtprint(fp, "(%d) %A %D,%lD", + p->line, p->as, &p->from, &p->to); + break; default: fmtprint(fp, "(%d) %A %D,%D", p->line, p->as, &p->from, &p->to); @@ -423,7 +426,7 @@ Iconv(Fmt *fp) void diag(char *fmt, ...) { - char buf[STRINGSZ], *tn, *sep; + char buf[1024], *tn, *sep; va_list arg; tn = ""; diff --git a/src/cmd/6l/obj.c b/src/cmd/6l/obj.c index e98f91eeb..ae649a74b 100644 --- a/src/cmd/6l/obj.c +++ b/src/cmd/6l/obj.c @@ -48,6 +48,7 @@ Header headers[] = { "plan9", Hplan9x64, "elf", Helf, "darwin", Hdarwin, + "dragonfly", Hdragonfly, "linux", Hlinux, "freebsd", Hfreebsd, "netbsd", Hnetbsd, @@ -62,6 +63,7 @@ Header headers[] = { * -Hplan9 -T0x200028 -R0x200000 is plan9 64-bit format * -Helf -T0x80110000 -R4096 is ELF32 * -Hdarwin -Tx -Rx is apple MH-exec + * -Hdragonfly -Tx -Rx is DragonFly elf-exec * -Hlinux -Tx -Rx is linux elf-exec * -Hfreebsd -Tx -Rx is FreeBSD elf-exec * -Hnetbsd -Tx -Rx is NetBSD elf-exec @@ -82,7 +84,6 @@ main(int argc, char *argv[]) INITDAT = -1; INITRND = -1; INITENTRY = 0; - LIBINITENTRY = 0; linkmode = LinkAuto; nuxiinit(); @@ -111,6 +112,7 @@ main(int argc, char *argv[]) flagstr("extldflags", "flags for external linker", &extldflags); flagcount("f", "ignore version mismatch", &debug['f']); flagcount("g", "disable go package data checks", &debug['g']); + flagstr("installsuffix", "pkg directory suffix", &flag_installsuffix); flagfn1("linkmode", "mode: set link mode (internal, external, auto)", setlinkmode); flagstr("k", "sym: set field tracking symbol", &tracksym); flagcount("n", "dump symbol table", &debug['n']); @@ -119,7 +121,7 @@ main(int argc, char *argv[]) flagstr("r", "dir1:dir2:...: set ELF dynamic linker search path", &rpath); flagcount("race", "enable race detector", &flag_race); flagcount("s", "disable symbol table", &debug['s']); - flagcount("shared", "generate shared object", &flag_shared); + flagcount("shared", "generate shared object (implies -linkmode external)", &flag_shared); flagstr("tmpdir", "leave temporary files in this directory", &tmpdir); flagcount("u", "reject unsafe packages", &debug['u']); flagcount("v", "print link trace", &debug['v']); @@ -140,6 +142,9 @@ main(int argc, char *argv[]) if(linkmode == LinkAuto && strcmp(getgoextlinkenabled(), "0") == 0) linkmode = LinkInternal; + if(flag_shared) + linkmode = LinkExternal; + switch(HEADTYPE) { default: if(linkmode == LinkAuto) @@ -148,6 +153,7 @@ main(int argc, char *argv[]) sysfatal("cannot use -linkmode=external with -H %s", headstr(HEADTYPE)); break; case Hdarwin: + case Hdragonfly: case Hfreebsd: case Hlinux: case Hnetbsd: @@ -168,7 +174,7 @@ main(int argc, char *argv[]) default: diag("unknown -H option"); errorexit(); - case Hplan9x32: /* plan 9 */ + case Hplan9x32: /* plan 9 */ HEADR = 32L; if(INITTEXT == -1) INITTEXT = 4096+HEADR; @@ -177,7 +183,7 @@ main(int argc, char *argv[]) if(INITRND == -1) INITRND = 4096; break; - case Hplan9x64: /* plan 9 */ + case Hplan9x64: /* plan 9 */ HEADR = 32L + 8L; if(INITTEXT == -1) INITTEXT = 0x200000+HEADR; @@ -186,7 +192,7 @@ main(int argc, char *argv[]) if(INITRND == -1) INITRND = 0x200000; break; - case Helf: /* elf32 executable */ + case Helf: /* elf32 executable */ HEADR = rnd(52L+3*32L, 16); if(INITTEXT == -1) INITTEXT = 0x80110000L; @@ -195,7 +201,7 @@ main(int argc, char *argv[]) if(INITRND == -1) INITRND = 4096; break; - case Hdarwin: /* apple MACH */ + case Hdarwin: /* apple MACH */ /* * OS X system constant - offset from 0(GS) to our TLS. * Explained in ../../pkg/runtime/cgo/gcc_darwin_amd64.c. @@ -210,10 +216,11 @@ main(int argc, char *argv[]) if(INITDAT == -1) INITDAT = 0; break; - case Hlinux: /* elf64 executable */ - case Hfreebsd: /* freebsd */ - case Hnetbsd: /* netbsd */ - case Hopenbsd: /* openbsd */ + case Hlinux: /* elf64 executable */ + case Hfreebsd: /* freebsd */ + case Hnetbsd: /* netbsd */ + case Hopenbsd: /* openbsd */ + case Hdragonfly: /* dragonfly */ /* * ELF uses TLS offset negative from FS. * Translate 0(FS) and 8(FS) into -16(FS) and -8(FS). @@ -230,7 +237,7 @@ main(int argc, char *argv[]) if(INITRND == -1) INITRND = 4096; break; - case Hwindows: /* PE executable */ + case Hwindows: /* PE executable */ peinit(); HEADR = PEFILEHEADR; if(INITTEXT == -1) @@ -337,10 +344,10 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[]) } a->offset = 0; if(t & T_OFFSET) { - a->offset = Bget4(f); + a->offset = BGETLE4(f); if(t & T_64) { a->offset &= 0xFFFFFFFFULL; - a->offset |= (vlong)Bget4(f) << 32; + a->offset |= (uvlong)BGETLE4(f) << 32; } } a->sym = S; @@ -348,8 +355,8 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[]) a->sym = zsym(pn, f, h); a->type = D_NONE; if(t & T_FCONST) { - a->ieee.l = Bget4(f); - a->ieee.h = Bget4(f); + a->ieee.l = BGETLE4(f); + a->ieee.h = BGETLE4(f); a->type = D_FCONST; } else if(t & T_SCONST) { @@ -365,7 +372,7 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[]) adrgotype = zsym(pn, f, h); s = a->sym; t = a->type; - if(t == D_INDIR+D_GS) + if(t == D_INDIR+D_GS || a->index == D_GS) a->offset += tlsoffset; if(t != D_AUTO && t != D_PARAM) { if(s && adrgotype) @@ -456,7 +463,7 @@ loop: if(o == ANAME || o == ASIGNAME) { sig = 0; if(o == ASIGNAME) - sig = Bget4(f); + sig = BGETLE4(f); v = BGETC(f); /* type */ o = BGETC(f); /* sym */ r = 0; @@ -511,7 +518,7 @@ loop: p = mal(sizeof(*p)); p->as = o; - p->line = Bget4(f); + p->line = BGETLE4(f); p->back = 2; p->mode = mode; zaddr(pn, f, &p->from, h); @@ -542,6 +549,7 @@ loop: addhist(p->line, D_FILE); /* 'z' */ if(p->to.offset) addhist(p->to.offset, D_FILE1); /* 'Z' */ + savehist(p->line, p->to.offset); histfrogp = 0; goto loop; @@ -603,13 +611,6 @@ loop: pc++; goto loop; - case ALOCALS: - if(skip) - goto casdef; - cursym->locals = p->to.offset; - pc++; - goto loop; - case ATYPE: if(skip) goto casdef; @@ -658,6 +659,7 @@ loop: s->gotype = fromgotype; } s->type = STEXT; + s->hist = gethist(); s->value = pc; s->args = p->to.offset >> 32; lastp = p; diff --git a/src/cmd/6l/optab.c b/src/cmd/6l/optab.c index b0d5ca788..46603ad45 100644 --- a/src/cmd/6l/optab.c +++ b/src/cmd/6l/optab.c @@ -42,11 +42,25 @@ uchar ytext[] = }; uchar ynop[] = { - Ynone, Ynone, Zpseudo,1, - Ynone, Yml, Zpseudo,1, - Ynone, Yrf, Zpseudo,1, - Yml, Ynone, Zpseudo,1, - Yrf, Ynone, Zpseudo,1, + Ynone, Ynone, Zpseudo,0, + Ynone, Yiauto, Zpseudo,0, + Ynone, Yml, Zpseudo,0, + Ynone, Yrf, Zpseudo,0, + Ynone, Yxr, Zpseudo,0, + Yiauto, Ynone, Zpseudo,0, + Yml, Ynone, Zpseudo,0, + Yrf, Ynone, Zpseudo,0, + Yxr, Ynone, Zpseudo,1, + 0 +}; +uchar yfuncdata[] = +{ + Yi32, Ym, Zpseudo, 0, + 0 +}; +uchar ypcdata[] = +{ + Yi32, Yi32, Zpseudo, 0, 0 }; uchar yxorb[] = @@ -243,8 +257,10 @@ uchar yrb_mb[] = Yrb, Ymb, Zr_m, 1, 0 }; -uchar yml_ml[] = +uchar yxchg[] = { + Yax, Yrl, Z_rp, 1, + Yrl, Yax, Zrp_, 1, Yrl, Yml, Zr_m, 1, Yml, Yrl, Zm_r, 1, 0 @@ -897,7 +913,7 @@ Optab optab[] = { AMOVHLPS, yxr, Pm, 0x12 }, { AMOVHPD, yxmov, Pe, 0x16,0x17 }, { AMOVHPS, yxmov, Pm, 0x16,0x17 }, - { AMOVL, ymovl, Px, 0x89,0x8b,0x31,0xb8,0xc7,(00),0x6e,0x7e,Pe,0x6e,Pe,0x7e }, + { AMOVL, ymovl, Px, 0x89,0x8b,0x31,0xb8,0xc7,(00),0x6e,0x7e,Pe,0x6e,Pe,0x7e,0 }, { AMOVLHPS, yxr, Pm, 0x16 }, { AMOVLPD, yxmov, Pe, 0x12,0x13 }, { AMOVLPS, yxmov, Pm, 0x12,0x13 }, @@ -909,7 +925,7 @@ Optab optab[] = { AMOVNTPD, yxr_ml, Pe, 0x2b }, { AMOVNTPS, yxr_ml, Pm, 0x2b }, { AMOVNTQ, ymr_ml, Pm, 0xe7 }, - { AMOVQ, ymovq, Pw, 0x89, 0x8b, 0x31, 0xc7,(00), 0xb8, 0xc7,(00), 0x6f, 0x7f, 0x6e, 0x7e, Pf2,0xd6, Pf3,0x7e, Pe,0xd6, Pe,0x6e, Pe,0x7e }, + { AMOVQ, ymovq, Pw, 0x89, 0x8b, 0x31, 0xc7,(00), 0xb8, 0xc7,(00), 0x6f, 0x7f, 0x6e, 0x7e, Pf2,0xd6, Pf3,0x7e, Pe,0xd6, Pe,0x6e, Pe,0x7e,0 }, { AMOVQOZX, ymrxr, Pf3, 0xd6,0x7e }, { AMOVSB, ynone, Pb, 0xa4 }, { AMOVSD, yxmov, Pf2, 0x10,0x11 }, @@ -919,7 +935,7 @@ Optab optab[] = { AMOVSW, ynone, Pe, 0xa5 }, { AMOVUPD, yxmov, Pe, 0x10,0x11 }, { AMOVUPS, yxmov, Pm, 0x10,0x11 }, - { AMOVW, ymovw, Pe, 0x89,0x8b,0x31,0xb8,0xc7,(00) }, + { AMOVW, ymovw, Pe, 0x89,0x8b,0x31,0xb8,0xc7,(00),0 }, { AMOVWLSX, yml_rl, Pm, 0xbf }, { AMOVWLZX, yml_rl, Pm, 0xb7 }, { AMOVWQSX, yml_rl, Pw, 0x0f,0xbf }, @@ -1170,9 +1186,9 @@ Optab optab[] = { AWAIT, ynone, Px, 0x9b }, { AWORD, ybyte, Px, 2 }, { AXCHGB, yml_mb, Pb, 0x86,0x86 }, - { AXCHGL, yml_ml, Px, 0x87,0x87 }, - { AXCHGQ, yml_ml, Pw, 0x87,0x87 }, - { AXCHGW, yml_ml, Pe, 0x87,0x87 }, + { AXCHGL, yxchg, Px, 0x90,0x90,0x87,0x87 }, + { AXCHGQ, yxchg, Pw, 0x90,0x90,0x87,0x87 }, + { AXCHGW, yxchg, Pe, 0x90,0x90,0x87,0x87 }, { AXLAT, ynone, Px, 0xd7 }, { AXORB, yxorb, Pb, 0x34,0x80,(06),0x30,0x32 }, { AXORL, yxorl, Px, 0x83,(06),0x35,0x81,(06),0x31,0x33 }, @@ -1333,10 +1349,11 @@ Optab optab[] = { AAESKEYGENASSIST, yaes2, Pq, 0x3a,0xdf,(0) }, { APSHUFD, yaes2, Pq, 0x70,(0) }, + { APCLMULQDQ, yxshuf, Pq, 0x3a,0x44,0 }, { AUSEFIELD, ynop, Px, 0,0 }, - { ALOCALS }, - { ATYPE }, + { AFUNCDATA, yfuncdata, Px, 0,0 }, + { APCDATA, ypcdata, Px, 0,0 }, { AEND }, 0 diff --git a/src/cmd/6l/pass.c b/src/cmd/6l/pass.c index 0054b329f..1be3c18fe 100644 --- a/src/cmd/6l/pass.c +++ b/src/cmd/6l/pass.c @@ -273,6 +273,7 @@ patch(void) Prog *p, *q; Sym *s; int32 vexit; + Sym *gmsym; if(debug['v']) Bprint(&bso, "%5.2f mkfwd\n", cputime()); @@ -282,6 +283,17 @@ patch(void) Bprint(&bso, "%5.2f patch\n", cputime()); Bflush(&bso); + if(flag_shared) { + s = lookup("init_array", 0); + s->type = SINITARR; + s->reachable = 1; + s->hide = 1; + addaddr(s, lookup(INITENTRY, 0)); + } + + gmsym = lookup("runtime.tlsgm", 0); + if(linkmode != LinkExternal) + gmsym->reachable = 0; s = lookup("exit", 0); vexit = s->value; for(cursym = textp; cursym != nil; cursym = cursym->next) @@ -311,14 +323,67 @@ patch(void) } if(HEADTYPE == Hlinux || HEADTYPE == Hfreebsd || HEADTYPE == Hopenbsd || HEADTYPE == Hnetbsd - || HEADTYPE == Hplan9x64) { + || HEADTYPE == Hplan9x64 || HEADTYPE == Hdragonfly) { // ELF uses FS instead of GS. if(p->from.type == D_INDIR+D_GS) p->from.type = D_INDIR+D_FS; if(p->to.type == D_INDIR+D_GS) p->to.type = D_INDIR+D_FS; + if(p->from.index == D_GS) + p->from.index = D_FS; + if(p->to.index == D_GS) + p->to.index = D_FS; + } + if(!flag_shared) { + // Convert g() or m() accesses of the form + // op n(reg)(GS*1), reg + // to + // op n(GS*1), reg + if(p->from.index == D_FS || p->from.index == D_GS) { + p->from.type = D_INDIR + p->from.index; + p->from.index = D_NONE; + } + // Convert g() or m() accesses of the form + // op reg, n(reg)(GS*1) + // to + // op reg, n(GS*1) + if(p->to.index == D_FS || p->to.index == D_GS) { + p->to.type = D_INDIR + p->to.index; + p->to.index = D_NONE; + } + // Convert get_tls access of the form + // op runtime.tlsgm(SB), reg + // to + // NOP + if(gmsym != S && p->from.sym == gmsym) { + p->as = ANOP; + p->from.type = D_NONE; + p->to.type = D_NONE; + p->from.sym = nil; + p->to.sym = nil; + continue; + } + } else { + // Convert TLS reads of the form + // op n(GS), reg + // to + // MOVQ $runtime.tlsgm(SB), reg + // op n(reg)(GS*1), reg + if((p->from.type == D_INDIR+D_FS || p->from.type == D_INDIR + D_GS) && p->to.type >= D_AX && p->to.type <= D_DI) { + q = appendp(p); + q->to = p->to; + q->as = p->as; + q->from.type = D_INDIR+p->to.type; + q->from.index = p->from.type - D_INDIR; + q->from.scale = 1; + q->from.offset = p->from.offset; + p->as = AMOVQ; + p->from.type = D_EXTERN; + p->from.sym = gmsym; + p->from.offset = 0; + } } - if(p->as == ACALL || (p->as == AJMP && p->to.type != D_BRANCH)) { + if(p->as == ACALL || (p->as == AJMP && p->to.type != D_BRANCH) || (p->as == ARET && p->to.sym != nil)) { s = p->to.sym; if(s) { if(debug['c']) @@ -403,6 +468,10 @@ morename[] = }; Prog* pmorestack[nelem(morename)]; Sym* symmorestack[nelem(morename)]; +Sym* gmsym; + +static Prog* load_g_cx(Prog*); +static Prog* stacksplit(Prog*, int32, Prog**); void dostkoff(void) @@ -410,8 +479,9 @@ dostkoff(void) Prog *p, *q, *q1; int32 autoffset, deltasp; int a, pcsize; - uint32 moreconst1, moreconst2, i; + uint32 i; + gmsym = lookup("runtime.tlsgm", 0); for(i=0; i<nelem(morename); i++) { symmorestack[i] = lookup(morename[i], 0); if(symmorestack[i]->type != STEXT) @@ -437,164 +507,16 @@ dostkoff(void) noleaf:; } - q = P; if((p->from.scale & NOSPLIT) && autoffset >= StackSmall) diag("nosplit func likely to overflow stack"); - if(!(p->from.scale & NOSPLIT)) { - p = appendp(p); // load g into CX - p->as = AMOVQ; - if(HEADTYPE == Hlinux || HEADTYPE == Hfreebsd - || HEADTYPE == Hopenbsd || HEADTYPE == Hnetbsd - || HEADTYPE == Hplan9x64) // ELF uses FS - p->from.type = D_INDIR+D_FS; - else - p->from.type = D_INDIR+D_GS; - p->from.offset = tlsoffset+0; - p->to.type = D_CX; - if(HEADTYPE == Hwindows) { - // movq %gs:0x28, %rcx - // movq (%rcx), %rcx - p->as = AMOVQ; - p->from.type = D_INDIR+D_GS; - p->from.offset = 0x28; - p->to.type = D_CX; - - - p = appendp(p); - p->as = AMOVQ; - p->from.type = D_INDIR+D_CX; - p->from.offset = 0; - p->to.type = D_CX; - } - - if(debug['K']) { - // 6l -K means check not only for stack - // overflow but stack underflow. - // On underflow, INT 3 (breakpoint). - // Underflow itself is rare but this also - // catches out-of-sync stack guard info - - p = appendp(p); - p->as = ACMPQ; - p->from.type = D_INDIR+D_CX; - p->from.offset = 8; - p->to.type = D_SP; - - p = appendp(p); - p->as = AJHI; - p->to.type = D_BRANCH; - p->to.offset = 4; - q1 = p; - - p = appendp(p); - p->as = AINT; - p->from.type = D_CONST; - p->from.offset = 3; - - p = appendp(p); - p->as = ANOP; - q1->pcond = p; - } - - if(autoffset < StackBig) { // do we need to call morestack? - if(autoffset <= StackSmall) { - // small stack - p = appendp(p); - p->as = ACMPQ; - p->from.type = D_SP; - p->to.type = D_INDIR+D_CX; - } else { - // large stack - p = appendp(p); - p->as = ALEAQ; - p->from.type = D_INDIR+D_SP; - p->from.offset = -(autoffset-StackSmall); - p->to.type = D_AX; - - p = appendp(p); - p->as = ACMPQ; - p->from.type = D_AX; - p->to.type = D_INDIR+D_CX; - } - - // common - p = appendp(p); - p->as = AJHI; - p->to.type = D_BRANCH; - p->to.offset = 4; - q = p; - } - - // If we ask for more stack, we'll get a minimum of StackMin bytes. - // We need a stack frame large enough to hold the top-of-stack data, - // the function arguments+results, our caller's PC, our frame, - // a word for the return PC of the next call, and then the StackLimit bytes - // that must be available on entry to any function called from a function - // that did a stack check. If StackMin is enough, don't ask for a specific - // amount: then we can use the custom functions and save a few - // instructions. - moreconst1 = 0; - if(StackTop + textarg + PtrSize + autoffset + PtrSize + StackLimit >= StackMin) - moreconst1 = autoffset; - moreconst2 = textarg; - - // 4 varieties varieties (const1==0 cross const2==0) - // and 6 subvarieties of (const1==0 and const2!=0) + q = P; + if(!(p->from.scale & NOSPLIT) || (p->from.scale & WRAPPER)) { p = appendp(p); - if(moreconst1 == 0 && moreconst2 == 0) { - p->as = ACALL; - p->to.type = D_BRANCH; - p->pcond = pmorestack[0]; - p->to.sym = symmorestack[0]; - } else - if(moreconst1 != 0 && moreconst2 == 0) { - p->as = AMOVL; - p->from.type = D_CONST; - p->from.offset = moreconst1; - p->to.type = D_AX; - - p = appendp(p); - p->as = ACALL; - p->to.type = D_BRANCH; - p->pcond = pmorestack[1]; - p->to.sym = symmorestack[1]; - } else - if(moreconst1 == 0 && moreconst2 <= 48 && moreconst2%8 == 0) { - i = moreconst2/8 + 3; - p->as = ACALL; - p->to.type = D_BRANCH; - p->pcond = pmorestack[i]; - p->to.sym = symmorestack[i]; - } else - if(moreconst1 == 0 && moreconst2 != 0) { - p->as = AMOVL; - p->from.type = D_CONST; - p->from.offset = moreconst2; - p->to.type = D_AX; - - p = appendp(p); - p->as = ACALL; - p->to.type = D_BRANCH; - p->pcond = pmorestack[2]; - p->to.sym = symmorestack[2]; - } else { - p->as = AMOVQ; - p->from.type = D_CONST; - p->from.offset = (uint64)moreconst2 << 32; - p->from.offset |= moreconst1; - p->to.type = D_AX; - - p = appendp(p); - p->as = ACALL; - p->to.type = D_BRANCH; - p->pcond = pmorestack[3]; - p->to.sym = symmorestack[3]; - } + p = load_g_cx(p); // load g into CX } - - if(q != P) - q->pcond = p->link; + if(!(cursym->text->from.scale & NOSPLIT)) + p = stacksplit(p, autoffset, &q); // emit split check if(autoffset) { p = appendp(p); @@ -602,8 +524,6 @@ dostkoff(void) p->from.type = D_CONST; p->from.offset = autoffset; p->spadj = autoffset; - if(q != P) - q->pcond = p; } else { // zero-byte stack adjustment. // Insert a fake non-zero adjustment so that stkcheck can @@ -615,7 +535,19 @@ dostkoff(void) p->as = ANOP; p->spadj = PtrSize; } + if(q != P) + q->pcond = p; deltasp = autoffset; + + if(cursym->text->from.scale & WRAPPER) { + // g->panicwrap += autoffset + PtrSize; + p = appendp(p); + p->as = AADDL; + p->from.type = D_CONST; + p->from.offset = autoffset + PtrSize; + p->to.type = D_INDIR+D_CX; + p->to.offset = 2*PtrSize; + } if(debug['K'] > 1 && autoffset) { // 6l -KK means double-check for stack overflow @@ -733,6 +665,19 @@ dostkoff(void) if(autoffset != deltasp) diag("unbalanced PUSH/POP"); + + if(cursym->text->from.scale & WRAPPER) { + p = load_g_cx(p); + p = appendp(p); + // g->panicwrap -= autoffset + PtrSize; + p->as = ASUBL; + p->from.type = D_CONST; + p->from.offset = autoffset + PtrSize; + p->to.type = D_INDIR+D_CX; + p->to.offset = 2*PtrSize; + p = appendp(p); + p->as = ARET; + } if(autoffset) { p->as = AADJSP; @@ -747,10 +692,264 @@ dostkoff(void) // the cleanup. p->spadj = +autoffset; } + if(p->to.sym) // retjmp + p->as = AJMP; } } } +// Append code to p to load g into cx. +// Overwrites p with the first instruction (no first appendp). +// Overwriting p is unusual but it lets use this in both the +// prologue (caller must call appendp first) and in the epilogue. +// Returns last new instruction. +static Prog* +load_g_cx(Prog *p) +{ + if(flag_shared) { + // Load TLS offset with MOVQ $runtime.tlsgm(SB), CX + p->as = AMOVQ; + p->from.type = D_EXTERN; + p->from.sym = gmsym; + p->to.type = D_CX; + p = appendp(p); + } + p->as = AMOVQ; + if(HEADTYPE == Hlinux || HEADTYPE == Hfreebsd + || HEADTYPE == Hopenbsd || HEADTYPE == Hnetbsd + || HEADTYPE == Hplan9x64 || HEADTYPE == Hdragonfly) + // ELF uses FS + p->from.type = D_INDIR+D_FS; + else + p->from.type = D_INDIR+D_GS; + if(flag_shared) { + // Add TLS offset stored in CX + p->from.index = p->from.type - D_INDIR; + p->from.type = D_INDIR + D_CX; + } + p->from.offset = tlsoffset+0; + p->to.type = D_CX; + if(HEADTYPE == Hwindows) { + // movq %gs:0x28, %rcx + // movq (%rcx), %rcx + p->as = AMOVQ; + p->from.type = D_INDIR+D_GS; + p->from.offset = 0x28; + p->to.type = D_CX; + + p = appendp(p); + p->as = AMOVQ; + p->from.type = D_INDIR+D_CX; + p->from.offset = 0; + p->to.type = D_CX; + } + return p; +} + +// Append code to p to check for stack split. +// Appends to (does not overwrite) p. +// Assumes g is in CX. +// Returns last new instruction. +// On return, *jmpok is the instruction that should jump +// to the stack frame allocation if no split is needed. +static Prog* +stacksplit(Prog *p, int32 framesize, Prog **jmpok) +{ + Prog *q, *q1; + uint32 moreconst1, moreconst2, i; + + if(debug['K']) { + // 6l -K means check not only for stack + // overflow but stack underflow. + // On underflow, INT 3 (breakpoint). + // Underflow itself is rare but this also + // catches out-of-sync stack guard info + + p = appendp(p); + p->as = ACMPQ; + p->from.type = D_INDIR+D_CX; + p->from.offset = 8; + p->to.type = D_SP; + + p = appendp(p); + p->as = AJHI; + p->to.type = D_BRANCH; + p->to.offset = 4; + q1 = p; + + p = appendp(p); + p->as = AINT; + p->from.type = D_CONST; + p->from.offset = 3; + + p = appendp(p); + p->as = ANOP; + q1->pcond = p; + } + + q = P; + q1 = P; + if(framesize <= StackSmall) { + // small stack: SP <= stackguard + // CMPQ SP, stackguard + p = appendp(p); + p->as = ACMPQ; + p->from.type = D_SP; + p->to.type = D_INDIR+D_CX; + } else if(framesize <= StackBig) { + // large stack: SP-framesize <= stackguard-StackSmall + // LEAQ -xxx(SP), AX + // CMPQ AX, stackguard + p = appendp(p); + p->as = ALEAQ; + p->from.type = D_INDIR+D_SP; + p->from.offset = -(framesize-StackSmall); + p->to.type = D_AX; + + p = appendp(p); + p->as = ACMPQ; + p->from.type = D_AX; + p->to.type = D_INDIR+D_CX; + } else { + // Such a large stack we need to protect against wraparound. + // If SP is close to zero: + // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall) + // The +StackGuard on both sides is required to keep the left side positive: + // SP is allowed to be slightly below stackguard. See stack.h. + // + // Preemption sets stackguard to StackPreempt, a very large value. + // That breaks the math above, so we have to check for that explicitly. + // MOVQ stackguard, CX + // CMPQ CX, $StackPreempt + // JEQ label-of-call-to-morestack + // LEAQ StackGuard(SP), AX + // SUBQ CX, AX + // CMPQ AX, $(framesize+(StackGuard-StackSmall)) + + p = appendp(p); + p->as = AMOVQ; + p->from.type = D_INDIR+D_CX; + p->from.offset = 0; + p->to.type = D_SI; + + p = appendp(p); + p->as = ACMPQ; + p->from.type = D_SI; + p->to.type = D_CONST; + p->to.offset = StackPreempt; + + p = appendp(p); + p->as = AJEQ; + p->to.type = D_BRANCH; + q1 = p; + + p = appendp(p); + p->as = ALEAQ; + p->from.type = D_INDIR+D_SP; + p->from.offset = StackGuard; + p->to.type = D_AX; + + p = appendp(p); + p->as = ASUBQ; + p->from.type = D_SI; + p->to.type = D_AX; + + p = appendp(p); + p->as = ACMPQ; + p->from.type = D_AX; + p->to.type = D_CONST; + p->to.offset = framesize+(StackGuard-StackSmall); + } + + // common + p = appendp(p); + p->as = AJHI; + p->to.type = D_BRANCH; + q = p; + + // If we ask for more stack, we'll get a minimum of StackMin bytes. + // We need a stack frame large enough to hold the top-of-stack data, + // the function arguments+results, our caller's PC, our frame, + // a word for the return PC of the next call, and then the StackLimit bytes + // that must be available on entry to any function called from a function + // that did a stack check. If StackMin is enough, don't ask for a specific + // amount: then we can use the custom functions and save a few + // instructions. + moreconst1 = 0; + if(StackTop + textarg + PtrSize + framesize + PtrSize + StackLimit >= StackMin) + moreconst1 = framesize; + moreconst2 = textarg; + if(moreconst2 == 1) // special marker + moreconst2 = 0; + if((moreconst2&7) != 0) + diag("misaligned argument size in stack split"); + // 4 varieties varieties (const1==0 cross const2==0) + // and 6 subvarieties of (const1==0 and const2!=0) + p = appendp(p); + if(moreconst1 == 0 && moreconst2 == 0) { + p->as = ACALL; + p->to.type = D_BRANCH; + p->pcond = pmorestack[0]; + p->to.sym = symmorestack[0]; + } else + if(moreconst1 != 0 && moreconst2 == 0) { + p->as = AMOVL; + p->from.type = D_CONST; + p->from.offset = moreconst1; + p->to.type = D_AX; + + p = appendp(p); + p->as = ACALL; + p->to.type = D_BRANCH; + p->pcond = pmorestack[1]; + p->to.sym = symmorestack[1]; + } else + if(moreconst1 == 0 && moreconst2 <= 48 && moreconst2%8 == 0) { + i = moreconst2/8 + 3; + p->as = ACALL; + p->to.type = D_BRANCH; + p->pcond = pmorestack[i]; + p->to.sym = symmorestack[i]; + } else + if(moreconst1 == 0 && moreconst2 != 0) { + p->as = AMOVL; + p->from.type = D_CONST; + p->from.offset = moreconst2; + p->to.type = D_AX; + + p = appendp(p); + p->as = ACALL; + p->to.type = D_BRANCH; + p->pcond = pmorestack[2]; + p->to.sym = symmorestack[2]; + } else { + p->as = AMOVQ; + p->from.type = D_CONST; + p->from.offset = (uint64)moreconst2 << 32; + p->from.offset |= moreconst1; + p->to.type = D_AX; + + p = appendp(p); + p->as = ACALL; + p->to.type = D_BRANCH; + p->pcond = pmorestack[3]; + p->to.sym = symmorestack[3]; + } + + p = appendp(p); + p->as = AJMP; + p->to.type = D_BRANCH; + p->pcond = cursym->text->link; + + if(q != P) + q->pcond = p->link; + if(q1 != P) + q1->pcond = q->link; + + *jmpok = q; + return p; +} + vlong atolwhex(char *s) { diff --git a/src/cmd/6l/span.c b/src/cmd/6l/span.c index 460a34f2f..74f11d635 100644 --- a/src/cmd/6l/span.c +++ b/src/cmd/6l/span.c @@ -358,6 +358,18 @@ prefixof(Adr *a) case D_INDIR+D_GS: return 0x65; } + switch(a->index) { + case D_CS: + return 0x2e; + case D_DS: + return 0x3e; + case D_ES: + return 0x26; + case D_FS: + return 0x64; + case D_GS: + return 0x65; + } return 0; } @@ -735,15 +747,20 @@ vaddr(Adr *a, Reloc *r) diag("need reloc for %D", a); errorexit(); } - if(flag_shared) - r->type = D_PCREL; - else - r->type = D_ADDR; r->siz = 4; // TODO: 8 for external symbols r->off = -1; // caller must fill in r->sym = s; r->add = v; v = 0; + if(flag_shared) { + if(s->type == STLSBSS) { + r->xadd = r->add - r->siz; + r->type = D_TLS; + r->xsym = s; + } else + r->type = D_PCREL; + } else + r->type = D_ADDR; } return v; } @@ -760,7 +777,7 @@ asmandsz(Adr *a, int r, int rex, int m64) v = a->offset; t = a->type; rel.siz = 0; - if(a->index != D_NONE) { + if(a->index != D_NONE && a->index != D_FS && a->index != D_GS) { if(t < D_INDIR) { switch(t) { default: @@ -888,18 +905,11 @@ putrelv: r = addrel(cursym); r->off = curp->pc + andptr - and; - r->add = 0; - r->xadd = 0; + r->add = a->offset-tlsoffset; + r->xadd = r->add; r->siz = 4; r->type = D_TLS; - if(a->offset == tlsoffset+0) - s = lookup("runtime.g", 0); - else - s = lookup("runtime.m", 0); - s->type = STLSBSS; - s->reachable = 1; - s->size = PtrSize; - s->hide = 1; + s = lookup("runtime.tlsgm", 0); r->sym = s; r->xsym = s; v = 0; @@ -1227,6 +1237,8 @@ found: break; } + if(z >= nelem(o->op)) + sysfatal("asmins bad table %P", p); op = o->op[z]; if(op == 0x0f) { *andptr++ = op; diff --git a/src/cmd/8a/a.y b/src/cmd/8a/a.y index 246643427..13ccc985b 100644 --- a/src/cmd/8a/a.y +++ b/src/cmd/8a/a.y @@ -33,6 +33,7 @@ #include <stdio.h> /* if we don't, bison will, and a.h re-#defines getc */ #include <libc.h> #include "a.h" +#include "../../pkg/runtime/funcdata.h" %} %union { Sym *sym; @@ -54,7 +55,7 @@ %left '*' '/' '%' %token <lval> LTYPE0 LTYPE1 LTYPE2 LTYPE3 LTYPE4 %token <lval> LTYPEC LTYPED LTYPEN LTYPER LTYPET LTYPES LTYPEM LTYPEI LTYPEG LTYPEXC -%token <lval> LTYPEX LCONST LFP LPC LSB +%token <lval> LTYPEX LTYPEPC LTYPEF LCONST LFP LPC LSB %token <lval> LBREG LLREG LSREG LFREG LXREG %token <dval> LFCONST %token <sval> LSCONST LSP @@ -63,7 +64,7 @@ %type <con2> con2 %type <gen> mem imm imm2 reg nam rel rem rim rom omem nmem %type <gen2> nonnon nonrel nonrem rimnon rimrem remrim -%type <gen2> spec1 spec2 spec3 spec4 spec5 spec6 spec7 spec8 spec9 spec10 +%type <gen2> spec1 spec2 spec3 spec4 spec5 spec6 spec7 spec8 spec9 spec10 spec11 spec12 %% prog: | prog @@ -118,6 +119,8 @@ inst: | LTYPEG spec8 { outcode($1, &$2); } | LTYPEXC spec9 { outcode($1, &$2); } | LTYPEX spec10 { outcode($1, &$2); } +| LTYPEPC spec11 { outcode($1, &$2); } +| LTYPEF spec12 { outcode($1, &$2); } nonnon: { @@ -307,6 +310,26 @@ spec10: /* PINSRD */ $$.to.offset = $1.offset; } +spec11: /* PCDATA */ + rim ',' rim + { + if($1.type != D_CONST || $3.type != D_CONST) + yyerror("arguments to PCDATA must be integer constants"); + $$.from = $1; + $$.to = $3; + } + +spec12: /* FUNCDATA */ + rim ',' rim + { + if($1.type != D_CONST) + yyerror("index for FUNCDATA must be integer constant"); + if($3.type != D_EXTERN && $3.type != D_STATIC) + yyerror("value for FUNCDATA must be symbol reference"); + $$.from = $1; + $$.to = $3; + } + rem: reg | mem @@ -448,12 +471,12 @@ con2: LCONST { $$.v1 = $1; - $$.v2 = 0; + $$.v2 = ArgsSizeUnknown; } | '-' LCONST { $$.v1 = -$2; - $$.v2 = 0; + $$.v2 = ArgsSizeUnknown; } | LCONST '-' LCONST { diff --git a/src/cmd/8a/doc.go b/src/cmd/8a/doc.go index 737c56f13..84c7254c8 100644 --- a/src/cmd/8a/doc.go +++ b/src/cmd/8a/doc.go @@ -10,6 +10,11 @@ http://plan9.bell-labs.com/magic/man2html/1/8a +Go-specific considerations are documented at + + http://golang.org/doc/asm + +I Its target architecture is the x86, referred to by these tools for historical reasons as 386. */ diff --git a/src/cmd/8a/lex.c b/src/cmd/8a/lex.c index a7840f625..f2ccc3361 100644 --- a/src/cmd/8a/lex.c +++ b/src/cmd/8a/lex.c @@ -74,7 +74,7 @@ main(int argc, char *argv[]) ARGBEGIN { default: c = ARGC(); - if(c >= 0 || c < sizeof(debug)) + if(c >= 0 && c < sizeof(debug)) debug[c] = 1; break; @@ -799,7 +799,8 @@ struct "XORPD", LTYPE3, AXORPD, "XORPS", LTYPE3, AXORPS, "USEFIELD", LTYPEN, AUSEFIELD, - + "PCDATA", LTYPEPC, APCDATA, + "FUNCDATA", LTYPEF, AFUNCDATA, 0 }; @@ -879,15 +880,14 @@ void zname(char *n, int t, int s) { - Bputc(&obuf, ANAME); /* as(2) */ - Bputc(&obuf, ANAME>>8); - Bputc(&obuf, t); /* type */ - Bputc(&obuf, s); /* sym */ + BPUTLE2(&obuf, ANAME); /* as(2) */ + BPUTC(&obuf, t); /* type */ + BPUTC(&obuf, s); /* sym */ while(*n) { - Bputc(&obuf, *n); + BPUTC(&obuf, *n); n++; } - Bputc(&obuf, 0); + BPUTC(&obuf, 0); } void @@ -922,52 +922,38 @@ zaddr(Gen *a, int s) case D_NONE: break; } - Bputc(&obuf, t); + BPUTC(&obuf, t); if(t & T_INDEX) { /* implies index, scale */ - Bputc(&obuf, a->index); - Bputc(&obuf, a->scale); + BPUTC(&obuf, a->index); + BPUTC(&obuf, a->scale); } if(t & T_OFFSET) { /* implies offset */ l = a->offset; - Bputc(&obuf, l); - Bputc(&obuf, l>>8); - Bputc(&obuf, l>>16); - Bputc(&obuf, l>>24); + BPUTLE4(&obuf, l); } if(t & T_OFFSET2) { l = a->offset2; - Bputc(&obuf, l); - Bputc(&obuf, l>>8); - Bputc(&obuf, l>>16); - Bputc(&obuf, l>>24); + BPUTLE4(&obuf, l); } if(t & T_SYM) /* implies sym */ - Bputc(&obuf, s); + BPUTC(&obuf, s); if(t & T_FCONST) { ieeedtod(&e, a->dval); - l = e.l; - Bputc(&obuf, l); - Bputc(&obuf, l>>8); - Bputc(&obuf, l>>16); - Bputc(&obuf, l>>24); - l = e.h; - Bputc(&obuf, l); - Bputc(&obuf, l>>8); - Bputc(&obuf, l>>16); - Bputc(&obuf, l>>24); + BPUTLE4(&obuf, e.l); + BPUTLE4(&obuf, e.h); return; } if(t & T_SCONST) { n = a->sval; for(i=0; i<NSNAME; i++) { - Bputc(&obuf, *n); + BPUTC(&obuf, *n); n++; } return; } if(t & T_TYPE) - Bputc(&obuf, a->type); + BPUTC(&obuf, a->type); } void @@ -1026,12 +1012,8 @@ jackpot: goto jackpot; break; } - Bputc(&obuf, a); - Bputc(&obuf, a>>8); - Bputc(&obuf, stmtline); - Bputc(&obuf, stmtline>>8); - Bputc(&obuf, stmtline>>16); - Bputc(&obuf, stmtline>>24); + BPUTLE2(&obuf, a); + BPUTLE4(&obuf, stmtline); zaddr(&g2->from, sf); zaddr(&g2->to, st); @@ -1106,13 +1088,12 @@ outhist(void) q = 0; } if(n) { - Bputc(&obuf, ANAME); - Bputc(&obuf, ANAME>>8); - Bputc(&obuf, D_FILE); /* type */ - Bputc(&obuf, 1); /* sym */ - Bputc(&obuf, '<'); + BPUTLE2(&obuf, ANAME); + BPUTC(&obuf, D_FILE); /* type */ + BPUTC(&obuf, 1); /* sym */ + BPUTC(&obuf, '<'); Bwrite(&obuf, p, n); - Bputc(&obuf, 0); + BPUTC(&obuf, 0); } p = q; if(p == 0 && op) { @@ -1122,12 +1103,8 @@ outhist(void) } g.offset = h->offset; - Bputc(&obuf, AHISTORY); - Bputc(&obuf, AHISTORY>>8); - Bputc(&obuf, h->line); - Bputc(&obuf, h->line>>8); - Bputc(&obuf, h->line>>16); - Bputc(&obuf, h->line>>24); + BPUTLE2(&obuf, AHISTORY); + BPUTLE4(&obuf, h->line); zaddr(&nullgen, 0); zaddr(&g, 0); diff --git a/src/cmd/8a/y.tab.c b/src/cmd/8a/y.tab.c index 6616c9139..aec4856f6 100644 --- a/src/cmd/8a/y.tab.c +++ b/src/cmd/8a/y.tab.c @@ -1,24 +1,21 @@ -/* A Bison parser, made by GNU Bison 2.3. */ +/* A Bison parser, made by GNU Bison 2.5. */ -/* Skeleton implementation for Bison's Yacc-like parsers in C - - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 - Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ + along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work @@ -29,7 +26,7 @@ special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. - + This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ @@ -47,7 +44,7 @@ #define YYBISON 1 /* Bison version. */ -#define YYBISON_VERSION "2.3" +#define YYBISON_VERSION "2.5" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -55,11 +52,51 @@ /* Pure parsers. */ #define YYPURE 0 +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + /* Using locations. */ #define YYLSP_NEEDED 0 +/* Copy the first part of user declarations. */ + +/* Line 268 of yacc.c */ +#line 31 "a.y" + +#include <u.h> +#include <stdio.h> /* if we don't, bison will, and a.h re-#defines getc */ +#include <libc.h> +#include "a.h" +#include "../../pkg/runtime/funcdata.h" + + +/* Line 268 of yacc.c */ +#line 80 "y.tab.c" + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + + /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE @@ -82,21 +119,23 @@ LTYPEG = 271, LTYPEXC = 272, LTYPEX = 273, - LCONST = 274, - LFP = 275, - LPC = 276, - LSB = 277, - LBREG = 278, - LLREG = 279, - LSREG = 280, - LFREG = 281, - LXREG = 282, - LFCONST = 283, - LSCONST = 284, - LSP = 285, - LNAME = 286, - LLAB = 287, - LVAR = 288 + LTYPEPC = 274, + LTYPEF = 275, + LCONST = 276, + LFP = 277, + LPC = 278, + LSB = 279, + LBREG = 280, + LLREG = 281, + LSREG = 282, + LFREG = 283, + LXREG = 284, + LFCONST = 285, + LSCONST = 286, + LSP = 287, + LNAME = 288, + LLAB = 289, + LVAR = 290 }; #endif /* Tokens. */ @@ -116,56 +155,34 @@ #define LTYPEG 271 #define LTYPEXC 272 #define LTYPEX 273 -#define LCONST 274 -#define LFP 275 -#define LPC 276 -#define LSB 277 -#define LBREG 278 -#define LLREG 279 -#define LSREG 280 -#define LFREG 281 -#define LXREG 282 -#define LFCONST 283 -#define LSCONST 284 -#define LSP 285 -#define LNAME 286 -#define LLAB 287 -#define LVAR 288 - - - - -/* Copy the first part of user declarations. */ -#line 31 "a.y" - -#include <u.h> -#include <stdio.h> /* if we don't, bison will, and a.h re-#defines getc */ -#include <libc.h> -#include "a.h" - +#define LTYPEPC 274 +#define LTYPEF 275 +#define LCONST 276 +#define LFP 277 +#define LPC 278 +#define LSB 279 +#define LBREG 280 +#define LLREG 281 +#define LSREG 282 +#define LFREG 283 +#define LXREG 284 +#define LFCONST 285 +#define LSCONST 286 +#define LSP 287 +#define LNAME 288 +#define LLAB 289 +#define LVAR 290 -/* Enabling traces. */ -#ifndef YYDEBUG -# define YYDEBUG 0 -#endif -/* Enabling verbose error messages. */ -#ifdef YYERROR_VERBOSE -# undef YYERROR_VERBOSE -# define YYERROR_VERBOSE 1 -#else -# define YYERROR_VERBOSE 0 -#endif -/* Enabling the token table. */ -#ifndef YYTOKEN_TABLE -# define YYTOKEN_TABLE 0 -#endif #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE -#line 37 "a.y" { + +/* Line 293 of yacc.c */ +#line 38 "a.y" + Sym *sym; int32 lval; struct { @@ -176,22 +193,23 @@ typedef union YYSTYPE char sval[8]; Gen gen; Gen2 gen2; -} -/* Line 193 of yacc.c. */ -#line 182 "y.tab.c" - YYSTYPE; + + + +/* Line 293 of yacc.c */ +#line 201 "y.tab.c" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 -# define YYSTYPE_IS_TRIVIAL 1 #endif - /* Copy the second part of user declarations. */ -/* Line 216 of yacc.c. */ -#line 195 "y.tab.c" +/* Line 343 of yacc.c */ +#line 213 "y.tab.c" #ifdef short # undef short @@ -266,14 +284,14 @@ typedef short int yytype_int16; #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static int -YYID (int i) +YYID (int yyi) #else static int -YYID (i) - int i; +YYID (yyi) + int yyi; #endif { - return i; + return yyi; } #endif @@ -294,11 +312,11 @@ YYID (i) # define alloca _alloca # else # define YYSTACK_ALLOC alloca -# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 # endif # endif # endif @@ -321,24 +339,24 @@ YYID (i) # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif -# if (defined __cplusplus && ! defined _STDLIB_H \ +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc -# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free -# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif @@ -354,9 +372,9 @@ void free (void *); /* INFRINGES ON USER NAME SPACE */ /* A type that is properly aligned for any stack member. */ union yyalloc { - yytype_int16 yyss; - YYSTYPE yyvs; - }; + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; +}; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) @@ -367,6 +385,27 @@ union yyalloc ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED /* Copy COUNT objects from FROM to TO. The source and destination do not overlap. */ # ifndef YYCOPY @@ -384,42 +423,25 @@ union yyalloc while (YYID (0)) # endif # endif - -/* Relocate STACK from its old location to the new one. The - local variables YYSIZE and YYSTACKSIZE give the old and new number of - elements in the stack, and YYPTR gives the new location of the - stack. Advance YYPTR to a properly aligned location for the next - stack. */ -# define YYSTACK_RELOCATE(Stack) \ - do \ - { \ - YYSIZE_T yynewbytes; \ - YYCOPY (&yyptr->Stack, Stack, yysize); \ - Stack = &yyptr->Stack; \ - yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ - yyptr += yynewbytes / sizeof (*yyptr); \ - } \ - while (YYID (0)) - -#endif +#endif /* !YYCOPY_NEEDED */ /* YYFINAL -- State number of the termination state. */ #define YYFINAL 2 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 537 +#define YYLAST 546 /* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 52 +#define YYNTOKENS 54 /* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 39 +#define YYNNTS 41 /* YYNRULES -- Number of rules. */ -#define YYNRULES 131 +#define YYNRULES 135 /* YYNRULES -- Number of states. */ -#define YYNSTATES 266 +#define YYNSTATES 276 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 -#define YYMAXUTOK 288 +#define YYMAXUTOK 290 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) @@ -430,16 +452,16 @@ static const yytype_uint8 yytranslate[] = 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 50, 12, 5, 2, - 48, 49, 10, 8, 47, 9, 2, 11, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 44, 45, - 6, 46, 7, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 52, 12, 5, 2, + 50, 51, 10, 8, 49, 9, 2, 11, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 46, 47, + 6, 48, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 3, 2, 51, 2, 2, 2, + 2, 2, 2, 2, 3, 2, 53, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -455,7 +477,8 @@ static const yytype_uint8 yytranslate[] = 2, 2, 2, 2, 2, 2, 1, 2, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, - 35, 36, 37, 38, 39, 40, 41, 42, 43 + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45 }; #if YYDEBUG @@ -466,87 +489,88 @@ static const yytype_uint16 yyprhs[] = 0, 0, 3, 4, 5, 9, 10, 15, 16, 21, 23, 26, 29, 33, 37, 40, 43, 46, 49, 52, 55, 58, 61, 64, 67, 70, 73, 76, 79, 82, - 85, 86, 88, 92, 96, 99, 101, 104, 106, 109, - 111, 115, 121, 125, 131, 134, 136, 139, 141, 143, - 147, 153, 157, 163, 166, 168, 172, 176, 182, 188, - 194, 196, 198, 200, 202, 205, 208, 210, 212, 214, - 216, 218, 223, 226, 229, 231, 233, 235, 237, 239, - 241, 244, 247, 250, 253, 258, 264, 268, 271, 273, - 276, 280, 285, 287, 289, 291, 296, 301, 308, 318, - 328, 332, 336, 341, 347, 356, 358, 365, 371, 379, - 380, 383, 386, 388, 390, 392, 394, 396, 399, 402, - 405, 409, 411, 415, 419, 423, 427, 431, 436, 441, - 445, 449 + 85, 88, 91, 92, 94, 98, 102, 105, 107, 110, + 112, 115, 117, 121, 127, 131, 137, 140, 142, 145, + 147, 149, 153, 159, 163, 169, 172, 174, 178, 182, + 188, 194, 200, 204, 208, 210, 212, 214, 216, 219, + 222, 224, 226, 228, 230, 232, 237, 240, 243, 245, + 247, 249, 251, 253, 255, 258, 261, 264, 267, 272, + 278, 282, 285, 287, 290, 294, 299, 301, 303, 305, + 310, 315, 322, 332, 342, 346, 350, 355, 361, 370, + 372, 379, 385, 393, 394, 397, 400, 402, 404, 406, + 408, 410, 413, 416, 419, 423, 425, 429, 433, 437, + 441, 445, 450, 455, 459, 463 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int8 yyrhs[] = { - 53, 0, -1, -1, -1, 53, 54, 55, -1, -1, - 42, 44, 56, 55, -1, -1, 41, 44, 57, 55, - -1, 45, -1, 58, 45, -1, 1, 45, -1, 41, - 46, 90, -1, 43, 46, 90, -1, 13, 59, -1, - 14, 63, -1, 15, 62, -1, 16, 60, -1, 17, - 61, -1, 21, 64, -1, 19, 65, -1, 22, 66, - -1, 18, 67, -1, 20, 68, -1, 23, 69, -1, - 24, 70, -1, 25, 71, -1, 26, 72, -1, 27, - 73, -1, 28, 74, -1, -1, 47, -1, 77, 47, - 75, -1, 75, 47, 77, -1, 77, 47, -1, 77, - -1, 47, 75, -1, 75, -1, 47, 78, -1, 78, - -1, 80, 47, 78, -1, 86, 11, 89, 47, 80, - -1, 83, 47, 81, -1, 83, 47, 89, 47, 81, - -1, 47, 76, -1, 76, -1, 10, 86, -1, 59, - -1, 63, -1, 77, 47, 75, -1, 77, 47, 75, - 44, 34, -1, 77, 47, 75, -1, 77, 47, 75, - 44, 35, -1, 77, 47, -1, 77, -1, 77, 47, - 75, -1, 83, 47, 80, -1, 83, 47, 89, 47, - 80, -1, 79, 47, 75, 47, 89, -1, 80, 47, - 75, 47, 79, -1, 79, -1, 83, -1, 78, -1, - 85, -1, 10, 79, -1, 10, 84, -1, 79, -1, - 84, -1, 80, -1, 75, -1, 80, -1, 89, 48, - 31, 49, -1, 41, 87, -1, 42, 87, -1, 33, - -1, 36, -1, 34, -1, 37, -1, 40, -1, 35, - -1, 50, 89, -1, 50, 86, -1, 50, 39, -1, - 50, 38, -1, 50, 48, 38, 49, -1, 50, 48, - 9, 38, 49, -1, 50, 9, 38, -1, 50, 82, - -1, 29, -1, 9, 29, -1, 29, 9, 29, -1, - 9, 29, 9, 29, -1, 84, -1, 85, -1, 89, - -1, 89, 48, 34, 49, -1, 89, 48, 40, 49, - -1, 89, 48, 34, 10, 89, 49, -1, 89, 48, - 34, 49, 48, 34, 10, 89, 49, -1, 89, 48, - 34, 49, 48, 35, 10, 89, 49, -1, 48, 34, - 49, -1, 48, 40, 49, -1, 89, 48, 35, 49, - -1, 48, 34, 10, 89, 49, -1, 48, 34, 49, - 48, 34, 10, 89, 49, -1, 86, -1, 86, 48, - 34, 10, 89, 49, -1, 41, 87, 48, 88, 49, - -1, 41, 6, 7, 87, 48, 32, 49, -1, -1, - 8, 89, -1, 9, 89, -1, 32, -1, 40, -1, - 30, -1, 29, -1, 43, -1, 9, 89, -1, 8, - 89, -1, 51, 89, -1, 48, 90, 49, -1, 89, - -1, 90, 8, 90, -1, 90, 9, 90, -1, 90, - 10, 90, -1, 90, 11, 90, -1, 90, 12, 90, - -1, 90, 6, 6, 90, -1, 90, 7, 7, 90, - -1, 90, 5, 90, -1, 90, 4, 90, -1, 90, - 3, 90, -1 + 55, 0, -1, -1, -1, 55, 56, 57, -1, -1, + 44, 46, 58, 57, -1, -1, 43, 46, 59, 57, + -1, 47, -1, 60, 47, -1, 1, 47, -1, 43, + 48, 94, -1, 45, 48, 94, -1, 13, 61, -1, + 14, 65, -1, 15, 64, -1, 16, 62, -1, 17, + 63, -1, 21, 66, -1, 19, 67, -1, 22, 68, + -1, 18, 69, -1, 20, 70, -1, 23, 71, -1, + 24, 72, -1, 25, 73, -1, 26, 74, -1, 27, + 75, -1, 28, 76, -1, 29, 77, -1, 30, 78, + -1, -1, 49, -1, 81, 49, 79, -1, 79, 49, + 81, -1, 81, 49, -1, 81, -1, 49, 79, -1, + 79, -1, 49, 82, -1, 82, -1, 84, 49, 82, + -1, 90, 11, 93, 49, 84, -1, 87, 49, 85, + -1, 87, 49, 93, 49, 85, -1, 49, 80, -1, + 80, -1, 10, 90, -1, 61, -1, 65, -1, 81, + 49, 79, -1, 81, 49, 79, 46, 36, -1, 81, + 49, 79, -1, 81, 49, 79, 46, 37, -1, 81, + 49, -1, 81, -1, 81, 49, 79, -1, 87, 49, + 84, -1, 87, 49, 93, 49, 84, -1, 83, 49, + 79, 49, 93, -1, 84, 49, 79, 49, 83, -1, + 81, 49, 81, -1, 81, 49, 81, -1, 83, -1, + 87, -1, 82, -1, 89, -1, 10, 83, -1, 10, + 88, -1, 83, -1, 88, -1, 84, -1, 79, -1, + 84, -1, 93, 50, 33, 51, -1, 43, 91, -1, + 44, 91, -1, 35, -1, 38, -1, 36, -1, 39, + -1, 42, -1, 37, -1, 52, 93, -1, 52, 90, + -1, 52, 41, -1, 52, 40, -1, 52, 50, 40, + 51, -1, 52, 50, 9, 40, 51, -1, 52, 9, + 40, -1, 52, 86, -1, 31, -1, 9, 31, -1, + 31, 9, 31, -1, 9, 31, 9, 31, -1, 88, + -1, 89, -1, 93, -1, 93, 50, 36, 51, -1, + 93, 50, 42, 51, -1, 93, 50, 36, 10, 93, + 51, -1, 93, 50, 36, 51, 50, 36, 10, 93, + 51, -1, 93, 50, 36, 51, 50, 37, 10, 93, + 51, -1, 50, 36, 51, -1, 50, 42, 51, -1, + 93, 50, 37, 51, -1, 50, 36, 10, 93, 51, + -1, 50, 36, 51, 50, 36, 10, 93, 51, -1, + 90, -1, 90, 50, 36, 10, 93, 51, -1, 43, + 91, 50, 92, 51, -1, 43, 6, 7, 91, 50, + 34, 51, -1, -1, 8, 93, -1, 9, 93, -1, + 34, -1, 42, -1, 32, -1, 31, -1, 45, -1, + 9, 93, -1, 8, 93, -1, 53, 93, -1, 50, + 94, 51, -1, 93, -1, 94, 8, 94, -1, 94, + 9, 94, -1, 94, 10, 94, -1, 94, 11, 94, + -1, 94, 12, 94, -1, 94, 6, 6, 94, -1, + 94, 7, 7, 94, -1, 94, 5, 94, -1, 94, + 4, 94, -1, 94, 3, 94, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { - 0, 68, 68, 70, 69, 77, 76, 84, 83, 89, - 90, 91, 94, 99, 105, 106, 107, 108, 109, 110, - 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, - 123, 127, 134, 141, 148, 153, 160, 165, 172, 177, - 182, 189, 197, 202, 210, 215, 220, 229, 230, 233, - 238, 248, 253, 263, 268, 273, 280, 285, 293, 301, - 311, 312, 315, 316, 317, 321, 325, 326, 327, 330, - 331, 334, 340, 349, 358, 363, 368, 373, 378, 383, - 390, 396, 407, 413, 419, 425, 431, 439, 448, 453, - 458, 463, 470, 471, 474, 480, 486, 492, 501, 510, - 519, 524, 529, 535, 543, 553, 557, 566, 573, 582, - 585, 589, 595, 596, 600, 603, 604, 608, 612, 616, - 620, 626, 627, 631, 635, 639, 643, 647, 651, 655, - 659, 663 + 0, 69, 69, 71, 70, 78, 77, 85, 84, 90, + 91, 92, 95, 100, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 126, 130, 137, 144, 151, 156, 163, 168, + 175, 180, 185, 192, 200, 205, 213, 218, 223, 232, + 233, 236, 241, 251, 256, 266, 271, 276, 283, 288, + 296, 304, 314, 323, 334, 335, 338, 339, 340, 344, + 348, 349, 350, 353, 354, 357, 363, 372, 381, 386, + 391, 396, 401, 406, 413, 419, 430, 436, 442, 448, + 454, 462, 471, 476, 481, 486, 493, 494, 497, 503, + 509, 515, 524, 533, 542, 547, 552, 558, 566, 576, + 580, 589, 596, 605, 608, 612, 618, 619, 623, 626, + 627, 631, 635, 639, 643, 649, 650, 654, 658, 662, + 666, 670, 674, 678, 682, 686 }; #endif @@ -558,14 +582,15 @@ static const char *const yytname[] = "$end", "error", "$undefined", "'|'", "'^'", "'&'", "'<'", "'>'", "'+'", "'-'", "'*'", "'/'", "'%'", "LTYPE0", "LTYPE1", "LTYPE2", "LTYPE3", "LTYPE4", "LTYPEC", "LTYPED", "LTYPEN", "LTYPER", "LTYPET", "LTYPES", - "LTYPEM", "LTYPEI", "LTYPEG", "LTYPEXC", "LTYPEX", "LCONST", "LFP", - "LPC", "LSB", "LBREG", "LLREG", "LSREG", "LFREG", "LXREG", "LFCONST", - "LSCONST", "LSP", "LNAME", "LLAB", "LVAR", "':'", "';'", "'='", "','", - "'('", "')'", "'$'", "'~'", "$accept", "prog", "@1", "line", "@2", "@3", - "inst", "nonnon", "rimrem", "remrim", "rimnon", "nonrem", "nonrel", - "spec1", "spec2", "spec3", "spec4", "spec5", "spec6", "spec7", "spec8", - "spec9", "spec10", "rem", "rom", "rim", "rel", "reg", "imm", "imm2", - "con2", "mem", "omem", "nmem", "nam", "offset", "pointer", "con", "expr", 0 + "LTYPEM", "LTYPEI", "LTYPEG", "LTYPEXC", "LTYPEX", "LTYPEPC", "LTYPEF", + "LCONST", "LFP", "LPC", "LSB", "LBREG", "LLREG", "LSREG", "LFREG", + "LXREG", "LFCONST", "LSCONST", "LSP", "LNAME", "LLAB", "LVAR", "':'", + "';'", "'='", "','", "'('", "')'", "'$'", "'~'", "$accept", "prog", + "$@1", "line", "$@2", "$@3", "inst", "nonnon", "rimrem", "remrim", + "rimnon", "nonrem", "nonrel", "spec1", "spec2", "spec3", "spec4", + "spec5", "spec6", "spec7", "spec8", "spec9", "spec10", "spec11", + "spec12", "rem", "rom", "rim", "rel", "reg", "imm", "imm2", "con2", + "mem", "omem", "nmem", "nam", "offset", "pointer", "con", "expr", 0 }; #endif @@ -578,28 +603,28 @@ static const yytype_uint16 yytoknum[] = 42, 47, 37, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, - 285, 286, 287, 288, 58, 59, 61, 44, 40, 41, - 36, 126 + 285, 286, 287, 288, 289, 290, 58, 59, 61, 44, + 40, 41, 36, 126 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { - 0, 52, 53, 54, 53, 56, 55, 57, 55, 55, - 55, 55, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 59, 59, 60, 61, 62, 62, 63, 63, 64, 64, - 64, 65, 66, 66, 67, 67, 67, 68, 68, 69, - 69, 70, 70, 71, 71, 71, 72, 72, 73, 74, - 75, 75, 76, 76, 76, 76, 76, 76, 76, 77, - 77, 78, 78, 78, 79, 79, 79, 79, 79, 79, - 80, 80, 80, 80, 80, 80, 80, 81, 82, 82, - 82, 82, 83, 83, 84, 84, 84, 84, 84, 84, - 84, 84, 84, 84, 84, 85, 85, 86, 86, 87, - 87, 87, 88, 88, 88, 89, 89, 89, 89, 89, - 89, 90, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 90 + 0, 54, 55, 56, 55, 58, 57, 59, 57, 57, + 57, 57, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 61, 61, 62, 63, 64, 64, 65, 65, + 66, 66, 66, 67, 68, 68, 69, 69, 69, 70, + 70, 71, 71, 72, 72, 73, 73, 73, 74, 74, + 75, 76, 77, 78, 79, 79, 80, 80, 80, 80, + 80, 80, 80, 81, 81, 82, 82, 82, 83, 83, + 83, 83, 83, 83, 84, 84, 84, 84, 84, 84, + 84, 85, 86, 86, 86, 86, 87, 87, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 89, + 89, 90, 90, 91, 91, 91, 92, 92, 92, 93, + 93, 93, 93, 93, 93, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ @@ -608,257 +633,269 @@ static const yytype_uint8 yyr2[] = 0, 2, 0, 0, 3, 0, 4, 0, 4, 1, 2, 2, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 0, 1, 3, 3, 2, 1, 2, 1, 2, 1, - 3, 5, 3, 5, 2, 1, 2, 1, 1, 3, - 5, 3, 5, 2, 1, 3, 3, 5, 5, 5, - 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, - 1, 4, 2, 2, 1, 1, 1, 1, 1, 1, - 2, 2, 2, 2, 4, 5, 3, 2, 1, 2, - 3, 4, 1, 1, 1, 4, 4, 6, 9, 9, - 3, 3, 4, 5, 8, 1, 6, 5, 7, 0, - 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, - 3, 1, 3, 3, 3, 3, 3, 4, 4, 3, - 3, 3 + 2, 2, 0, 1, 3, 3, 2, 1, 2, 1, + 2, 1, 3, 5, 3, 5, 2, 1, 2, 1, + 1, 3, 5, 3, 5, 2, 1, 3, 3, 5, + 5, 5, 3, 3, 1, 1, 1, 1, 2, 2, + 1, 1, 1, 1, 1, 4, 2, 2, 1, 1, + 1, 1, 1, 1, 2, 2, 2, 2, 4, 5, + 3, 2, 1, 2, 3, 4, 1, 1, 1, 4, + 4, 6, 9, 9, 3, 3, 4, 5, 8, 1, + 6, 5, 7, 0, 2, 2, 1, 1, 1, 1, + 1, 2, 2, 2, 3, 1, 3, 3, 3, 3, + 3, 4, 4, 3, 3, 3 }; -/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state - STATE-NUM when YYTABLE doesn't specify something else to do. Zero +/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { - 2, 3, 1, 0, 0, 30, 0, 0, 0, 0, - 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 9, 4, 0, 11, 31, 14, - 0, 0, 115, 74, 76, 79, 75, 77, 78, 109, - 116, 0, 0, 0, 15, 37, 60, 61, 92, 93, - 105, 94, 0, 16, 69, 35, 70, 17, 0, 18, - 0, 0, 109, 109, 0, 22, 45, 62, 66, 68, - 67, 63, 94, 20, 0, 31, 47, 48, 23, 109, - 0, 0, 19, 39, 0, 0, 21, 0, 24, 0, - 25, 0, 26, 54, 27, 0, 28, 0, 29, 0, - 7, 0, 5, 0, 10, 118, 117, 0, 0, 0, - 0, 36, 0, 0, 121, 0, 119, 0, 0, 0, - 83, 82, 0, 81, 80, 34, 0, 0, 64, 65, - 46, 72, 73, 0, 44, 0, 0, 72, 38, 0, - 0, 0, 0, 0, 53, 0, 0, 0, 0, 12, - 0, 13, 109, 110, 111, 0, 0, 100, 101, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, - 0, 0, 0, 0, 86, 0, 0, 32, 33, 0, - 0, 40, 0, 42, 0, 49, 51, 55, 56, 0, - 0, 0, 8, 6, 0, 114, 112, 113, 0, 0, - 0, 131, 130, 129, 0, 0, 122, 123, 124, 125, - 126, 0, 0, 95, 102, 96, 0, 84, 71, 0, - 0, 88, 87, 0, 0, 0, 0, 0, 0, 0, - 107, 103, 0, 127, 128, 0, 0, 0, 85, 41, - 89, 0, 43, 50, 52, 57, 58, 59, 0, 0, - 106, 97, 0, 0, 0, 90, 108, 0, 0, 0, - 91, 104, 0, 0, 98, 99 + 2, 3, 1, 0, 0, 32, 0, 0, 0, 0, + 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 9, 4, 0, 11, + 33, 14, 0, 0, 119, 78, 80, 83, 79, 81, + 82, 113, 120, 0, 0, 0, 15, 39, 64, 65, + 96, 97, 109, 98, 0, 16, 73, 37, 74, 17, + 0, 18, 0, 0, 113, 113, 0, 22, 47, 66, + 70, 72, 71, 67, 98, 20, 0, 33, 49, 50, + 23, 113, 0, 0, 19, 41, 0, 0, 21, 0, + 24, 0, 25, 0, 26, 56, 27, 0, 28, 0, + 29, 0, 30, 0, 31, 0, 7, 0, 5, 0, + 10, 122, 121, 0, 0, 0, 0, 38, 0, 0, + 125, 0, 123, 0, 0, 0, 87, 86, 0, 85, + 84, 36, 0, 0, 68, 69, 48, 76, 77, 0, + 46, 0, 0, 76, 40, 0, 0, 0, 0, 0, + 55, 0, 0, 0, 0, 0, 0, 12, 0, 13, + 113, 114, 115, 0, 0, 104, 105, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 124, 0, 0, + 0, 0, 90, 0, 0, 34, 35, 0, 0, 42, + 0, 44, 0, 51, 53, 57, 58, 0, 0, 0, + 62, 63, 8, 6, 0, 118, 116, 117, 0, 0, + 0, 135, 134, 133, 0, 0, 126, 127, 128, 129, + 130, 0, 0, 99, 106, 100, 0, 88, 75, 0, + 0, 92, 91, 0, 0, 0, 0, 0, 0, 0, + 111, 107, 0, 131, 132, 0, 0, 0, 89, 43, + 93, 0, 45, 52, 54, 59, 60, 61, 0, 0, + 110, 101, 0, 0, 0, 94, 112, 0, 0, 0, + 95, 108, 0, 0, 102, 103 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { - -1, 1, 3, 25, 150, 148, 26, 29, 57, 59, - 53, 44, 82, 73, 86, 65, 78, 88, 90, 92, - 94, 96, 98, 54, 66, 55, 67, 46, 56, 183, - 222, 47, 48, 49, 50, 110, 198, 51, 115 + -1, 1, 3, 27, 158, 156, 28, 31, 59, 61, + 55, 46, 84, 75, 88, 67, 80, 90, 92, 94, + 96, 98, 100, 102, 104, 56, 68, 57, 69, 48, + 58, 191, 232, 49, 50, 51, 52, 116, 208, 53, + 121 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ -#define YYPACT_NINF -100 +#define YYPACT_NINF -104 static const yytype_int16 yypact[] = { - -100, 22, -100, 165, -32, -22, 265, 288, 288, 334, - 195, -1, 311, 212, 416, 288, 288, 288, 416, 81, - 7, -16, 24, -12, -100, -100, -4, -100, -100, -100, - 469, 469, -100, -100, -100, -100, -100, -100, -100, 39, - -100, 334, 387, 469, -100, -100, -100, -100, -100, -100, - 46, 65, 372, -100, -100, 72, -100, -100, 83, -100, - 86, 334, 39, 102, 242, -100, -100, -100, -100, -100, - -100, -100, 77, -100, 117, 334, -100, -100, -100, 102, - 410, 469, -100, -100, 89, 90, -100, 92, -100, 97, - -100, 98, -100, 100, -100, 101, -100, 105, -100, 106, - -100, 469, -100, 469, -100, -100, -100, 135, 469, 469, - 114, -100, -6, 128, -100, 71, -100, 175, 32, 218, - -100, -100, 425, -100, -100, -100, 334, 288, -100, -100, - -100, 114, -100, 357, -100, 29, 469, -100, -100, 410, - 181, 440, 334, 334, 334, 457, 334, 334, 165, 164, - 165, 164, 102, -100, -100, 6, 469, 166, -100, 469, - 469, 469, 207, 208, 469, 469, 469, 469, 469, -100, - 206, 4, 173, 174, -100, 463, 176, -100, -100, 184, - 187, -100, 15, -100, 193, 200, 213, -100, -100, 211, - 217, 220, -100, -100, 222, -100, -100, -100, 216, 219, - 238, 517, 525, 78, 469, 469, 95, 95, -100, -100, - -100, 469, 469, 232, -100, -100, 237, -100, -100, 7, - 252, 278, -100, 239, 254, 256, 7, 469, 81, 263, - -100, -100, 293, 188, 188, 255, 258, 88, -100, -100, - 300, 281, -100, -100, -100, -100, -100, -100, 262, 469, - -100, -100, 304, 305, 289, -100, -100, 277, 469, 469, - -100, -100, 283, 284, -100, -100 + -104, 4, -104, 173, -26, -25, 277, 297, 297, 349, + 225, -14, 329, 396, 18, 297, 297, 297, 18, 171, + -20, 297, 297, 2, -4, 26, -104, -104, 43, -104, + -104, -104, 478, 478, -104, -104, -104, -104, -104, -104, + -104, 111, -104, 349, 402, 478, -104, -104, -104, -104, + -104, -104, -12, -5, 83, -104, -104, 44, -104, -104, + 46, -104, 49, 349, 111, 113, 245, -104, -104, -104, + -104, -104, -104, -104, 50, -104, 100, 349, -104, -104, + -104, 113, 420, 478, -104, -104, 64, 66, -104, 78, + -104, 80, -104, 85, -104, 89, -104, 93, -104, 98, + -104, 101, -104, 112, -104, 121, -104, 478, -104, 478, + -104, -104, -104, 153, 478, 478, 135, -104, 8, 163, + -104, 74, -104, 179, 52, 427, -104, -104, 445, -104, + -104, -104, 349, 297, -104, -104, -104, 135, -104, 381, + -104, 33, 478, -104, -104, 420, 186, 451, 349, 349, + 349, 460, 349, 349, 297, 297, 173, 172, 173, 172, + 113, -104, -104, 5, 478, 180, -104, 478, 478, 478, + 226, 224, 478, 478, 478, 478, 478, -104, 235, 36, + 195, 196, -104, 466, 197, -104, -104, 199, 202, -104, + 21, -104, 203, 211, 219, -104, -104, 217, 222, 223, + -104, -104, -104, -104, 229, -104, -104, -104, 240, 241, + 237, 232, 527, 534, 478, 478, 134, 134, -104, -104, + -104, 478, 478, 243, -104, -104, 248, -104, -104, -20, + 263, 287, -104, 249, 264, 265, -20, 478, 171, 269, + -104, -104, 294, 214, 214, 256, 258, 119, -104, -104, + 301, 280, -104, -104, -104, -104, -104, -104, 266, 478, + -104, -104, 308, 311, 292, -104, -104, 273, 478, 478, + -104, -104, 274, 278, -104, -104 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { - -100, -100, -100, -99, -100, -100, -100, 315, -100, -100, - -100, 318, -100, -100, -100, -100, -100, -100, -100, -100, - -100, -100, -100, 17, 270, 0, -7, -9, -8, 112, - -100, 13, 1, -3, -2, -44, -100, -10, -64 + -104, -104, -104, -103, -104, -104, -104, 319, -104, -104, + -104, 331, -104, -104, -104, -104, -104, -104, -104, -104, + -104, -104, -104, -104, -104, 19, 275, -2, -6, -9, + -8, 115, -104, 22, 1, -1, -3, -48, -104, -10, + -66 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which - number is the opposite. If zero, do what YYDEFACT says. - If YYTABLE_NINF, syntax error. */ + number is the opposite. If YYTABLE_NINF, syntax error. */ #define YYTABLE_NINF -1 static const yytype_uint16 yytable[] = { - 72, 68, 69, 85, 156, 84, 83, 71, 58, 74, - 97, 70, 99, 27, 212, 89, 91, 93, 131, 132, - 105, 106, 2, 45, 220, 28, 60, 87, 100, 45, - 101, 95, 114, 116, 103, 137, 195, 149, 196, 151, - 39, 104, 124, 157, 221, 107, 197, 108, 109, 192, - 123, 193, 128, 213, 72, 68, 69, 52, 111, 130, - 179, 71, 129, 171, 172, 70, 171, 172, 102, 173, - 85, 114, 173, 138, 159, 160, 161, 162, 163, 164, - 165, 166, 167, 168, 162, 163, 164, 165, 166, 167, - 168, 114, 111, 114, 117, 201, 202, 203, 153, 154, - 206, 207, 208, 209, 210, 166, 167, 168, 194, 106, - 108, 109, 114, 118, 33, 34, 35, 36, 37, 125, - 169, 38, 252, 253, 128, 135, 180, 178, 136, 85, - 126, 184, 181, 127, 129, 189, 139, 188, 140, 141, - 233, 234, 152, 177, 142, 143, 199, 144, 145, 114, - 114, 114, 146, 147, 114, 114, 114, 114, 114, 185, - 186, 187, 155, 190, 191, 106, 4, 159, 160, 161, - 162, 163, 164, 165, 166, 167, 168, 158, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 114, 114, 164, 165, 166, 167, - 168, 235, 236, 30, 31, 61, 21, 22, 23, 170, - 24, 239, 179, 204, 200, 205, 211, 246, 245, 247, - 30, 31, 214, 215, 32, 217, 30, 31, 33, 34, - 35, 36, 37, 218, 219, 38, 62, 63, 40, 257, - 223, 32, 64, 42, 224, 52, 43, 32, 262, 263, - 30, 31, 133, 79, 63, 40, 174, 225, 226, 80, - 81, 40, 52, 43, 227, 230, 81, 228, 231, 43, - 229, 32, 232, 30, 31, 33, 34, 35, 36, 37, - 237, 240, 38, 62, 63, 40, 238, 241, 243, 182, - 42, 244, 52, 43, 32, 248, 30, 31, 33, 34, - 35, 36, 37, 249, 250, 38, 39, 251, 40, 254, - 255, 256, 41, 42, 258, 259, 43, 32, 260, 30, - 31, 33, 34, 35, 36, 37, 261, 76, 38, 39, - 77, 40, 264, 265, 134, 242, 42, 0, 52, 43, - 32, 0, 30, 31, 33, 34, 35, 36, 37, 0, - 0, 38, 39, 0, 40, 0, 0, 0, 75, 42, - 0, 0, 43, 32, 0, 30, 31, 33, 34, 35, - 36, 37, 0, 0, 38, 39, 0, 40, 0, 0, - 30, 119, 42, 0, 0, 43, 32, 0, 0, 0, - 33, 34, 35, 36, 37, 30, 31, 38, 0, 0, - 40, 32, 0, 0, 0, 42, 0, 0, 43, 0, - 120, 121, 0, 39, 0, 40, 32, 0, 30, 31, - 122, 112, 0, 43, 30, 31, 0, 113, 0, 0, - 40, 0, 0, 30, 175, 81, 0, 0, 43, 32, - 0, 0, 0, 0, 0, 32, 0, 0, 30, 31, - 0, 79, 63, 40, 32, 0, 0, 39, 81, 40, - 0, 43, 0, 176, 42, 30, 31, 43, 40, 32, - 0, 30, 31, 81, 0, 0, 43, 30, 31, 0, - 0, 0, 0, 40, 0, 0, 32, 0, 81, 0, - 182, 43, 32, 0, 0, 0, 0, 0, 32, 0, - 40, 216, 0, 0, 0, 81, 40, 52, 43, 0, - 0, 81, 40, 0, 43, 0, 0, 81, 0, 0, - 43, 160, 161, 162, 163, 164, 165, 166, 167, 168, - 161, 162, 163, 164, 165, 166, 167, 168 + 74, 70, 71, 87, 2, 86, 60, 85, 76, 73, + 99, 72, 101, 91, 93, 95, 137, 138, 164, 103, + 105, 29, 111, 112, 30, 47, 32, 33, 62, 41, + 230, 47, 54, 143, 120, 122, 89, 205, 123, 206, + 97, 157, 108, 159, 130, 124, 222, 207, 106, 34, + 107, 129, 231, 202, 134, 203, 74, 70, 71, 165, + 136, 41, 117, 42, 135, 73, 187, 72, 44, 179, + 180, 45, 87, 120, 109, 181, 144, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 223, 179, 180, + 110, 32, 125, 131, 181, 132, 117, 120, 133, 120, + 141, 211, 212, 213, 161, 162, 216, 217, 218, 219, + 220, 142, 204, 145, 34, 112, 146, 113, 120, 114, + 115, 114, 115, 126, 127, 177, 41, 147, 42, 148, + 134, 186, 188, 128, 149, 87, 45, 192, 150, 189, + 135, 197, 151, 196, 174, 175, 176, 152, 243, 244, + 153, 185, 200, 201, 209, 262, 263, 120, 120, 120, + 160, 154, 120, 120, 120, 120, 120, 193, 194, 195, + 155, 198, 199, 112, 4, 167, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 163, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 120, 120, 35, 36, 37, 38, + 39, 245, 246, 40, 166, 178, 23, 24, 25, 187, + 26, 249, 172, 173, 174, 175, 176, 256, 255, 257, + 210, 215, 214, 32, 33, 63, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 221, 224, 225, 227, 267, + 228, 229, 233, 32, 33, 139, 34, 234, 272, 273, + 35, 36, 37, 38, 39, 235, 236, 40, 64, 65, + 42, 237, 238, 242, 66, 44, 34, 54, 45, 239, + 35, 36, 37, 38, 39, 32, 33, 40, 64, 65, + 42, 240, 241, 247, 250, 44, 251, 54, 45, 248, + 253, 190, 254, 258, 259, 32, 33, 260, 34, 261, + 264, 265, 35, 36, 37, 38, 39, 266, 268, 40, + 41, 269, 42, 270, 271, 274, 43, 44, 34, 275, + 45, 78, 35, 36, 37, 38, 39, 32, 33, 40, + 41, 140, 42, 79, 0, 0, 0, 44, 252, 54, + 45, 0, 0, 0, 0, 0, 0, 32, 33, 0, + 34, 0, 0, 0, 35, 36, 37, 38, 39, 0, + 0, 40, 41, 0, 42, 0, 0, 0, 77, 44, + 34, 0, 45, 0, 35, 36, 37, 38, 39, 32, + 33, 40, 41, 0, 42, 0, 0, 0, 0, 44, + 0, 0, 45, 0, 32, 33, 0, 0, 0, 0, + 32, 33, 34, 0, 0, 0, 35, 36, 37, 38, + 39, 0, 0, 40, 0, 0, 42, 34, 32, 33, + 0, 44, 0, 34, 45, 32, 33, 0, 118, 81, + 65, 42, 0, 0, 119, 82, 83, 42, 54, 45, + 0, 34, 83, 32, 183, 45, 0, 0, 34, 32, + 33, 0, 0, 81, 65, 42, 0, 182, 32, 33, + 83, 0, 42, 45, 32, 33, 34, 83, 0, 0, + 45, 0, 34, 0, 0, 184, 32, 33, 0, 0, + 42, 34, 0, 0, 0, 83, 42, 34, 45, 0, + 0, 83, 0, 190, 45, 42, 226, 0, 0, 34, + 83, 42, 54, 45, 0, 0, 83, 0, 0, 45, + 0, 0, 0, 42, 0, 0, 0, 0, 83, 0, + 0, 45, 169, 170, 171, 172, 173, 174, 175, 176, + 170, 171, 172, 173, 174, 175, 176 }; +#define yypact_value_is_default(yystate) \ + ((yystate) == (-104)) + +#define yytable_value_is_error(yytable_value) \ + YYID (0) + static const yytype_int16 yycheck[] = { - 10, 10, 10, 13, 10, 13, 13, 10, 8, 11, - 19, 10, 20, 45, 10, 15, 16, 17, 62, 63, - 30, 31, 0, 6, 9, 47, 9, 14, 44, 12, - 46, 18, 42, 43, 46, 79, 30, 101, 32, 103, - 41, 45, 52, 49, 29, 6, 40, 8, 9, 148, - 52, 150, 61, 49, 64, 64, 64, 50, 41, 61, - 31, 64, 61, 34, 35, 64, 34, 35, 44, 40, - 80, 81, 40, 80, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 6, 7, 8, 9, 10, 11, - 12, 101, 75, 103, 48, 159, 160, 161, 108, 109, - 164, 165, 166, 167, 168, 10, 11, 12, 152, 119, - 8, 9, 122, 48, 33, 34, 35, 36, 37, 47, - 49, 40, 34, 35, 133, 48, 136, 127, 11, 139, - 47, 141, 139, 47, 133, 145, 47, 145, 48, 47, - 204, 205, 7, 126, 47, 47, 156, 47, 47, 159, - 160, 161, 47, 47, 164, 165, 166, 167, 168, 142, - 143, 144, 48, 146, 147, 175, 1, 3, 4, 5, - 6, 7, 8, 9, 10, 11, 12, 49, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 204, 205, 8, 9, 10, 11, - 12, 211, 212, 8, 9, 10, 41, 42, 43, 34, - 45, 219, 31, 6, 48, 7, 10, 227, 226, 228, - 8, 9, 49, 49, 29, 49, 8, 9, 33, 34, - 35, 36, 37, 49, 47, 40, 41, 42, 43, 249, - 47, 29, 47, 48, 44, 50, 51, 29, 258, 259, - 8, 9, 10, 41, 42, 43, 38, 44, 47, 47, - 48, 43, 50, 51, 47, 49, 48, 47, 49, 51, - 48, 29, 34, 8, 9, 33, 34, 35, 36, 37, - 48, 29, 40, 41, 42, 43, 49, 9, 34, 50, - 48, 35, 50, 51, 29, 32, 8, 9, 33, 34, - 35, 36, 37, 10, 49, 40, 41, 49, 43, 9, - 29, 49, 47, 48, 10, 10, 51, 29, 29, 8, - 9, 33, 34, 35, 36, 37, 49, 12, 40, 41, - 12, 43, 49, 49, 64, 223, 48, -1, 50, 51, - 29, -1, 8, 9, 33, 34, 35, 36, 37, -1, - -1, 40, 41, -1, 43, -1, -1, -1, 47, 48, - -1, -1, 51, 29, -1, 8, 9, 33, 34, 35, - 36, 37, -1, -1, 40, 41, -1, 43, -1, -1, - 8, 9, 48, -1, -1, 51, 29, -1, -1, -1, - 33, 34, 35, 36, 37, 8, 9, 40, -1, -1, - 43, 29, -1, -1, -1, 48, -1, -1, 51, -1, - 38, 39, -1, 41, -1, 43, 29, -1, 8, 9, - 48, 34, -1, 51, 8, 9, -1, 40, -1, -1, - 43, -1, -1, 8, 9, 48, -1, -1, 51, 29, - -1, -1, -1, -1, -1, 29, -1, -1, 8, 9, - -1, 41, 42, 43, 29, -1, -1, 41, 48, 43, - -1, 51, -1, 38, 48, 8, 9, 51, 43, 29, - -1, 8, 9, 48, -1, -1, 51, 8, 9, -1, - -1, -1, -1, 43, -1, -1, 29, -1, 48, -1, - 50, 51, 29, -1, -1, -1, -1, -1, 29, -1, - 43, 38, -1, -1, -1, 48, 43, 50, 51, -1, - -1, 48, 43, -1, 51, -1, -1, 48, -1, -1, - 51, 4, 5, 6, 7, 8, 9, 10, 11, 12, - 5, 6, 7, 8, 9, 10, 11, 12 + 10, 10, 10, 13, 0, 13, 8, 13, 11, 10, + 19, 10, 20, 15, 16, 17, 64, 65, 10, 21, + 22, 47, 32, 33, 49, 6, 8, 9, 9, 43, + 9, 12, 52, 81, 44, 45, 14, 32, 50, 34, + 18, 107, 46, 109, 54, 50, 10, 42, 46, 31, + 48, 54, 31, 156, 63, 158, 66, 66, 66, 51, + 63, 43, 43, 45, 63, 66, 33, 66, 50, 36, + 37, 53, 82, 83, 48, 42, 82, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 51, 36, 37, + 47, 8, 9, 49, 42, 49, 77, 107, 49, 109, + 50, 167, 168, 169, 114, 115, 172, 173, 174, 175, + 176, 11, 160, 49, 31, 125, 50, 6, 128, 8, + 9, 8, 9, 40, 41, 51, 43, 49, 45, 49, + 139, 133, 142, 50, 49, 145, 53, 147, 49, 145, + 139, 151, 49, 151, 10, 11, 12, 49, 214, 215, + 49, 132, 154, 155, 164, 36, 37, 167, 168, 169, + 7, 49, 172, 173, 174, 175, 176, 148, 149, 150, + 49, 152, 153, 183, 1, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 50, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 214, 215, 35, 36, 37, 38, + 39, 221, 222, 42, 51, 36, 43, 44, 45, 33, + 47, 229, 8, 9, 10, 11, 12, 237, 236, 238, + 50, 7, 6, 8, 9, 10, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 10, 51, 51, 51, 259, + 51, 49, 49, 8, 9, 10, 31, 46, 268, 269, + 35, 36, 37, 38, 39, 46, 49, 42, 43, 44, + 45, 49, 49, 36, 49, 50, 31, 52, 53, 50, + 35, 36, 37, 38, 39, 8, 9, 42, 43, 44, + 45, 51, 51, 50, 31, 50, 9, 52, 53, 51, + 36, 52, 37, 34, 10, 8, 9, 51, 31, 51, + 9, 31, 35, 36, 37, 38, 39, 51, 10, 42, + 43, 10, 45, 31, 51, 51, 49, 50, 31, 51, + 53, 12, 35, 36, 37, 38, 39, 8, 9, 42, + 43, 66, 45, 12, -1, -1, -1, 50, 233, 52, + 53, -1, -1, -1, -1, -1, -1, 8, 9, -1, + 31, -1, -1, -1, 35, 36, 37, 38, 39, -1, + -1, 42, 43, -1, 45, -1, -1, -1, 49, 50, + 31, -1, 53, -1, 35, 36, 37, 38, 39, 8, + 9, 42, 43, -1, 45, -1, -1, -1, -1, 50, + -1, -1, 53, -1, 8, 9, -1, -1, -1, -1, + 8, 9, 31, -1, -1, -1, 35, 36, 37, 38, + 39, -1, -1, 42, -1, -1, 45, 31, 8, 9, + -1, 50, -1, 31, 53, 8, 9, -1, 36, 43, + 44, 45, -1, -1, 42, 49, 50, 45, 52, 53, + -1, 31, 50, 8, 9, 53, -1, -1, 31, 8, + 9, -1, -1, 43, 44, 45, -1, 40, 8, 9, + 50, -1, 45, 53, 8, 9, 31, 50, -1, -1, + 53, -1, 31, -1, -1, 40, 8, 9, -1, -1, + 45, 31, -1, -1, -1, 50, 45, 31, 53, -1, + -1, 50, -1, 52, 53, 45, 40, -1, -1, 31, + 50, 45, 52, 53, -1, -1, 50, -1, -1, 53, + -1, -1, -1, 45, -1, -1, -1, -1, 50, -1, + -1, 53, 5, 6, 7, 8, 9, 10, 11, 12, + 6, 7, 8, 9, 10, 11, 12 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { - 0, 53, 0, 54, 1, 13, 14, 15, 16, 17, + 0, 55, 0, 56, 1, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, - 28, 41, 42, 43, 45, 55, 58, 45, 47, 59, - 8, 9, 29, 33, 34, 35, 36, 37, 40, 41, - 43, 47, 48, 51, 63, 75, 79, 83, 84, 85, - 86, 89, 50, 62, 75, 77, 80, 60, 77, 61, - 75, 10, 41, 42, 47, 67, 76, 78, 79, 80, - 84, 85, 89, 65, 86, 47, 59, 63, 68, 41, - 47, 48, 64, 78, 80, 89, 66, 83, 69, 77, - 70, 77, 71, 77, 72, 83, 73, 79, 74, 80, - 44, 46, 44, 46, 45, 89, 89, 6, 8, 9, - 87, 75, 34, 40, 89, 90, 89, 48, 48, 9, - 38, 39, 48, 86, 89, 47, 47, 47, 79, 84, - 86, 87, 87, 10, 76, 48, 11, 87, 78, 47, - 48, 47, 47, 47, 47, 47, 47, 47, 57, 90, - 56, 90, 7, 89, 89, 48, 10, 49, 49, 3, - 4, 5, 6, 7, 8, 9, 10, 11, 12, 49, - 34, 34, 35, 40, 38, 9, 38, 75, 77, 31, - 89, 78, 50, 81, 89, 75, 75, 75, 80, 89, - 75, 75, 55, 55, 87, 30, 32, 40, 88, 89, - 48, 90, 90, 90, 6, 7, 90, 90, 90, 90, - 90, 10, 10, 49, 49, 49, 38, 49, 49, 47, - 9, 29, 82, 47, 44, 44, 47, 47, 47, 48, - 49, 49, 34, 90, 90, 89, 89, 48, 49, 80, - 29, 9, 81, 34, 35, 80, 89, 79, 32, 10, - 49, 49, 34, 35, 9, 29, 49, 89, 10, 10, - 29, 49, 89, 89, 49, 49 + 28, 29, 30, 43, 44, 45, 47, 57, 60, 47, + 49, 61, 8, 9, 31, 35, 36, 37, 38, 39, + 42, 43, 45, 49, 50, 53, 65, 79, 83, 87, + 88, 89, 90, 93, 52, 64, 79, 81, 84, 62, + 81, 63, 79, 10, 43, 44, 49, 69, 80, 82, + 83, 84, 88, 89, 93, 67, 90, 49, 61, 65, + 70, 43, 49, 50, 66, 82, 84, 93, 68, 87, + 71, 81, 72, 81, 73, 81, 74, 87, 75, 83, + 76, 84, 77, 81, 78, 81, 46, 48, 46, 48, + 47, 93, 93, 6, 8, 9, 91, 79, 36, 42, + 93, 94, 93, 50, 50, 9, 40, 41, 50, 90, + 93, 49, 49, 49, 83, 88, 90, 91, 91, 10, + 80, 50, 11, 91, 82, 49, 50, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 59, 94, 58, 94, + 7, 93, 93, 50, 10, 51, 51, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 51, 36, 36, + 37, 42, 40, 9, 40, 79, 81, 33, 93, 82, + 52, 85, 93, 79, 79, 79, 84, 93, 79, 79, + 81, 81, 57, 57, 91, 32, 34, 42, 92, 93, + 50, 94, 94, 94, 6, 7, 94, 94, 94, 94, + 94, 10, 10, 51, 51, 51, 40, 51, 51, 49, + 9, 31, 86, 49, 46, 46, 49, 49, 49, 50, + 51, 51, 36, 94, 94, 93, 93, 50, 51, 84, + 31, 9, 85, 36, 37, 84, 93, 83, 34, 10, + 51, 51, 36, 37, 9, 31, 51, 93, 10, 10, + 31, 51, 93, 93, 51, 51 }; #define yyerrok (yyerrstatus = 0) @@ -873,9 +910,18 @@ static const yytype_uint8 yystos[] = /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. - Once GCC version 2 has supplanted version 1, this can go. */ + Once GCC version 2 has supplanted version 1, this can go. However, + YYFAIL appears to be in use. Nevertheless, it is formally deprecated + in Bison 2.4.2's NEWS entry, where a plan to phase it out is + discussed. */ #define YYFAIL goto yyerrlab +#if defined YYFAIL + /* This is here to suppress warnings from the GCC cpp's + -Wunused-macros. Normally we don't worry about that warning, but + some users do, and we want to make it easy for users to remove + YYFAIL uses, which will produce warnings from Bison 2.5. */ +#endif #define YYRECOVERING() (!!yyerrstatus) @@ -885,7 +931,6 @@ do \ { \ yychar = (Token); \ yylval = (Value); \ - yytoken = YYTRANSLATE (yychar); \ YYPOPSTACK (1); \ goto yybackup; \ } \ @@ -927,19 +972,10 @@ while (YYID (0)) #endif -/* YY_LOCATION_PRINT -- Print the location on the stream. - This macro was not mandated originally: define only if we know - we won't break user code: when these are the locations we know. */ +/* This macro is provided for backward compatibility. */ #ifndef YY_LOCATION_PRINT -# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL -# define YY_LOCATION_PRINT(File, Loc) \ - fprintf (File, "%d.%d-%d.%d", \ - (Loc).first_line, (Loc).first_column, \ - (Loc).last_line, (Loc).last_column) -# else -# define YY_LOCATION_PRINT(File, Loc) ((void) 0) -# endif +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) #endif @@ -1043,17 +1079,20 @@ yy_symbol_print (yyoutput, yytype, yyvaluep) #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void -yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) #else static void -yy_stack_print (bottom, top) - yytype_int16 *bottom; - yytype_int16 *top; +yy_stack_print (yybottom, yytop) + yytype_int16 *yybottom; + yytype_int16 *yytop; #endif { YYFPRINTF (stderr, "Stack now"); - for (; bottom <= top; ++bottom) - YYFPRINTF (stderr, " %d", *bottom); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } YYFPRINTF (stderr, "\n"); } @@ -1087,11 +1126,11 @@ yy_reduce_print (yyvsp, yyrule) /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { - fprintf (stderr, " $%d = ", yyi + 1); + YYFPRINTF (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], &(yyvsp[(yyi + 1) - (yynrhs)]) ); - fprintf (stderr, "\n"); + YYFPRINTF (stderr, "\n"); } } @@ -1128,7 +1167,6 @@ int yydebug; # define YYMAXDEPTH 10000 #endif - #if YYERROR_VERBOSE @@ -1231,115 +1269,142 @@ yytnamerr (char *yyres, const char *yystr) } # endif -/* Copy into YYRESULT an error message about the unexpected token - YYCHAR while in state YYSTATE. Return the number of bytes copied, - including the terminating null byte. If YYRESULT is null, do not - copy anything; just return the number of bytes that would be - copied. As a special case, return 0 if an ordinary "syntax error" - message will do. Return YYSIZE_MAXIMUM if overflow occurs during - size calculation. */ -static YYSIZE_T -yysyntax_error (char *yyresult, int yystate, int yychar) -{ - int yyn = yypact[yystate]; +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. - if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) - return 0; - else - { - int yytype = YYTRANSLATE (yychar); - YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); - YYSIZE_T yysize = yysize0; - YYSIZE_T yysize1; - int yysize_overflow = 0; - enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; - char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; - int yyx; - -# if 0 - /* This is so xgettext sees the translatable formats that are - constructed on the fly. */ - YY_("syntax error, unexpected %s"); - YY_("syntax error, unexpected %s, expecting %s"); - YY_("syntax error, unexpected %s, expecting %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); -# endif - char *yyfmt; - char const *yyf; - static char const yyunexpected[] = "syntax error, unexpected %s"; - static char const yyexpecting[] = ", expecting %s"; - static char const yyor[] = " or %s"; - char yyformat[sizeof yyunexpected - + sizeof yyexpecting - 1 - + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) - * (sizeof yyor - 1))]; - char const *yyprefix = yyexpecting; - - /* Start YYX at -YYN if negative to avoid negative indexes in - YYCHECK. */ - int yyxbegin = yyn < 0 ? -yyn : 0; - - /* Stay within bounds of both yycheck and yytname. */ - int yychecklim = YYLAST - yyn + 1; - int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; - int yycount = 1; - - yyarg[0] = yytname[yytype]; - yyfmt = yystpcpy (yyformat, yyunexpected); - - for (yyx = yyxbegin; yyx < yyxend; ++yyx) - if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) - { - if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) - { - yycount = 1; - yysize = yysize0; - yyformat[sizeof yyunexpected - 1] = '\0'; - break; - } - yyarg[yycount++] = yytname[yyx]; - yysize1 = yysize + yytnamerr (0, yytname[yyx]); - yysize_overflow |= (yysize1 < yysize); - yysize = yysize1; - yyfmt = yystpcpy (yyfmt, yyprefix); - yyprefix = yyor; - } + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) +{ + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = 0; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; + + /* There are many possibilities here to consider: + - Assume YYFAIL is not used. It's too flawed to consider. See + <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html> + for details. YYERROR is fine as it does not invoke this + function. + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } - yyf = YY_(yyformat); - yysize1 = yysize + yystrlen (yyf); - yysize_overflow |= (yysize1 < yysize); - yysize = yysize1; + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } - if (yysize_overflow) - return YYSIZE_MAXIMUM; + yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; - if (yyresult) - { - /* Avoid sprintf, as that infringes on the user's name space. - Don't have undefined behavior even if the translation - produced a string with the wrong number of "%s"s. */ - char *yyp = yyresult; - int yyi = 0; - while ((*yyp = *yyf) != '\0') - { - if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) - { - yyp += yytnamerr (yyp, yyarg[yyi++]); - yyf += 2; - } - else - { - yyp++; - yyf++; - } - } - } - return yysize; + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; } #endif /* YYERROR_VERBOSE */ - /*-----------------------------------------------. | Release the memory associated to this symbol. | @@ -1371,10 +1436,9 @@ yydestruct (yymsg, yytype, yyvaluep) break; } } - -/* Prevent warnings from -Wmissing-prototypes. */ +/* Prevent warnings from -Wmissing-prototypes. */ #ifdef YYPARSE_PARAM #if defined __STDC__ || defined __cplusplus int yyparse (void *YYPARSE_PARAM); @@ -1390,18 +1454,16 @@ int yyparse (); #endif /* ! YYPARSE_PARAM */ - -/* The look-ahead symbol. */ +/* The lookahead symbol. */ int yychar; -/* The semantic value of the look-ahead symbol. */ +/* The semantic value of the lookahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; - /*----------. | yyparse. | `----------*/ @@ -1428,66 +1490,66 @@ yyparse () #endif #endif { - - int yystate; - int yyn; - int yyresult; - /* Number of tokens to shift before error messages enabled. */ - int yyerrstatus; - /* Look-ahead token as an internal (translated) token number. */ - int yytoken = 0; -#if YYERROR_VERBOSE - /* Buffer for error messages, and its allocated size. */ - char yymsgbuf[128]; - char *yymsg = yymsgbuf; - YYSIZE_T yymsg_alloc = sizeof yymsgbuf; -#endif - - /* Three stacks and their tools: - `yyss': related to states, - `yyvs': related to semantic values, - `yyls': related to locations. - - Refer to the stacks thru separate pointers, to allow yyoverflow - to reallocate them elsewhere. */ - - /* The state stack. */ - yytype_int16 yyssa[YYINITDEPTH]; - yytype_int16 *yyss = yyssa; - yytype_int16 *yyssp; + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; - /* The semantic value stack. */ - YYSTYPE yyvsa[YYINITDEPTH]; - YYSTYPE *yyvs = yyvsa; - YYSTYPE *yyvsp; + /* The stacks and their tools: + `yyss': related to states. + `yyvs': related to semantic values. + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss; + yytype_int16 *yyssp; -#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; - YYSIZE_T yystacksize = YYINITDEPTH; + YYSIZE_T yystacksize; + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; + yytoken = 0; + yyss = yyssa; + yyvs = yyvsa; + yystacksize = YYINITDEPTH; + YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; - yychar = YYEMPTY; /* Cause a token to be read. */ + yychar = YYEMPTY; /* Cause a token to be read. */ /* Initialize stack pointers. Waste one element of value and location stack so that they stay on the same level as the state stack. The wasted elements are never initialized. */ - yyssp = yyss; yyvsp = yyvs; @@ -1517,7 +1579,6 @@ yyparse () YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; - /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might @@ -1525,7 +1586,6 @@ yyparse () yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), - &yystacksize); yyss = yyss1; @@ -1548,9 +1608,8 @@ yyparse () (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; - YYSTACK_RELOCATE (yyss); - YYSTACK_RELOCATE (yyvs); - + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); @@ -1561,7 +1620,6 @@ yyparse () yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; - YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); @@ -1571,6 +1629,9 @@ yyparse () YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + if (yystate == YYFINAL) + YYACCEPT; + goto yybackup; /*-----------. @@ -1579,16 +1640,16 @@ yyparse () yybackup: /* Do appropriate processing given the current state. Read a - look-ahead token if we need one and don't already have one. */ + lookahead token if we need one and don't already have one. */ - /* First try to decide what to do without reference to look-ahead token. */ + /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; - if (yyn == YYPACT_NINF) + if (yypact_value_is_default (yyn)) goto yydefault; - /* Not known => get a look-ahead token if don't already have one. */ + /* Not known => get a lookahead token if don't already have one. */ - /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); @@ -1614,26 +1675,22 @@ yybackup: yyn = yytable[yyn]; if (yyn <= 0) { - if (yyn == 0 || yyn == YYTABLE_NINF) - goto yyerrlab; + if (yytable_value_is_error (yyn)) + goto yyerrlab; yyn = -yyn; goto yyreduce; } - if (yyn == YYFINAL) - YYACCEPT; - /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; - /* Shift the look-ahead token. */ + /* Shift the lookahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); - /* Discard the shifted token unless it is eof. */ - if (yychar != YYEOF) - yychar = YYEMPTY; + /* Discard the shifted token. */ + yychar = YYEMPTY; yystate = yyn; *++yyvsp = yylval; @@ -1673,14 +1730,18 @@ yyreduce: switch (yyn) { case 3: -#line 70 "a.y" + +/* Line 1806 of yacc.c */ +#line 71 "a.y" { stmtline = lineno; } break; case 5: -#line 77 "a.y" + +/* Line 1806 of yacc.c */ +#line 78 "a.y" { if((yyvsp[(1) - (2)].sym)->value != pc) yyerror("redeclaration of %s", (yyvsp[(1) - (2)].sym)->name); @@ -1689,7 +1750,9 @@ yyreduce: break; case 7: -#line 84 "a.y" + +/* Line 1806 of yacc.c */ +#line 85 "a.y" { (yyvsp[(1) - (2)].sym)->type = LLAB; (yyvsp[(1) - (2)].sym)->value = pc; @@ -1697,7 +1760,9 @@ yyreduce: break; case 12: -#line 95 "a.y" + +/* Line 1806 of yacc.c */ +#line 96 "a.y" { (yyvsp[(1) - (3)].sym)->type = LVAR; (yyvsp[(1) - (3)].sym)->value = (yyvsp[(3) - (3)].lval); @@ -1705,7 +1770,9 @@ yyreduce: break; case 13: -#line 100 "a.y" + +/* Line 1806 of yacc.c */ +#line 101 "a.y" { if((yyvsp[(1) - (3)].sym)->value != (yyvsp[(3) - (3)].lval)) yyerror("redeclaration of %s", (yyvsp[(1) - (3)].sym)->name); @@ -1714,175 +1781,245 @@ yyreduce: break; case 14: -#line 105 "a.y" + +/* Line 1806 of yacc.c */ +#line 106 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 15: -#line 106 "a.y" + +/* Line 1806 of yacc.c */ +#line 107 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 16: -#line 107 "a.y" + +/* Line 1806 of yacc.c */ +#line 108 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 17: -#line 108 "a.y" + +/* Line 1806 of yacc.c */ +#line 109 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 18: -#line 109 "a.y" + +/* Line 1806 of yacc.c */ +#line 110 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 19: -#line 110 "a.y" + +/* Line 1806 of yacc.c */ +#line 111 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 20: -#line 111 "a.y" + +/* Line 1806 of yacc.c */ +#line 112 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 21: -#line 112 "a.y" + +/* Line 1806 of yacc.c */ +#line 113 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 22: -#line 113 "a.y" + +/* Line 1806 of yacc.c */ +#line 114 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 23: -#line 114 "a.y" + +/* Line 1806 of yacc.c */ +#line 115 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 24: -#line 115 "a.y" + +/* Line 1806 of yacc.c */ +#line 116 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 25: -#line 116 "a.y" + +/* Line 1806 of yacc.c */ +#line 117 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 26: -#line 117 "a.y" + +/* Line 1806 of yacc.c */ +#line 118 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 27: -#line 118 "a.y" + +/* Line 1806 of yacc.c */ +#line 119 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 28: -#line 119 "a.y" + +/* Line 1806 of yacc.c */ +#line 120 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 29: -#line 120 "a.y" + +/* Line 1806 of yacc.c */ +#line 121 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 30: + +/* Line 1806 of yacc.c */ +#line 122 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 31: + +/* Line 1806 of yacc.c */ #line 123 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 32: + +/* Line 1806 of yacc.c */ +#line 126 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = nullgen; } break; - case 31: -#line 128 "a.y" + case 33: + +/* Line 1806 of yacc.c */ +#line 131 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = nullgen; } break; - case 32: -#line 135 "a.y" + case 34: + +/* Line 1806 of yacc.c */ +#line 138 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 33: -#line 142 "a.y" + case 35: + +/* Line 1806 of yacc.c */ +#line 145 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 34: -#line 149 "a.y" + case 36: + +/* Line 1806 of yacc.c */ +#line 152 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (2)].gen); (yyval.gen2).to = nullgen; } break; - case 35: -#line 154 "a.y" + case 37: + +/* Line 1806 of yacc.c */ +#line 157 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (1)].gen); (yyval.gen2).to = nullgen; } break; - case 36: -#line 161 "a.y" + case 38: + +/* Line 1806 of yacc.c */ +#line 164 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(2) - (2)].gen); } break; - case 37: -#line 166 "a.y" + case 39: + +/* Line 1806 of yacc.c */ +#line 169 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(1) - (1)].gen); } break; - case 38: -#line 173 "a.y" + case 40: + +/* Line 1806 of yacc.c */ +#line 176 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(2) - (2)].gen); } break; - case 39: -#line 178 "a.y" + case 41: + +/* Line 1806 of yacc.c */ +#line 181 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(1) - (1)].gen); } break; - case 40: -#line 183 "a.y" + case 42: + +/* Line 1806 of yacc.c */ +#line 186 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 41: -#line 190 "a.y" + case 43: + +/* Line 1806 of yacc.c */ +#line 193 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); @@ -1890,16 +2027,20 @@ yyreduce: } break; - case 42: -#line 198 "a.y" + case 44: + +/* Line 1806 of yacc.c */ +#line 201 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 43: -#line 203 "a.y" + case 45: + +/* Line 1806 of yacc.c */ +#line 206 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); @@ -1907,24 +2048,30 @@ yyreduce: } break; - case 44: -#line 211 "a.y" + case 46: + +/* Line 1806 of yacc.c */ +#line 214 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(2) - (2)].gen); } break; - case 45: -#line 216 "a.y" + case 47: + +/* Line 1806 of yacc.c */ +#line 219 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(1) - (1)].gen); } break; - case 46: -#line 221 "a.y" + case 48: + +/* Line 1806 of yacc.c */ +#line 224 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(2) - (2)].gen); @@ -1933,16 +2080,20 @@ yyreduce: } break; - case 49: -#line 234 "a.y" + case 51: + +/* Line 1806 of yacc.c */ +#line 237 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 50: -#line 239 "a.y" + case 52: + +/* Line 1806 of yacc.c */ +#line 242 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).to = (yyvsp[(3) - (5)].gen); @@ -1952,16 +2103,20 @@ yyreduce: } break; - case 51: -#line 249 "a.y" + case 53: + +/* Line 1806 of yacc.c */ +#line 252 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 52: -#line 254 "a.y" + case 54: + +/* Line 1806 of yacc.c */ +#line 257 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).to = (yyvsp[(3) - (5)].gen); @@ -1971,40 +2126,50 @@ yyreduce: } break; - case 53: -#line 264 "a.y" + case 55: + +/* Line 1806 of yacc.c */ +#line 267 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (2)].gen); (yyval.gen2).to = nullgen; } break; - case 54: -#line 269 "a.y" + case 56: + +/* Line 1806 of yacc.c */ +#line 272 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (1)].gen); (yyval.gen2).to = nullgen; } break; - case 55: -#line 274 "a.y" + case 57: + +/* Line 1806 of yacc.c */ +#line 277 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 56: -#line 281 "a.y" + case 58: + +/* Line 1806 of yacc.c */ +#line 284 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 57: -#line 286 "a.y" + case 59: + +/* Line 1806 of yacc.c */ +#line 289 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); @@ -2012,8 +2177,10 @@ yyreduce: } break; - case 58: -#line 294 "a.y" + case 60: + +/* Line 1806 of yacc.c */ +#line 297 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).to = (yyvsp[(3) - (5)].gen); @@ -2021,8 +2188,10 @@ yyreduce: } break; - case 59: -#line 302 "a.y" + case 61: + +/* Line 1806 of yacc.c */ +#line 305 "a.y" { (yyval.gen2).from = (yyvsp[(3) - (5)].gen); (yyval.gen2).to = (yyvsp[(5) - (5)].gen); @@ -2032,22 +2201,54 @@ yyreduce: } break; - case 64: -#line 318 "a.y" + case 62: + +/* Line 1806 of yacc.c */ +#line 315 "a.y" + { + if((yyvsp[(1) - (3)].gen).type != D_CONST || (yyvsp[(3) - (3)].gen).type != D_CONST) + yyerror("arguments to PCDATA must be integer constants"); + (yyval.gen2).from = (yyvsp[(1) - (3)].gen); + (yyval.gen2).to = (yyvsp[(3) - (3)].gen); + } + break; + + case 63: + +/* Line 1806 of yacc.c */ +#line 324 "a.y" + { + if((yyvsp[(1) - (3)].gen).type != D_CONST) + yyerror("index for FUNCDATA must be integer constant"); + if((yyvsp[(3) - (3)].gen).type != D_EXTERN && (yyvsp[(3) - (3)].gen).type != D_STATIC) + yyerror("value for FUNCDATA must be symbol reference"); + (yyval.gen2).from = (yyvsp[(1) - (3)].gen); + (yyval.gen2).to = (yyvsp[(3) - (3)].gen); + } + break; + + case 68: + +/* Line 1806 of yacc.c */ +#line 341 "a.y" { (yyval.gen) = (yyvsp[(2) - (2)].gen); } break; - case 65: -#line 322 "a.y" + case 69: + +/* Line 1806 of yacc.c */ +#line 345 "a.y" { (yyval.gen) = (yyvsp[(2) - (2)].gen); } break; - case 71: -#line 335 "a.y" + case 75: + +/* Line 1806 of yacc.c */ +#line 358 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_BRANCH; @@ -2055,8 +2256,10 @@ yyreduce: } break; - case 72: -#line 341 "a.y" + case 76: + +/* Line 1806 of yacc.c */ +#line 364 "a.y" { (yyval.gen) = nullgen; if(pass == 2) @@ -2067,8 +2270,10 @@ yyreduce: } break; - case 73: -#line 350 "a.y" + case 77: + +/* Line 1806 of yacc.c */ +#line 373 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_BRANCH; @@ -2077,56 +2282,70 @@ yyreduce: } break; - case 74: -#line 359 "a.y" + case 78: + +/* Line 1806 of yacc.c */ +#line 382 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(1) - (1)].lval); } break; - case 75: -#line 364 "a.y" + case 79: + +/* Line 1806 of yacc.c */ +#line 387 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(1) - (1)].lval); } break; - case 76: -#line 369 "a.y" + case 80: + +/* Line 1806 of yacc.c */ +#line 392 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(1) - (1)].lval); } break; - case 77: -#line 374 "a.y" + case 81: + +/* Line 1806 of yacc.c */ +#line 397 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(1) - (1)].lval); } break; - case 78: -#line 379 "a.y" + case 82: + +/* Line 1806 of yacc.c */ +#line 402 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SP; } break; - case 79: -#line 384 "a.y" + case 83: + +/* Line 1806 of yacc.c */ +#line 407 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(1) - (1)].lval); } break; - case 80: -#line 391 "a.y" + case 84: + +/* Line 1806 of yacc.c */ +#line 414 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_CONST; @@ -2134,8 +2353,10 @@ yyreduce: } break; - case 81: -#line 397 "a.y" + case 85: + +/* Line 1806 of yacc.c */ +#line 420 "a.y" { (yyval.gen) = (yyvsp[(2) - (2)].gen); (yyval.gen).index = (yyvsp[(2) - (2)].gen).type; @@ -2148,8 +2369,10 @@ yyreduce: } break; - case 82: -#line 408 "a.y" + case 86: + +/* Line 1806 of yacc.c */ +#line 431 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SCONST; @@ -2157,8 +2380,10 @@ yyreduce: } break; - case 83: -#line 414 "a.y" + case 87: + +/* Line 1806 of yacc.c */ +#line 437 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2166,8 +2391,10 @@ yyreduce: } break; - case 84: -#line 420 "a.y" + case 88: + +/* Line 1806 of yacc.c */ +#line 443 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2175,8 +2402,10 @@ yyreduce: } break; - case 85: -#line 426 "a.y" + case 89: + +/* Line 1806 of yacc.c */ +#line 449 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2184,8 +2413,10 @@ yyreduce: } break; - case 86: -#line 432 "a.y" + case 90: + +/* Line 1806 of yacc.c */ +#line 455 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2193,8 +2424,10 @@ yyreduce: } break; - case 87: -#line 440 "a.y" + case 91: + +/* Line 1806 of yacc.c */ +#line 463 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_CONST2; @@ -2203,40 +2436,50 @@ yyreduce: } break; - case 88: -#line 449 "a.y" + case 92: + +/* Line 1806 of yacc.c */ +#line 472 "a.y" { (yyval.con2).v1 = (yyvsp[(1) - (1)].lval); - (yyval.con2).v2 = 0; + (yyval.con2).v2 = ArgsSizeUnknown; } break; - case 89: -#line 454 "a.y" + case 93: + +/* Line 1806 of yacc.c */ +#line 477 "a.y" { (yyval.con2).v1 = -(yyvsp[(2) - (2)].lval); - (yyval.con2).v2 = 0; + (yyval.con2).v2 = ArgsSizeUnknown; } break; - case 90: -#line 459 "a.y" + case 94: + +/* Line 1806 of yacc.c */ +#line 482 "a.y" { (yyval.con2).v1 = (yyvsp[(1) - (3)].lval); (yyval.con2).v2 = (yyvsp[(3) - (3)].lval); } break; - case 91: -#line 464 "a.y" + case 95: + +/* Line 1806 of yacc.c */ +#line 487 "a.y" { (yyval.con2).v1 = -(yyvsp[(2) - (4)].lval); (yyval.con2).v2 = (yyvsp[(4) - (4)].lval); } break; - case 94: -#line 475 "a.y" + case 98: + +/* Line 1806 of yacc.c */ +#line 498 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_NONE; @@ -2244,8 +2487,10 @@ yyreduce: } break; - case 95: -#line 481 "a.y" + case 99: + +/* Line 1806 of yacc.c */ +#line 504 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval); @@ -2253,8 +2498,10 @@ yyreduce: } break; - case 96: -#line 487 "a.y" + case 100: + +/* Line 1806 of yacc.c */ +#line 510 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_SP; @@ -2262,8 +2509,10 @@ yyreduce: } break; - case 97: -#line 493 "a.y" + case 101: + +/* Line 1806 of yacc.c */ +#line 516 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_NONE; @@ -2274,8 +2523,10 @@ yyreduce: } break; - case 98: -#line 502 "a.y" + case 102: + +/* Line 1806 of yacc.c */ +#line 525 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(3) - (9)].lval); @@ -2286,8 +2537,10 @@ yyreduce: } break; - case 99: -#line 511 "a.y" + case 103: + +/* Line 1806 of yacc.c */ +#line 534 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(3) - (9)].lval); @@ -2298,24 +2551,30 @@ yyreduce: } break; - case 100: -#line 520 "a.y" + case 104: + +/* Line 1806 of yacc.c */ +#line 543 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(2) - (3)].lval); } break; - case 101: -#line 525 "a.y" + case 105: + +/* Line 1806 of yacc.c */ +#line 548 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_SP; } break; - case 102: -#line 530 "a.y" + case 106: + +/* Line 1806 of yacc.c */ +#line 553 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval); @@ -2323,8 +2582,10 @@ yyreduce: } break; - case 103: -#line 536 "a.y" + case 107: + +/* Line 1806 of yacc.c */ +#line 559 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_NONE; @@ -2334,8 +2595,10 @@ yyreduce: } break; - case 104: -#line 544 "a.y" + case 108: + +/* Line 1806 of yacc.c */ +#line 567 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(2) - (8)].lval); @@ -2345,15 +2608,19 @@ yyreduce: } break; - case 105: -#line 554 "a.y" + case 109: + +/* Line 1806 of yacc.c */ +#line 577 "a.y" { (yyval.gen) = (yyvsp[(1) - (1)].gen); } break; - case 106: -#line 558 "a.y" + case 110: + +/* Line 1806 of yacc.c */ +#line 581 "a.y" { (yyval.gen) = (yyvsp[(1) - (6)].gen); (yyval.gen).index = (yyvsp[(3) - (6)].lval); @@ -2362,8 +2629,10 @@ yyreduce: } break; - case 107: -#line 567 "a.y" + case 111: + +/* Line 1806 of yacc.c */ +#line 590 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(4) - (5)].lval); @@ -2372,8 +2641,10 @@ yyreduce: } break; - case 108: -#line 574 "a.y" + case 112: + +/* Line 1806 of yacc.c */ +#line 597 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_STATIC; @@ -2382,144 +2653,194 @@ yyreduce: } break; - case 109: -#line 582 "a.y" + case 113: + +/* Line 1806 of yacc.c */ +#line 605 "a.y" { (yyval.lval) = 0; } break; - case 110: -#line 586 "a.y" + case 114: + +/* Line 1806 of yacc.c */ +#line 609 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; - case 111: -#line 590 "a.y" + case 115: + +/* Line 1806 of yacc.c */ +#line 613 "a.y" { (yyval.lval) = -(yyvsp[(2) - (2)].lval); } break; - case 113: -#line 597 "a.y" + case 117: + +/* Line 1806 of yacc.c */ +#line 620 "a.y" { (yyval.lval) = D_AUTO; } break; - case 116: -#line 605 "a.y" + case 120: + +/* Line 1806 of yacc.c */ +#line 628 "a.y" { (yyval.lval) = (yyvsp[(1) - (1)].sym)->value; } break; - case 117: -#line 609 "a.y" + case 121: + +/* Line 1806 of yacc.c */ +#line 632 "a.y" { (yyval.lval) = -(yyvsp[(2) - (2)].lval); } break; - case 118: -#line 613 "a.y" + case 122: + +/* Line 1806 of yacc.c */ +#line 636 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; - case 119: -#line 617 "a.y" + case 123: + +/* Line 1806 of yacc.c */ +#line 640 "a.y" { (yyval.lval) = ~(yyvsp[(2) - (2)].lval); } break; - case 120: -#line 621 "a.y" + case 124: + +/* Line 1806 of yacc.c */ +#line 644 "a.y" { (yyval.lval) = (yyvsp[(2) - (3)].lval); } break; - case 122: -#line 628 "a.y" + case 126: + +/* Line 1806 of yacc.c */ +#line 651 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) + (yyvsp[(3) - (3)].lval); } break; - case 123: -#line 632 "a.y" + case 127: + +/* Line 1806 of yacc.c */ +#line 655 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) - (yyvsp[(3) - (3)].lval); } break; - case 124: -#line 636 "a.y" + case 128: + +/* Line 1806 of yacc.c */ +#line 659 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) * (yyvsp[(3) - (3)].lval); } break; - case 125: -#line 640 "a.y" + case 129: + +/* Line 1806 of yacc.c */ +#line 663 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) / (yyvsp[(3) - (3)].lval); } break; - case 126: -#line 644 "a.y" + case 130: + +/* Line 1806 of yacc.c */ +#line 667 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) % (yyvsp[(3) - (3)].lval); } break; - case 127: -#line 648 "a.y" + case 131: + +/* Line 1806 of yacc.c */ +#line 671 "a.y" { (yyval.lval) = (yyvsp[(1) - (4)].lval) << (yyvsp[(4) - (4)].lval); } break; - case 128: -#line 652 "a.y" + case 132: + +/* Line 1806 of yacc.c */ +#line 675 "a.y" { (yyval.lval) = (yyvsp[(1) - (4)].lval) >> (yyvsp[(4) - (4)].lval); } break; - case 129: -#line 656 "a.y" + case 133: + +/* Line 1806 of yacc.c */ +#line 679 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) & (yyvsp[(3) - (3)].lval); } break; - case 130: -#line 660 "a.y" + case 134: + +/* Line 1806 of yacc.c */ +#line 683 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) ^ (yyvsp[(3) - (3)].lval); } break; - case 131: -#line 664 "a.y" + case 135: + +/* Line 1806 of yacc.c */ +#line 687 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) | (yyvsp[(3) - (3)].lval); } break; -/* Line 1267 of yacc.c. */ -#line 2521 "y.tab.c" + +/* Line 1806 of yacc.c */ +#line 2831 "y.tab.c" default: break; } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); @@ -2528,7 +2849,6 @@ yyreduce: *++yyvsp = yyval; - /* Now `shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ @@ -2548,6 +2868,10 @@ yyreduce: | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { @@ -2555,37 +2879,36 @@ yyerrlab: #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) { - YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); - if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) - { - YYSIZE_T yyalloc = 2 * yysize; - if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) - yyalloc = YYSTACK_ALLOC_MAXIMUM; - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); - yymsg = (char *) YYSTACK_ALLOC (yyalloc); - if (yymsg) - yymsg_alloc = yyalloc; - else - { - yymsg = yymsgbuf; - yymsg_alloc = sizeof yymsgbuf; - } - } - - if (0 < yysize && yysize <= yymsg_alloc) - { - (void) yysyntax_error (yymsg, yystate, yychar); - yyerror (yymsg); - } - else - { - yyerror (YY_("syntax error")); - if (yysize != 0) - goto yyexhaustedlab; - } + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; } +# undef YYSYNTAX_ERROR #endif } @@ -2593,7 +2916,7 @@ yyerrlab: if (yyerrstatus == 3) { - /* If just tried and failed to reuse look-ahead token after an + /* If just tried and failed to reuse lookahead token after an error, discard it. */ if (yychar <= YYEOF) @@ -2610,7 +2933,7 @@ yyerrlab: } } - /* Else will try to reuse look-ahead token after shifting the error + /* Else will try to reuse lookahead token after shifting the error token. */ goto yyerrlab1; @@ -2644,7 +2967,7 @@ yyerrlab1: for (;;) { yyn = yypact[yystate]; - if (yyn != YYPACT_NINF) + if (!yypact_value_is_default (yyn)) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) @@ -2667,9 +2990,6 @@ yyerrlab1: YY_STACK_PRINT (yyss, yyssp); } - if (yyn == YYFINAL) - YYACCEPT; - *++yyvsp = yylval; @@ -2694,7 +3014,7 @@ yyabortlab: yyresult = 1; goto yyreturn; -#ifndef yyoverflow +#if !defined(yyoverflow) || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ @@ -2705,9 +3025,14 @@ yyexhaustedlab: #endif yyreturn: - if (yychar != YYEOF && yychar != YYEMPTY) - yydestruct ("Cleanup: discarding lookahead", - yytoken, &yylval); + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + } /* Do not reclaim the symbols of the rule which action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); diff --git a/src/cmd/8a/y.tab.h b/src/cmd/8a/y.tab.h index 4a84b53e4..14637cb33 100644 --- a/src/cmd/8a/y.tab.h +++ b/src/cmd/8a/y.tab.h @@ -1,24 +1,21 @@ -/* A Bison parser, made by GNU Bison 2.3. */ +/* A Bison parser, made by GNU Bison 2.5. */ -/* Skeleton interface for Bison's Yacc-like parsers in C - - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 - Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ + along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work @@ -29,10 +26,11 @@ special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. - + This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ + /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE @@ -55,21 +53,23 @@ LTYPEG = 271, LTYPEXC = 272, LTYPEX = 273, - LCONST = 274, - LFP = 275, - LPC = 276, - LSB = 277, - LBREG = 278, - LLREG = 279, - LSREG = 280, - LFREG = 281, - LXREG = 282, - LFCONST = 283, - LSCONST = 284, - LSP = 285, - LNAME = 286, - LLAB = 287, - LVAR = 288 + LTYPEPC = 274, + LTYPEF = 275, + LCONST = 276, + LFP = 277, + LPC = 278, + LSB = 279, + LBREG = 280, + LLREG = 281, + LSREG = 282, + LFREG = 283, + LXREG = 284, + LFCONST = 285, + LSCONST = 286, + LSP = 287, + LNAME = 288, + LLAB = 289, + LVAR = 290 }; #endif /* Tokens. */ @@ -89,29 +89,34 @@ #define LTYPEG 271 #define LTYPEXC 272 #define LTYPEX 273 -#define LCONST 274 -#define LFP 275 -#define LPC 276 -#define LSB 277 -#define LBREG 278 -#define LLREG 279 -#define LSREG 280 -#define LFREG 281 -#define LXREG 282 -#define LFCONST 283 -#define LSCONST 284 -#define LSP 285 -#define LNAME 286 -#define LLAB 287 -#define LVAR 288 +#define LTYPEPC 274 +#define LTYPEF 275 +#define LCONST 276 +#define LFP 277 +#define LPC 278 +#define LSB 279 +#define LBREG 280 +#define LLREG 281 +#define LSREG 282 +#define LFREG 283 +#define LXREG 284 +#define LFCONST 285 +#define LSCONST 286 +#define LSP 287 +#define LNAME 288 +#define LLAB 289 +#define LVAR 290 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE -#line 37 "a.y" { + +/* Line 2068 of yacc.c */ +#line 38 "a.y" + Sym *sym; int32 lval; struct { @@ -122,14 +127,17 @@ typedef union YYSTYPE char sval[8]; Gen gen; Gen2 gen2; -} -/* Line 1529 of yacc.c. */ -#line 128 "y.tab.h" - YYSTYPE; + + + +/* Line 2068 of yacc.c */ +#line 135 "y.tab.h" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 -# define YYSTYPE_IS_TRIVIAL 1 #endif extern YYSTYPE yylval; + diff --git a/src/cmd/8c/cgen.c b/src/cmd/8c/cgen.c index 78eb7eced..f54102245 100644 --- a/src/cmd/8c/cgen.c +++ b/src/cmd/8c/cgen.c @@ -29,6 +29,7 @@ // THE SOFTWARE. #include "gc.h" +#include "../../pkg/runtime/funcdata.h" /* ,x/^(print|prtree)\(/i/\/\/ */ @@ -404,13 +405,13 @@ cgen(Node *n, Node *nn) } } - if(o == OMUL) { + if(o == OMUL || o == OLMUL) { if(l->addable >= INDEXED) { t = l; l = r; r = t; } - /* should favour AX */ + reg[D_DX]++; // for gopcode case OMUL regalloc(&nod, l, nn); cgen(l, &nod); if(r->addable < INDEXED) { @@ -422,6 +423,7 @@ cgen(Node *n, Node *nn) gopcode(OMUL, n->type, r, &nod); /* addressible */ gmove(&nod, nn); regfree(&nod); + reg[D_DX]--; break; } @@ -936,6 +938,7 @@ cgen(Node *n, Node *nn) return; } gargs(r, &nod, &nod1); + gpcdata(PCDATA_ArgSize, curarg); if(l->addable < INDEXED) { reglcgen(&nod, l, nn); nod.op = OREGISTER; @@ -943,6 +946,7 @@ cgen(Node *n, Node *nn) regfree(&nod); } else gopcode(OFUNC, n->type, Z, l); + gpcdata(PCDATA_ArgSize, -1); if(REGARG >= 0 && reg[REGARG]) reg[REGARG]--; if(nn != Z) { diff --git a/src/cmd/8c/gc.h b/src/cmd/8c/gc.h index bdf981b4c..b668b4c63 100644 --- a/src/cmd/8c/gc.h +++ b/src/cmd/8c/gc.h @@ -298,6 +298,7 @@ void patch(Prog*, int32); int sconst(Node*); void gpseudo(int, Sym*, Node*); void gprefetch(Node*); +void gpcdata(int, int); /* * swt.c diff --git a/src/cmd/8c/peep.c b/src/cmd/8c/peep.c index 9c3e9a5af..da0127d11 100644 --- a/src/cmd/8c/peep.c +++ b/src/cmd/8c/peep.c @@ -403,7 +403,7 @@ copy1(Adr *v1, Adr *v2, Reg *r, int f) } t = copyu(p, v2, A); switch(t) { - case 2: /* rar, cant split */ + case 2: /* rar, can't split */ if(debug['P']) print("; %D rar; return 0\n", v2); return 0; diff --git a/src/cmd/8c/reg.c b/src/cmd/8c/reg.c index 6c87d70a5..a3d5d6115 100644 --- a/src/cmd/8c/reg.c +++ b/src/cmd/8c/reg.c @@ -111,6 +111,7 @@ regopt(Prog *p) case AGLOBL: case ANAME: case ASIGNAME: + case AFUNCDATA: continue; } r = rega(); @@ -584,6 +585,7 @@ brk: case AGLOBL: case ANAME: case ASIGNAME: + case AFUNCDATA: break; } } diff --git a/src/cmd/8c/sgen.c b/src/cmd/8c/sgen.c index b0f2bc544..069bbc1fc 100644 --- a/src/cmd/8c/sgen.c +++ b/src/cmd/8c/sgen.c @@ -34,11 +34,9 @@ Prog* gtext(Sym *s, int32 stkoff) { int32 a; - - a = 0; - if(!(textflag & NOSPLIT)) - a = argsize(); - else if(stkoff >= 128) + + a = argsize(); + if((textflag & NOSPLIT) != 0 && stkoff >= 128) yyerror("stack frame too large for NOSPLIT function"); gpseudo(ATEXT, s, nodconst(stkoff)); diff --git a/src/cmd/8c/swt.c b/src/cmd/8c/swt.c index 1b8ceb0c6..b68197447 100644 --- a/src/cmd/8c/swt.c +++ b/src/cmd/8c/swt.c @@ -315,12 +315,8 @@ outcode(void) goto jackpot; break; } - Bputc(&b, p->as); - Bputc(&b, p->as>>8); - Bputc(&b, p->lineno); - Bputc(&b, p->lineno>>8); - Bputc(&b, p->lineno>>16); - Bputc(&b, p->lineno>>24); + BPUTLE2(&b, p->as); + BPUTLE4(&b, p->lineno); zaddr(&b, &p->from, sf); zaddr(&b, &p->to, st); } @@ -396,13 +392,12 @@ outhist(Biobuf *b) q = 0; } if(n) { - Bputc(b, ANAME); - Bputc(b, ANAME>>8); - Bputc(b, D_FILE); - Bputc(b, 1); - Bputc(b, '<'); + BPUTLE2(b, ANAME); + BPUTC(b, D_FILE); + BPUTC(b, 1); + BPUTC(b, '<'); Bwrite(b, p, n); - Bputc(b, 0); + BPUTC(b, 0); } p = q; if(p == 0 && op) { @@ -416,12 +411,8 @@ outhist(Biobuf *b) if(h->offset) pg.to.type = D_CONST; - Bputc(b, pg.as); - Bputc(b, pg.as>>8); - Bputc(b, pg.lineno); - Bputc(b, pg.lineno>>8); - Bputc(b, pg.lineno>>16); - Bputc(b, pg.lineno>>24); + BPUTLE2(b, pg.as); + BPUTLE4(b, pg.lineno); zaddr(b, &pg.from, 0); zaddr(b, &pg.to, 0); @@ -440,26 +431,21 @@ zname(Biobuf *b, Sym *s, int t) if(debug['T'] && t == D_EXTERN && s->sig != SIGDONE && s->type != types[TENUM] && s != symrathole){ sig = sign(s); - Bputc(b, ASIGNAME); - Bputc(b, ASIGNAME>>8); - Bputc(b, sig); - Bputc(b, sig>>8); - Bputc(b, sig>>16); - Bputc(b, sig>>24); + BPUTLE2(b, ASIGNAME); + BPUTLE4(b, sig); s->sig = SIGDONE; } else{ - Bputc(b, ANAME); /* as */ - Bputc(b, ANAME>>8); /* as */ + BPUTLE2(b, ANAME); /* as */ } - Bputc(b, t); /* type */ - Bputc(b, s->sym); /* sym */ + BPUTC(b, t); /* type */ + BPUTC(b, s->sym); /* sym */ n = s->name; while(*n) { - Bputc(b, *n); + BPUTC(b, *n); n++; } - Bputc(b, 0); + BPUTC(b, 0); } void @@ -493,52 +479,38 @@ zaddr(Biobuf *b, Adr *a, int s) t |= T_OFFSET|T_OFFSET2; break; } - Bputc(b, t); + BPUTC(b, t); if(t & T_INDEX) { /* implies index, scale */ - Bputc(b, a->index); - Bputc(b, a->scale); + BPUTC(b, a->index); + BPUTC(b, a->scale); } if(t & T_OFFSET) { /* implies offset */ l = a->offset; - Bputc(b, l); - Bputc(b, l>>8); - Bputc(b, l>>16); - Bputc(b, l>>24); + BPUTLE4(b, l); } if(t & T_OFFSET2) { /* implies offset2 */ l = a->offset2; - Bputc(b, l); - Bputc(b, l>>8); - Bputc(b, l>>16); - Bputc(b, l>>24); + BPUTLE4(b, l); } if(t & T_SYM) /* implies sym */ - Bputc(b, s); + BPUTC(b, s); if(t & T_FCONST) { ieeedtod(&e, a->dval); - l = e.l; - Bputc(b, l); - Bputc(b, l>>8); - Bputc(b, l>>16); - Bputc(b, l>>24); - l = e.h; - Bputc(b, l); - Bputc(b, l>>8); - Bputc(b, l>>16); - Bputc(b, l>>24); + BPUTLE4(b, e.l); + BPUTLE4(b, e.h); return; } if(t & T_SCONST) { n = a->sval; for(i=0; i<NSNAME; i++) { - Bputc(b, *n); + BPUTC(b, *n); n++; } return; } if(t & T_TYPE) - Bputc(b, a->type); + BPUTC(b, a->type); } int32 diff --git a/src/cmd/8c/txt.c b/src/cmd/8c/txt.c index 1b7617bc5..5c486af38 100644 --- a/src/cmd/8c/txt.c +++ b/src/cmd/8c/txt.c @@ -146,9 +146,7 @@ gclean(void) continue; if(s->type == types[TENUM]) continue; - textflag = s->dataflag; gpseudo(AGLOBL, s, nodconst(s->type->width)); - textflag = 0; } nextpc(); p->as = AEND; @@ -965,7 +963,7 @@ print("botch in doindex\n"); else if(n->left->op == OREGISTER) idx.ptr = n->left->reg; else if(n->left->op != OADDR) { - reg[D_BP]++; // cant be used as a base + reg[D_BP]++; // can't be used as a base regalloc(&nod1, ®node, Z); cgen(n->left, &nod1); idx.ptr = nod1.reg; @@ -1381,8 +1379,16 @@ gpseudo(int a, Sym *s, Node *n) p->as = a; p->from.type = D_EXTERN; p->from.sym = s; - p->from.scale = textflag; - textflag = 0; + + switch(a) { + case ATEXT: + p->from.scale = textflag; + textflag = 0; + break; + case AGLOBL: + p->from.scale = s->dataflag; + break; + } if(s->class == CSTATIC) p->from.type = D_STATIC; @@ -1392,6 +1398,15 @@ gpseudo(int a, Sym *s, Node *n) } void +gpcdata(int index, int value) +{ + Node n1; + + n1 = *nodconst(index); + gins(APCDATA, &n1, nodconst(value)); +} + +void gprefetch(Node *n) { Node n1; diff --git a/src/cmd/8g/cgen.c b/src/cmd/8g/cgen.c index d54db7e62..cc28a3145 100644 --- a/src/cmd/8g/cgen.c +++ b/src/cmd/8g/cgen.c @@ -67,6 +67,8 @@ cgen(Node *n, Node *res) case OSLICE: case OSLICEARR: case OSLICESTR: + case OSLICE3: + case OSLICE3ARR: if (res->op != ONAME || !res->addable) { tempname(&n1, n->type); cgen_slice(n, &n1); @@ -107,6 +109,7 @@ cgen(Node *n, Node *res) // can't do in walk because n->left->addable // changes if n->left is an escaping local variable. switch(n->op) { + case OSPTR: case OLEN: if(isslice(n->left->type) || istype(n->left->type, TSTRING)) n->addable = n->left->addable; @@ -286,6 +289,22 @@ cgen(Node *n, Node *res) regfree(&n1); break; + case OSPTR: + // pointer is the first word of string or slice. + if(isconst(nl, CTSTR)) { + regalloc(&n1, types[tptr], res); + p1 = gins(ALEAL, N, &n1); + datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from); + gmove(&n1, res); + regfree(&n1); + break; + } + igen(nl, &n1, res); + n1.type = n->type; + gmove(&n1, res); + regfree(&n1); + break; + case OLEN: if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) { // map has len in the first 32-bit word. @@ -474,12 +493,13 @@ igenindex(Node *n, Node *res, int bounded) /* * address gen * res = &n; + * The generated code checks that the result is not nil. */ void agen(Node *n, Node *res) { Node *nl, *nr; - Node n1, n2, n3, n4, tmp, nlen; + Node n1, n2, n3, tmp, nlen; Type *t; uint32 w; uint64 v; @@ -547,6 +567,8 @@ agen(Node *n, Node *res) case OSLICE: case OSLICEARR: case OSLICESTR: + case OSLICE3: + case OSLICE3ARR: tempname(&n1, n->type); cgen_slice(n, &n1); agen(&n1, res); @@ -602,16 +624,6 @@ agen(Node *n, Node *res) // len(a) is in nlen (if needed) // w is width - // explicit check for nil if array is large enough - // that we might derive too big a pointer. - if(isfixedarray(nl->type) && nl->type->width >= unmappedzero) { - n4 = n3; - n4.op = OINDREG; - n4.type = types[TUINT8]; - n4.xoffset = 0; - gins(ATESTB, nodintconst(0), &n4); - } - // constant index if(isconst(nr, CTINT)) { if(isconst(nl, CTSTR)) @@ -735,23 +747,11 @@ agen(Node *n, Node *res) case OIND: cgen(nl, res); + cgen_checknil(res); break; case ODOT: agen(nl, res); - // explicit check for nil if struct is large enough - // that we might derive too big a pointer. If the left node - // was ODOT we have already done the nil check. - if(nl->op != ODOT) - if(nl->type->width >= unmappedzero) { - regalloc(&n1, types[tptr], res); - gmove(res, &n1); - n1.op = OINDREG; - n1.type = types[TUINT8]; - n1.xoffset = 0; - gins(ATESTB, nodintconst(0), &n1); - regfree(&n1); - } if(n->xoffset != 0) { nodconst(&n1, types[tptr], n->xoffset); gins(optoas(OADD, types[tptr]), &n1, res); @@ -763,17 +763,7 @@ agen(Node *n, Node *res) if(!isptr[t->etype]) fatal("agen: not ptr %N", n); cgen(nl, res); - // explicit check for nil if struct is large enough - // that we might derive too big a pointer. - if(nl->type->type->width >= unmappedzero) { - regalloc(&n1, types[tptr], res); - gmove(res, &n1); - n1.op = OINDREG; - n1.type = types[TUINT8]; - n1.xoffset = 0; - gins(ATESTB, nodintconst(0), &n1); - regfree(&n1); - } + cgen_checknil(res); if(n->xoffset != 0) { nodconst(&n1, types[tptr], n->xoffset); gins(optoas(OADD, types[tptr]), &n1, res); @@ -789,6 +779,7 @@ agen(Node *n, Node *res) * * on exit, a has been changed to be *newreg. * caller must regfree(a). + * The generated code checks that the result is not *nil. */ void igen(Node *n, Node *a, Node *res) @@ -838,15 +829,7 @@ igen(Node *n, Node *a, Node *res) regalloc(a, types[tptr], res); cgen(n->left, a); } - // explicit check for nil if struct is large enough - // that we might derive too big a pointer. - if(n->left->type->type->width >= unmappedzero) { - n1 = *a; - n1.op = OINDREG; - n1.type = types[TUINT8]; - n1.xoffset = 0; - gins(ATESTB, nodintconst(0), &n1); - } + cgen_checknil(a); a->op = OINDREG; a->xoffset += n->xoffset; a->type = n->type; @@ -874,7 +857,35 @@ igen(Node *n, Node *a, Node *res) a->xoffset = fp->width; a->type = n->type; return; + + case OINDEX: + // Index of fixed-size array by constant can + // put the offset in the addressing. + // Could do the same for slice except that we need + // to use the real index for the bounds checking. + if(isfixedarray(n->left->type) || + (isptr[n->left->type->etype] && isfixedarray(n->left->left->type))) + if(isconst(n->right, CTINT)) { + // Compute &a. + if(!isptr[n->left->type->etype]) + igen(n->left, a, res); + else { + igen(n->left, &n1, res); + cgen_checknil(&n1); + regalloc(a, types[tptr], res); + gmove(&n1, a); + regfree(&n1); + a->op = OINDREG; + } + + // Compute &a[i] as &a + i*width. + a->type = n->type; + a->xoffset += mpgetfix(n->right->val.u.xval)*n->type->width; + return; + } + break; } + // release register for now, to avoid // confusing tempname. if(res != N && res->op == OREGISTER) @@ -1319,7 +1330,7 @@ cadable(Node *n) * copy a composite value by moving its individual components. * Slices, strings and interfaces are supported. * nr is N when assigning a zero value. - * return 1 if can do, 0 if cant. + * return 1 if can do, 0 if can't. */ int componentgen(Node *nr, Node *nl) diff --git a/src/cmd/8g/gg.h b/src/cmd/8g/gg.h index 03c206aa9..55fdded0b 100644 --- a/src/cmd/8g/gg.h +++ b/src/cmd/8g/gg.h @@ -42,7 +42,7 @@ struct Prog Addr from; // src address Addr to; // dst address Prog* link; // next instruction in this func - void* reg; // pointer to containing Reg struct + void* opt; // for optimizer passes }; #define TEXTFLAG from.scale @@ -75,7 +75,6 @@ extern uint32 unmappedzero; * ggen.c */ void compile(Node*); -void proglist(void); void gen(Node*); Node* lookdot(Node*, Node*, int); void cgen_as(Node*, Node*); @@ -123,7 +122,6 @@ void cgen64(Node*, Node*); * gsubr.c */ void clearp(Prog*); -void proglist(void); Prog* gbranch(int, Type*, int); Prog* prog(int); void gconv(int, int); @@ -153,7 +151,7 @@ void split64(Node*, Node*, Node*); void splitclean(void); void nswap(Node*, Node*); void gtrack(Sym*); - +void gargsize(int32); /* * cplx.c */ diff --git a/src/cmd/8g/ggen.c b/src/cmd/8g/ggen.c index b0154bb80..fa5ed00dd 100644 --- a/src/cmd/8g/ggen.c +++ b/src/cmd/8g/ggen.c @@ -9,17 +9,61 @@ #include "gg.h" #include "opt.h" +static Prog* appendp(Prog*, int, int, int32, int, int32); + void -defframe(Prog *ptxt) +defframe(Prog *ptxt, Bvec *bv) { + uint32 frame; + Prog *p; + int i, j; + // fill in argument size ptxt->to.offset2 = rnd(curfn->type->argwid, widthptr); // fill in final stack size if(stksize > maxstksize) maxstksize = stksize; - ptxt->to.offset = rnd(maxstksize+maxarg, widthptr); + frame = rnd(maxstksize+maxarg, widthptr); + ptxt->to.offset = frame; maxstksize = 0; + + // insert code to clear pointered part of the frame, + // so that garbage collector only sees initialized values + // when it looks for pointers. + p = ptxt; + if(stkzerosize >= 8*widthptr) { + p = appendp(p, AMOVL, D_CONST, 0, D_AX, 0); + p = appendp(p, AMOVL, D_CONST, stkzerosize/widthptr, D_CX, 0); + p = appendp(p, ALEAL, D_SP+D_INDIR, frame-stkzerosize, D_DI, 0); + p = appendp(p, AREP, D_NONE, 0, D_NONE, 0); + appendp(p, ASTOSL, D_NONE, 0, D_NONE, 0); + } else { + j = (stkptrsize - stkzerosize)/widthptr * 2; + for(i=0; i<stkzerosize; i+=widthptr) { + if(bvget(bv, j) || bvget(bv, j+1)) + p = appendp(p, AMOVL, D_CONST, 0, D_SP+D_INDIR, frame-stkzerosize+i); + j += 2; + } + } +} + +static Prog* +appendp(Prog *p, int as, int ftype, int32 foffset, int ttype, int32 toffset) +{ + Prog *q; + + q = mal(sizeof(*q)); + clearp(q); + q->as = as; + q->lineno = p->lineno; + q->from.type = ftype; + q->from.offset = foffset; + q->to.type = ttype; + q->to.offset = toffset; + q->link = p->link; + p->link = q; + return q; } // Sweep the prog list to mark any used nodes. @@ -38,7 +82,7 @@ markautoused(Prog* p) } } -// Fixup instructions after compactframe has moved all autos around. +// Fixup instructions after allocauto (formerly compactframe) has moved all autos around. void fixautoused(Prog* p) { @@ -115,9 +159,28 @@ clearfat(Node *nl) void ginscall(Node *f, int proc) { + int32 arg; Prog *p; Node reg, r1, con; + if(f->type != T) + setmaxarg(f->type); + + arg = -1; + // Most functions have a fixed-size argument block, so traceback uses that during unwind. + // Not all, though: there are some variadic functions in package runtime, + // and for those we emit call-specific metadata recorded by caller. + // Reflect generates functions with variable argsize (see reflect.methodValueCall/makeFuncStub), + // so we do this for all indirect calls as well. + if(f->type != T && (f->sym == S || (f->sym != S && f->sym->pkg == runtimepkg) || proc == 1 || proc == 2)) { + arg = f->type->argwid; + if(proc == 1 || proc == 2) + arg += 2*widthptr; + } + + if(arg != -1) + gargsize(arg); + switch(proc) { default: fatal("ginscall: bad proc %d", proc); @@ -126,6 +189,19 @@ ginscall(Node *f, int proc) case 0: // normal call case -1: // normal call but no return if(f->op == ONAME && f->class == PFUNC) { + if(f == deferreturn) { + // Deferred calls will appear to be returning to + // the CALL deferreturn(SB) that we are about to emit. + // However, the stack trace code will show the line + // of the instruction byte before the return PC. + // To avoid that being an unrelated instruction, + // insert an x86 NOP that we will have the right line number. + // x86 NOP 0x90 is really XCHG AX, AX; use that description + // because the NOP pseudo-instruction will be removed by + // the linker. + nodreg(®, types[TINT], D_AX); + gins(AXCHGL, ®, ®); + } p = gins(ACALL, N, f); afunclit(&p->to, f); if(proc == -1 || noreturn(p)) @@ -164,6 +240,9 @@ ginscall(Node *f, int proc) } break; } + + if(arg != -1) + gargsize(-1); } /* @@ -212,6 +291,7 @@ cgen_callinter(Node *n, Node *res, int proc) regalloc(&nodr, types[tptr], &nodo); if(n->left->xoffset == BADWIDTH) fatal("cgen_callinter: badwidth"); + cgen_checknil(&nodo); nodo.op = OINDREG; nodo.xoffset = n->left->xoffset + 3*widthptr + 8; @@ -224,14 +304,11 @@ cgen_callinter(Node *n, Node *res, int proc) gins(ALEAL, &nodo, &nodr); // REG = &(20+offset(REG)) -- i.tab->fun[f] } - // BOTCH nodr.type = fntype; nodr.type = n->left->type; ginscall(&nodr, proc); regfree(&nodr); regfree(&nodo); - - setmaxarg(n->left->type); } /* @@ -259,8 +336,6 @@ cgen_call(Node *n, int proc) genlist(n->list); // assign the args t = n->left->type; - setmaxarg(t); - // call tempname pointer if(n->left->ullman >= UINF) { regalloc(&nod, types[tptr], N); @@ -360,11 +435,18 @@ cgen_aret(Node *n, Node *res) void cgen_ret(Node *n) { + Prog *p; + genlist(n->list); // copy out args - if(retpc) + if(retpc) { gjmp(retpc); - else - gins(ARET, N, N); + return; + } + p = gins(ARET, N, N); + if(n->op == ORETJMP) { + p->to.type = D_EXTERN; + p->to.sym = n->left->sym; + } } /* @@ -1138,3 +1220,51 @@ ret: patch(gbranch(optoas(a, nr->type), T, likely), to); } + +// Called after regopt and peep have run. +// Expand CHECKNIL pseudo-op into actual nil pointer check. +void +expandchecks(Prog *firstp) +{ + Prog *p, *p1, *p2; + + for(p = firstp; p != P; p = p->link) { + if(p->as != ACHECKNIL) + continue; + if(debug_checknil && p->lineno > 1) // p->lineno==1 in generated wrappers + warnl(p->lineno, "generated nil check"); + // check is + // CMP arg, $0 + // JNE 2(PC) (likely) + // MOV AX, 0 + p1 = mal(sizeof *p1); + p2 = mal(sizeof *p2); + clearp(p1); + clearp(p2); + p1->link = p2; + p2->link = p->link; + p->link = p1; + p1->lineno = p->lineno; + p2->lineno = p->lineno; + p1->loc = 9999; + p2->loc = 9999; + p->as = ACMPL; + p->to.type = D_CONST; + p->to.offset = 0; + p1->as = AJNE; + p1->from.type = D_CONST; + p1->from.offset = 1; // likely + p1->to.type = D_BRANCH; + p1->to.u.branch = p2->link; + // crash by write to memory address 0. + // if possible, since we know arg is 0, use 0(arg), + // which will be shorter to encode than plain 0. + p2->as = AMOVL; + p2->from.type = D_AX; + if(regtyp(&p->from)) + p2->to.type = p->from.type + D_INDIR; + else + p2->to.type = D_INDIR+D_NONE; + p2->to.offset = 0; + } +} diff --git a/src/cmd/8g/gobj.c b/src/cmd/8g/gobj.c index 39717d5b1..0517824e0 100644 --- a/src/cmd/8g/gobj.c +++ b/src/cmd/8g/gobj.c @@ -35,10 +35,9 @@ void zname(Biobuf *b, Sym *s, int t) { - Bputc(b, ANAME); /* as */ - Bputc(b, ANAME>>8); /* as */ - Bputc(b, t); /* type */ - Bputc(b, s->sym); /* sym */ + BPUTLE2(b, ANAME); /* as */ + BPUTC(b, t); /* type */ + BPUTC(b, s->sym); /* sym */ Bputname(b, s); } @@ -46,13 +45,12 @@ zname(Biobuf *b, Sym *s, int t) void zfile(Biobuf *b, char *p, int n) { - Bputc(b, ANAME); - Bputc(b, ANAME>>8); - Bputc(b, D_FILE); - Bputc(b, 1); - Bputc(b, '<'); + BPUTLE2(b, ANAME); + BPUTC(b, D_FILE); + BPUTC(b, 1); + BPUTC(b, '<'); Bwrite(b, p, n); - Bputc(b, 0); + BPUTC(b, 0); } void @@ -60,12 +58,8 @@ zhist(Biobuf *b, int line, vlong offset) { Addr a; - Bputc(b, AHISTORY); - Bputc(b, AHISTORY>>8); - Bputc(b, line); - Bputc(b, line>>8); - Bputc(b, line>>16); - Bputc(b, line>>24); + BPUTLE2(b, AHISTORY); + BPUTLE4(b, line); zaddr(b, &zprog.from, 0, 0); a = zprog.to; if(offset != 0) { @@ -114,54 +108,40 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype) t |= T_SCONST; break; } - Bputc(b, t); + BPUTC(b, t); if(t & T_INDEX) { /* implies index, scale */ - Bputc(b, a->index); - Bputc(b, a->scale); + BPUTC(b, a->index); + BPUTC(b, a->scale); } if(t & T_OFFSET) { /* implies offset */ l = a->offset; - Bputc(b, l); - Bputc(b, l>>8); - Bputc(b, l>>16); - Bputc(b, l>>24); + BPUTLE4(b, l); } if(t & T_OFFSET2) { /* implies offset */ l = a->offset2; - Bputc(b, l); - Bputc(b, l>>8); - Bputc(b, l>>16); - Bputc(b, l>>24); + BPUTLE4(b, l); } if(t & T_SYM) /* implies sym */ - Bputc(b, s); + BPUTC(b, s); if(t & T_FCONST) { ieeedtod(&e, a->u.dval); - l = e; - Bputc(b, l); - Bputc(b, l>>8); - Bputc(b, l>>16); - Bputc(b, l>>24); - l = e >> 32; - Bputc(b, l); - Bputc(b, l>>8); - Bputc(b, l>>16); - Bputc(b, l>>24); + BPUTLE4(b, e); + BPUTLE4(b, e >> 32); return; } if(t & T_SCONST) { n = a->u.sval; for(i=0; i<NSNAME; i++) { - Bputc(b, *n); + BPUTC(b, *n); n++; } return; } if(t & T_TYPE) - Bputc(b, a->type); + BPUTC(b, a->type); if(t & T_GOTYPE) - Bputc(b, gotype); + BPUTC(b, gotype); } static struct { @@ -267,12 +247,8 @@ dumpfuncs(void) break; } - Bputc(bout, p->as); - Bputc(bout, p->as>>8); - Bputc(bout, p->lineno); - Bputc(bout, p->lineno>>8); - Bputc(bout, p->lineno>>16); - Bputc(bout, p->lineno>>24); + BPUTLE2(bout, p->as); + BPUTLE4(bout, p->lineno); zaddr(bout, &p->from, sf, gf); zaddr(bout, &p->to, st, gt); } @@ -508,111 +484,6 @@ dsymptr(Sym *s, int off, Sym *x, int xoff) } void -genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface) -{ - Sym *e; - int c, d, o, mov, add, loaded; - Prog *p; - Type *f; - - USED(iface); - - e = method->sym; - for(d=0; d<nelem(dotlist); d++) { - c = adddot1(e, rcvr, d, nil, 0); - if(c == 1) - goto out; - } - fatal("genembedtramp %T.%S", rcvr, method->sym); - -out: - newplist()->name = newname(newnam); - - //TEXT main·S_test2(SB),7,$0 - p = pc; - gins(ATEXT, N, N); - p->from.type = D_EXTERN; - p->from.sym = newnam; - p->to.type = D_CONST; - p->to.offset = 0; - p->from.scale = 7; -//print("1. %P\n", p); - - mov = AMOVL; - add = AADDL; - - loaded = 0; - o = 0; - for(c=d-1; c>=0; c--) { - f = dotlist[c].field; - o += f->width; - if(!isptr[f->type->etype]) - continue; - if(!loaded) { - loaded = 1; - //MOVL 4(SP), AX - p = pc; - gins(mov, N, N); - p->from.type = D_INDIR+D_SP; - p->from.offset = widthptr; - p->to.type = D_AX; -//print("2. %P\n", p); - } - - //MOVL o(AX), AX - p = pc; - gins(mov, N, N); - p->from.type = D_INDIR+D_AX; - p->from.offset = o; - p->to.type = D_AX; -//print("3. %P\n", p); - o = 0; - } - if(o != 0) { - //ADDL $XX, AX - p = pc; - gins(add, N, N); - p->from.type = D_CONST; - p->from.offset = o; - if(loaded) - p->to.type = D_AX; - else { - p->to.type = D_INDIR+D_SP; - p->to.offset = widthptr; - } -//print("4. %P\n", p); - } - - //MOVL AX, 4(SP) - if(loaded) { - p = pc; - gins(mov, N, N); - p->from.type = D_AX; - p->to.type = D_INDIR+D_SP; - p->to.offset = widthptr; -//print("5. %P\n", p); - } else { - // TODO(rsc): obviously this is unnecessary, - // but 6l has a bug, and it can't handle - // JMP instructions too close to the top of - // a new function. - gins(ANOP, N, N); - } - - f = dotlist[0].field; - //JMP main·*Sub_test2(SB) - if(isptr[f->type->etype]) - f = f->type; - p = pc; - gins(AJMP, N, N); - p->to.type = D_EXTERN; - p->to.sym = methodsym(method->sym, ptrto(f->type), 0); -//print("6. %P\n", p); - - pc->as = ARET; // overwrite AEND -} - -void nopout(Prog *p) { p->as = ANOP; diff --git a/src/cmd/8g/gsubr.c b/src/cmd/8g/gsubr.c index 756bdd203..34703ba6e 100644 --- a/src/cmd/8g/gsubr.c +++ b/src/cmd/8g/gsubr.c @@ -31,9 +31,11 @@ #include <u.h> #include <libc.h> #include "gg.h" +#include "../../pkg/runtime/funcdata.h" // TODO(rsc): Can make this bigger if we move // the text segment up higher in 8l for all GOOS. +// At the same time, can raise StackBig in ../../pkg/runtime/stack.h. uint32 unmappedzero = 4096; #define CASE(a,b) (((a)<<16)|((b)<<0)) @@ -208,6 +210,16 @@ ggloblnod(Node *nam) } void +gargsize(int32 size) +{ + Node n1, n2; + + nodconst(&n1, types[TINT32], PCDATA_ArgSize); + nodconst(&n2, types[TINT32], size); + gins(APCDATA, &n1, &n2); +} + +void ggloblsym(Sym *s, int32 width, int dupok, int rodata) { Prog *p; @@ -1145,6 +1157,7 @@ ismem(Node *n) { switch(n->op) { case OITAB: + case OSPTR: case OLEN: case OCAP: case OINDREG: @@ -2158,43 +2171,6 @@ gins(int as, Node *f, Node *t) return p; } -// Generate an instruction referencing *n -// to force segv on nil pointer dereference. -void -checkref(Node *n, int force) -{ - Node m; - - if(!force && isptr[n->type->etype] && n->type->type->width < unmappedzero) - return; - - regalloc(&m, types[TUINTPTR], n); - cgen(n, &m); - m.xoffset = 0; - m.op = OINDREG; - m.type = types[TUINT8]; - gins(ATESTB, nodintconst(0), &m); - regfree(&m); -} - -static void -checkoffset(Addr *a, int canemitcode) -{ - Prog *p; - - if(a->offset < unmappedzero) - return; - if(!canemitcode) - fatal("checkoffset %#x, cannot emit code", a->offset); - - // cannot rely on unmapped nil page at 0 to catch - // reference with large offset. instead, emit explicit - // test of 0(reg). - p = gins(ATESTB, nodintconst(0), N); - p->to = *a; - p->to.offset = 0; -} - /* * generate code to compute n; * make a refer to result. @@ -2344,8 +2320,16 @@ naddr(Node *n, Addr *a, int canemitcode) break; // len(nil) a->etype = tptr; a->width = widthptr; - if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero) - checkoffset(a, canemitcode); + break; + + case OSPTR: + // pointer in a string or slice + naddr(n->left, a, canemitcode); + if(a->type == D_CONST && a->offset == 0) + break; // ptr(nil) + a->etype = simtype[TUINTPTR]; + a->offset += Array_array; + a->width = widthptr; break; case OLEN: @@ -2356,8 +2340,6 @@ naddr(Node *n, Addr *a, int canemitcode) a->etype = TUINT32; a->offset += Array_nel; a->width = 4; - if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero) - checkoffset(a, canemitcode); break; case OCAP: @@ -2368,8 +2350,6 @@ naddr(Node *n, Addr *a, int canemitcode) a->etype = TUINT32; a->offset += Array_cap; a->width = 4; - if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero) - checkoffset(a, canemitcode); break; // case OADD: diff --git a/src/cmd/8g/opt.h b/src/cmd/8g/opt.h index b80043e0f..0d99bdb97 100644 --- a/src/cmd/8g/opt.h +++ b/src/cmd/8g/opt.h @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include "../gc/popt.h" + #define Z N #define Adr Addr @@ -50,9 +52,10 @@ typedef struct Rgn Rgn; // A Reg is a wrapper around a single Prog (one instruction) that holds // register optimization information while the optimizer runs. // r->prog is the instruction. -// r->prog->regp points back to r. +// r->prog->opt points back to r. struct Reg { + Flow f; Bits set; // variables written by this instruction. Bits use1; // variables read by prog->from. @@ -93,8 +96,6 @@ struct Rgn EXTERN int32 exregoffset; // not set EXTERN int32 exfregoffset; // not set -EXTERN Reg* firstr; -EXTERN Reg* lastr; EXTERN Reg zreg; EXTERN Reg* freer; EXTERN Reg** rpo2r; @@ -139,32 +140,80 @@ void paint1(Reg*, int); uint32 paint2(Reg*, int); void paint3(Reg*, int, int32, int); void addreg(Adr*, int); -void dumpone(Reg*); -void dumpit(char*, Reg*); -int noreturn(Prog *p); +void dumpone(Flow*, int); +void dumpit(char*, Flow*, int); /* * peep.c */ -void peep(void); -void excise(Reg*); -Reg* uniqp(Reg*); -Reg* uniqs(Reg*); -int regtyp(Adr*); -int anyvar(Adr*); -int subprop(Reg*); -int copyprop(Reg*); -int copy1(Adr*, Adr*, Reg*, int); +void peep(Prog*); +void excise(Flow*); int copyu(Prog*, Adr*, Adr*); -int copyas(Adr*, Adr*); -int copyau(Adr*, Adr*); -int copysub(Adr*, Adr*, Adr*, int); -int copysub1(Prog*, Adr*, Adr*, int); - int32 RtoB(int); int32 FtoB(int); int BtoR(int32); int BtoF(int32); #pragma varargck type "D" Adr* + +/* + * prog.c + */ +typedef struct ProgInfo ProgInfo; +struct ProgInfo +{ + uint32 flags; // the bits below + uint32 reguse; // required registers used by this instruction + uint32 regset; // required registers set by this instruction + uint32 regindex; // registers used by addressing mode +}; + +enum +{ + // Pseudo-op, like TEXT, GLOBL, TYPE, PCDATA, FUNCDATA. + Pseudo = 1<<1, + + // There's nothing to say about the instruction, + // but it's still okay to see. + OK = 1<<2, + + // Size of right-side write, or right-side read if no write. + SizeB = 1<<3, + SizeW = 1<<4, + SizeL = 1<<5, + SizeQ = 1<<6, + SizeF = 1<<7, // float aka float32 + SizeD = 1<<8, // double aka float64 + + // Left side: address taken, read, write. + LeftAddr = 1<<9, + LeftRead = 1<<10, + LeftWrite = 1<<11, + + // Right side: address taken, read, write. + RightAddr = 1<<12, + RightRead = 1<<13, + RightWrite = 1<<14, + + // Set, use, or kill of carry bit. + // Kill means we never look at the carry bit after this kind of instruction. + SetCarry = 1<<15, + UseCarry = 1<<16, + KillCarry = 1<<17, + + // Instruction kinds + Move = 1<<18, // straight move + Conv = 1<<19, // size conversion + Cjmp = 1<<20, // conditional jump + Break = 1<<21, // breaks control flow (no fallthrough) + Call = 1<<22, // function call + Jump = 1<<23, // jump + Skip = 1<<24, // data instruction + + // Special cases for register use. + ShiftCX = 1<<25, // possible shift by CX + ImulAXDX = 1<<26, // possible multiply into DX:AX +}; + +void proginfo(ProgInfo*, Prog*); diff --git a/src/cmd/8g/peep.c b/src/cmd/8g/peep.c index b8a1eaa08..966c0421b 100644 --- a/src/cmd/8g/peep.c +++ b/src/cmd/8g/peep.c @@ -35,53 +35,47 @@ #define REGEXT 0 -static void conprop(Reg *r); -static void elimshortmov(Reg *r); +static void conprop(Flow *r); +static void elimshortmov(Graph*); +static int subprop(Flow*); +static int copyprop(Graph*, Flow*); +static int copy1(Adr*, Adr*, Flow*, int); +static int copyas(Adr*, Adr*); +static int copyau(Adr*, Adr*); +static int copysub(Adr*, Adr*, Adr*, int); + +static uint32 gactive; // do we need the carry bit static int needc(Prog *p) { + ProgInfo info; + while(p != P) { - switch(p->as) { - case AADCL: - case ASBBL: - case ARCRB: - case ARCRW: - case ARCRL: + proginfo(&info, p); + if(info.flags & UseCarry) return 1; - case AADDB: - case AADDW: - case AADDL: - case ASUBB: - case ASUBW: - case ASUBL: - case AJMP: - case ARET: - case ACALL: + if(info.flags & (SetCarry|KillCarry)) return 0; - default: - if(p->to.type == D_BRANCH) - return 0; - } p = p->link; } return 0; } -static Reg* -rnops(Reg *r) +static Flow* +rnops(Flow *r) { Prog *p; - Reg *r1; + Flow *r1; - if(r != R) + if(r != nil) for(;;) { p = r->prog; if(p->as != ANOP || p->from.type != D_NONE || p->to.type != D_NONE) break; r1 = uniqs(r); - if(r1 == R) + if(r1 == nil) break; r = r1; } @@ -89,57 +83,26 @@ rnops(Reg *r) } void -peep(void) +peep(Prog *firstp) { - Reg *r, *r1, *r2; + Flow *r, *r1; + Graph *g; Prog *p, *p1; int t; - /* - * complete R structure - */ - t = 0; - for(r=firstr; r!=R; r=r1) { - r1 = r->link; - if(r1 == R) - break; - p = r->prog->link; - while(p != r1->prog) - switch(p->as) { - default: - r2 = rega(); - r->link = r2; - r2->link = r1; - - r2->prog = p; - p->reg = r2; - - r2->p1 = r; - r->s1 = r2; - r2->s1 = r1; - r1->p1 = r2; - - r = r2; - t++; - - case ADATA: - case AGLOBL: - case ANAME: - case ASIGNAME: - case ALOCALS: - case ATYPE: - p = p->link; - } - } + g = flowstart(firstp, sizeof(Flow)); + if(g == nil) + return; + gactive = 0; // byte, word arithmetic elimination. - elimshortmov(r); + elimshortmov(g); // constant propagation // find MOV $con,R followed by // another MOV $con,R without // setting R in the interim - for(r=firstr; r!=R; r=r->link) { + for(r=g->start; r!=nil; r=r->link) { p = r->prog; switch(p->as) { case ALEAL: @@ -163,10 +126,10 @@ peep(void) loop1: if(debug['P'] && debug['v']) - dumpit("loop1", firstr); + dumpit("loop1", g->start, 0); t = 0; - for(r=firstr; r!=R; r=r->link) { + for(r=g->start; r!=nil; r=r->link) { p = r->prog; switch(p->as) { case AMOVL: @@ -174,11 +137,11 @@ loop1: case AMOVSD: if(regtyp(&p->to)) if(regtyp(&p->from)) { - if(copyprop(r)) { + if(copyprop(g, r)) { excise(r); t++; } else - if(subprop(r) && copyprop(r)) { + if(subprop(r) && copyprop(g, r)) { excise(r); t++; } @@ -191,7 +154,7 @@ loop1: case AMOVWLSX: if(regtyp(&p->to)) { r1 = rnops(uniqs(r)); - if(r1 != R) { + if(r1 != nil) { p1 = r1->prog; if(p->as == p1->as && p->to.type == p1->from.type){ p1->as = AMOVL; @@ -254,17 +217,19 @@ loop1: // can be replaced by MOVAPD, which moves the pair of float64s // instead of just the lower one. We only use the lower one, but // the processor can do better if we do moves using both. - for(r=firstr; r!=R; r=r->link) { + for(r=g->start; r!=nil; r=r->link) { p = r->prog; if(p->as == AMOVSD) if(regtyp(&p->from)) if(regtyp(&p->to)) p->as = AMOVAPD; } + + flowend(g); } void -excise(Reg *r) +excise(Flow *r) { Prog *p; @@ -279,38 +244,6 @@ excise(Reg *r) ostats.ndelmov++; } -Reg* -uniqp(Reg *r) -{ - Reg *r1; - - r1 = r->p1; - if(r1 == R) { - r1 = r->p2; - if(r1 == R || r1->p2link != R) - return R; - } else - if(r->p2 != R) - return R; - return r1; -} - -Reg* -uniqs(Reg *r) -{ - Reg *r1; - - r1 = r->s1; - if(r1 == R) { - r1 = r->s2; - if(r1 == R) - return R; - } else - if(r->s2 != R) - return R; - return r1; -} - int regtyp(Adr *a) { @@ -332,11 +265,12 @@ regtyp(Adr *a) // can smash the entire 64-bit register without // causing any trouble. static void -elimshortmov(Reg *r) +elimshortmov(Graph *g) { Prog *p; + Flow *r; - for(r=firstr; r!=R; r=r->link) { + for(r=g->start; r!=nil; r=r->link) { p = r->prog; if(regtyp(&p->to)) { switch(p->as) { @@ -431,13 +365,14 @@ elimshortmov(Reg *r) * hopefully, then the former or latter MOV * will be eliminated by copy propagation. */ -int -subprop(Reg *r0) +static int +subprop(Flow *r0) { Prog *p; Adr *v1, *v2; - Reg *r; + Flow *r; int t; + ProgInfo info; p = r0->prog; v1 = &p->from; @@ -446,86 +381,25 @@ subprop(Reg *r0) v2 = &p->to; if(!regtyp(v2)) return 0; - for(r=uniqp(r0); r!=R; r=uniqp(r)) { - if(uniqs(r) == R) + for(r=uniqp(r0); r!=nil; r=uniqp(r)) { + if(debug['P'] && debug['v']) + print("\t? %P\n", r->prog); + if(uniqs(r) == nil) break; p = r->prog; - switch(p->as) { - case ACALL: + proginfo(&info, p); + if(info.flags & Call) return 0; - case AIMULL: - case AIMULW: - if(p->to.type != D_NONE) - break; - - case ARCLB: - case ARCLL: - case ARCLW: - case ARCRB: - case ARCRL: - case ARCRW: - case AROLB: - case AROLL: - case AROLW: - case ARORB: - case ARORL: - case ARORW: - case ASALB: - case ASALL: - case ASALW: - case ASARB: - case ASARL: - case ASARW: - case ASHLB: - case ASHLL: - case ASHLW: - case ASHRB: - case ASHRL: - case ASHRW: - if(p->from.type == D_CONST) - break; - - case ADIVB: - case ADIVL: - case ADIVW: - case AIDIVB: - case AIDIVL: - case AIDIVW: - case AIMULB: - case AMULB: - case AMULL: - case AMULW: - - case AREP: - case AREPN: - - case ACWD: - case ACDQ: - - case ASTOSB: - case ASTOSL: - case AMOVSB: - case AMOVSL: - - case AFMOVF: - case AFMOVD: - case AFMOVFP: - case AFMOVDP: + if(info.reguse | info.regset) return 0; - case AMOVL: - case AMOVSS: - case AMOVSD: - if(p->to.type == v1->type) - goto gotit; - break; - } - if(copyau(&p->from, v2) || - copyau(&p->to, v2)) + if((info.flags & Move) && (info.flags & (SizeL|SizeQ|SizeF|SizeD)) && p->to.type == v1->type) + goto gotit; + + if(copyau(&p->from, v2) || copyau(&p->to, v2)) break; - if(copysub(&p->from, v1, v2, 0) || - copysub(&p->to, v1, v2, 0)) + if(copysub(&p->from, v1, v2, 0) || copysub(&p->to, v1, v2, 0)) break; } return 0; @@ -565,49 +439,48 @@ gotit: * set v1 F=1 * set v2 return success */ -int -copyprop(Reg *r0) +static int +copyprop(Graph *g, Flow *r0) { Prog *p; Adr *v1, *v2; - Reg *r; + USED(g); p = r0->prog; v1 = &p->from; v2 = &p->to; if(copyas(v1, v2)) return 1; - for(r=firstr; r!=R; r=r->link) - r->active = 0; + gactive++; return copy1(v1, v2, r0->s1, 0); } -int -copy1(Adr *v1, Adr *v2, Reg *r, int f) +static int +copy1(Adr *v1, Adr *v2, Flow *r, int f) { int t; Prog *p; - if(r->active) { + if(r->active == gactive) { if(debug['P']) print("act set; return 1\n"); return 1; } - r->active = 1; + r->active = gactive; if(debug['P']) print("copy %D->%D f=%d\n", v1, v2, f); - for(; r != R; r = r->s1) { + for(; r != nil; r = r->s1) { p = r->prog; if(debug['P']) print("%P", p); - if(!f && uniqp(r) == R) { + if(!f && uniqp(r) == nil) { f = 1; if(debug['P']) print("; merge; f=%d", f); } t = copyu(p, v2, A); switch(t) { - case 2: /* rar, cant split */ + case 2: /* rar, can't split */ if(debug['P']) print("; %D rar; return 0\n", v2); return 0; @@ -670,215 +543,10 @@ copy1(Adr *v1, Adr *v2, Reg *r, int f) int copyu(Prog *p, Adr *v, Adr *s) { + ProgInfo info; switch(p->as) { - - default: - if(debug['P']) - print("unknown op %A\n", p->as); - /* SBBL; ADCL; FLD1; SAHF */ - return 2; - - - case ANEGB: - case ANEGW: - case ANEGL: - case ANOTB: - case ANOTW: - case ANOTL: - if(copyas(&p->to, v)) - return 2; - break; - - case ALEAL: /* lhs addr, rhs store */ - if(copyas(&p->from, v)) - return 2; - - - case ANOP: /* rhs store */ - case AMOVL: - case AMOVBLSX: - case AMOVBLZX: - case AMOVWLSX: - case AMOVWLZX: - - case AMOVSS: - case AMOVSD: - case ACVTSD2SL: - case ACVTSD2SS: - case ACVTSL2SD: - case ACVTSL2SS: - case ACVTSS2SD: - case ACVTSS2SL: - case ACVTTSD2SL: - case ACVTTSS2SL: - if(copyas(&p->to, v)) { - if(s != A) - return copysub(&p->from, v, s, 1); - if(copyau(&p->from, v)) - return 4; - return 3; - } - goto caseread; - - case ARCLB: - case ARCLL: - case ARCLW: - case ARCRB: - case ARCRL: - case ARCRW: - case AROLB: - case AROLL: - case AROLW: - case ARORB: - case ARORL: - case ARORW: - case ASALB: - case ASALL: - case ASALW: - case ASARB: - case ASARL: - case ASARW: - case ASHLB: - case ASHLL: - case ASHLW: - case ASHRB: - case ASHRL: - case ASHRW: - if(copyas(&p->to, v)) - return 2; - if(copyas(&p->from, v)) - if(p->from.type == D_CX) - return 2; - goto caseread; - - case AADDB: /* rhs rar */ - case AADDL: - case AADDW: - case AANDB: - case AANDL: - case AANDW: - case ADECL: - case ADECW: - case AINCL: - case AINCW: - case ASUBB: - case ASUBL: - case ASUBW: - case AORB: - case AORL: - case AORW: - case AXORB: - case AXORL: - case AXORW: - case AMOVB: - case AMOVW: - - case AADDSD: - case AADDSS: - case ACMPSD: - case ACMPSS: - case ADIVSD: - case ADIVSS: - case AMAXSD: - case AMAXSS: - case AMINSD: - case AMINSS: - case AMULSD: - case AMULSS: - case ARCPSS: - case ARSQRTSS: - case ASQRTSD: - case ASQRTSS: - case ASUBSD: - case ASUBSS: - case AXORPD: - if(copyas(&p->to, v)) - return 2; - goto caseread; - - case ACMPL: /* read only */ - case ACMPW: - case ACMPB: - - case ACOMISD: - case ACOMISS: - case AUCOMISD: - case AUCOMISS: - caseread: - if(s != A) { - if(copysub(&p->from, v, s, 1)) - return 1; - return copysub(&p->to, v, s, 1); - } - if(copyau(&p->from, v)) - return 1; - if(copyau(&p->to, v)) - return 1; - break; - - case AJGE: /* no reference */ - case AJNE: - case AJLE: - case AJEQ: - case AJHI: - case AJLS: - case AJMI: - case AJPL: - case AJGT: - case AJLT: - case AJCC: - case AJCS: - - case AADJSP: - case AWAIT: - case ACLD: - break; - - case AIMULL: - case AIMULW: - if(p->to.type != D_NONE) { - if(copyas(&p->to, v)) - return 2; - goto caseread; - } - - case ADIVB: - case ADIVL: - case ADIVW: - case AIDIVB: - case AIDIVL: - case AIDIVW: - case AIMULB: - case AMULB: - case AMULL: - case AMULW: - - case ACWD: - case ACDQ: - if(v->type == D_AX || v->type == D_DX) - return 2; - goto caseread; - - case AREP: - case AREPN: - if(v->type == D_CX) - return 2; - goto caseread; - - case AMOVSB: - case AMOVSL: - if(v->type == D_DI || v->type == D_SI) - return 2; - goto caseread; - - case ASTOSB: - case ASTOSL: - if(v->type == D_AX || v->type == D_DI) - return 2; - goto caseread; - - case AJMP: /* funny */ + case AJMP: if(s != A) { if(copysub(&p->to, v, s, 1)) return 1; @@ -888,12 +556,12 @@ copyu(Prog *p, Adr *v, Adr *s) return 1; return 0; - case ARET: /* funny */ + case ARET: if(s != A) return 1; return 3; - case ACALL: /* funny */ + case ACALL: if(REGEXT && v->type <= REGEXT && v->type > exregoffset) return 2; if(REGARG >= 0 && v->type == (uchar)REGARG) @@ -910,11 +578,47 @@ copyu(Prog *p, Adr *v, Adr *s) return 4; return 3; - case ATEXT: /* funny */ + case ATEXT: if(REGARG >= 0 && v->type == (uchar)REGARG) return 3; return 0; } + + proginfo(&info, p); + + if((info.reguse|info.regset) & RtoB(v->type)) + return 2; + + if(info.flags & LeftAddr) + if(copyas(&p->from, v)) + return 2; + + if((info.flags & (RightRead|RightWrite)) == (RightRead|RightWrite)) + if(copyas(&p->to, v)) + return 2; + + if(info.flags & RightWrite) { + if(copyas(&p->to, v)) { + if(s != A) + return copysub(&p->from, v, s, 1); + if(copyau(&p->from, v)) + return 4; + return 3; + } + } + + if(info.flags & (LeftAddr|LeftRead|LeftWrite|RightAddr|RightRead|RightWrite)) { + if(s != A) { + if(copysub(&p->from, v, s, 1)) + return 1; + return copysub(&p->to, v, s, 1); + } + if(copyau(&p->from, v)) + return 1; + if(copyau(&p->to, v)) + return 1; + } + return 0; } @@ -923,7 +627,7 @@ copyu(Prog *p, Adr *v, Adr *s) * could be set/use depending on * semantics */ -int +static int copyas(Adr *a, Adr *v) { if(a->type != v->type) @@ -936,10 +640,23 @@ copyas(Adr *a, Adr *v) return 0; } +int +sameaddr(Addr *a, Addr *v) +{ + if(a->type != v->type) + return 0; + if(regtyp(v)) + return 1; + if(v->type == D_AUTO || v->type == D_PARAM) + if(v->offset == a->offset) + return 1; + return 0; +} + /* * either direct or indirect */ -int +static int copyau(Adr *a, Adr *v) { @@ -958,7 +675,7 @@ copyau(Adr *a, Adr *v) * substitute s for v in a * return failure to substitute */ -int +static int copysub(Adr *a, Adr *v, Adr *s, int f) { int t; @@ -991,9 +708,9 @@ copysub(Adr *a, Adr *v, Adr *s, int f) } static void -conprop(Reg *r0) +conprop(Flow *r0) { - Reg *r; + Flow *r; Prog *p, *p0; int t; Adr *v0; @@ -1004,9 +721,9 @@ conprop(Reg *r0) loop: r = uniqs(r); - if(r == R || r == r0) + if(r == nil || r == r0) return; - if(uniqp(r) == R) + if(uniqp(r) == nil) return; p = r->prog; @@ -1034,3 +751,18 @@ loop: break; } } + +int +smallindir(Addr *a, Addr *reg) +{ + return regtyp(reg) && + a->type == D_INDIR + reg->type && + a->index == D_NONE && + 0 <= a->offset && a->offset < 4096; +} + +int +stackaddr(Addr *a) +{ + return regtyp(a) && a->type == D_SP; +} diff --git a/src/cmd/8g/prog.c b/src/cmd/8g/prog.c new file mode 100644 index 000000000..14f197b6a --- /dev/null +++ b/src/cmd/8g/prog.c @@ -0,0 +1,340 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include <u.h> +#include <libc.h> +#include "gg.h" +#include "opt.h" + +// Matches real RtoB but can be used in global initializer. +#define RtoB(r) (1<<((r)-D_AX)) + +enum { + AX = RtoB(D_AX), + BX = RtoB(D_BX), + CX = RtoB(D_CX), + DX = RtoB(D_DX), + DI = RtoB(D_DI), + SI = RtoB(D_SI), + + LeftRdwr = LeftRead | LeftWrite, + RightRdwr = RightRead | RightWrite, +}; + +#undef RtoB + +// This table gives the basic information about instruction +// generated by the compiler and processed in the optimizer. +// See opt.h for bit definitions. +// +// Instructions not generated need not be listed. +// As an exception to that rule, we typically write down all the +// size variants of an operation even if we just use a subset. +// +// The table is formatted for 8-space tabs. +static ProgInfo progtable[ALAST] = { + [ATYPE]= {Pseudo | Skip}, + [ATEXT]= {Pseudo}, + [AFUNCDATA]= {Pseudo}, + [APCDATA]= {Pseudo}, + [AUNDEF]= {OK}, + [AUSEFIELD]= {OK}, + [ACHECKNIL]= {LeftRead}, + + // NOP is an internal no-op that also stands + // for USED and SET annotations, not the Intel opcode. + [ANOP]= {LeftRead | RightWrite}, + + [AADCL]= {SizeL | LeftRead | RightRdwr | SetCarry | UseCarry}, + [AADCW]= {SizeW | LeftRead | RightRdwr | SetCarry | UseCarry}, + + [AADDB]= {SizeB | LeftRead | RightRdwr | SetCarry}, + [AADDL]= {SizeL | LeftRead | RightRdwr | SetCarry}, + [AADDW]= {SizeW | LeftRead | RightRdwr | SetCarry}, + + [AADDSD]= {SizeD | LeftRead | RightRdwr}, + [AADDSS]= {SizeF | LeftRead | RightRdwr}, + + [AANDB]= {SizeB | LeftRead | RightRdwr | SetCarry}, + [AANDL]= {SizeL | LeftRead | RightRdwr | SetCarry}, + [AANDW]= {SizeW | LeftRead | RightRdwr | SetCarry}, + + [ACALL]= {RightAddr | Call | KillCarry}, + + [ACDQ]= {OK, AX, AX | DX}, + [ACWD]= {OK, AX, AX | DX}, + + [ACLD]= {OK}, + [ASTD]= {OK}, + + [ACMPB]= {SizeB | LeftRead | RightRead | SetCarry}, + [ACMPL]= {SizeL | LeftRead | RightRead | SetCarry}, + [ACMPW]= {SizeW | LeftRead | RightRead | SetCarry}, + + [ACOMISD]= {SizeD | LeftRead | RightRead | SetCarry}, + [ACOMISS]= {SizeF | LeftRead | RightRead | SetCarry}, + + [ACVTSD2SL]= {SizeL | LeftRead | RightWrite | Conv}, + [ACVTSD2SS]= {SizeF | LeftRead | RightWrite | Conv}, + [ACVTSL2SD]= {SizeD | LeftRead | RightWrite | Conv}, + [ACVTSL2SS]= {SizeF | LeftRead | RightWrite | Conv}, + [ACVTSS2SD]= {SizeD | LeftRead | RightWrite | Conv}, + [ACVTSS2SL]= {SizeL | LeftRead | RightWrite | Conv}, + [ACVTTSD2SL]= {SizeL | LeftRead | RightWrite | Conv}, + [ACVTTSS2SL]= {SizeL | LeftRead | RightWrite | Conv}, + + [ADECB]= {SizeB | RightRdwr}, + [ADECL]= {SizeL | RightRdwr}, + [ADECW]= {SizeW | RightRdwr}, + + [ADIVB]= {SizeB | LeftRead | SetCarry, AX, AX}, + [ADIVL]= {SizeL | LeftRead | SetCarry, AX|DX, AX|DX}, + [ADIVW]= {SizeW | LeftRead | SetCarry, AX|DX, AX|DX}, + + [ADIVSD]= {SizeD | LeftRead | RightRdwr}, + [ADIVSS]= {SizeF | LeftRead | RightRdwr}, + + [AFLDCW]= {SizeW | LeftAddr}, + [AFSTCW]= {SizeW | RightAddr}, + + [AFSTSW]= {SizeW | RightAddr | RightWrite}, + + [AFADDD]= {SizeD | LeftAddr | RightRdwr}, + [AFADDDP]= {SizeD | LeftAddr | RightRdwr}, + [AFADDF]= {SizeF | LeftAddr | RightRdwr}, + + [AFCOMD]= {SizeD | LeftAddr | RightRead}, + [AFCOMDP]= {SizeD | LeftAddr | RightRead}, + [AFCOMDPP]= {SizeD | LeftAddr | RightRead}, + [AFCOMF]= {SizeF | LeftAddr | RightRead}, + [AFCOMFP]= {SizeF | LeftAddr | RightRead}, + [AFUCOMIP]= {SizeF | LeftAddr | RightRead}, + + [AFCHS]= {SizeD | RightRdwr}, // also SizeF + + [AFDIVDP]= {SizeD | LeftAddr | RightRdwr}, + [AFDIVF]= {SizeF | LeftAddr | RightRdwr}, + [AFDIVD]= {SizeD | LeftAddr | RightRdwr}, + + [AFDIVRDP]= {SizeD | LeftAddr | RightRdwr}, + [AFDIVRF]= {SizeF | LeftAddr | RightRdwr}, + [AFDIVRD]= {SizeD | LeftAddr | RightRdwr}, + + [AFXCHD]= {SizeD | LeftRdwr | RightRdwr}, + + [AFSUBD]= {SizeD | LeftAddr | RightRdwr}, + [AFSUBDP]= {SizeD | LeftAddr | RightRdwr}, + [AFSUBF]= {SizeF | LeftAddr | RightRdwr}, + [AFSUBRD]= {SizeD | LeftAddr | RightRdwr}, + [AFSUBRDP]= {SizeD | LeftAddr | RightRdwr}, + [AFSUBRF]= {SizeF | LeftAddr | RightRdwr}, + + [AFMOVD]= {SizeD | LeftAddr | RightWrite}, + [AFMOVF]= {SizeF | LeftAddr | RightWrite}, + [AFMOVL]= {SizeL | LeftAddr | RightWrite}, + [AFMOVW]= {SizeW | LeftAddr | RightWrite}, + [AFMOVV]= {SizeQ | LeftAddr | RightWrite}, + + [AFMOVDP]= {SizeD | LeftRead | RightAddr}, + [AFMOVFP]= {SizeF | LeftRead | RightAddr}, + [AFMOVLP]= {SizeL | LeftRead | RightAddr}, + [AFMOVWP]= {SizeW | LeftRead | RightAddr}, + [AFMOVVP]= {SizeQ | LeftRead | RightAddr}, + + [AFMULD]= {SizeD | LeftAddr | RightRdwr}, + [AFMULDP]= {SizeD | LeftAddr | RightRdwr}, + [AFMULF]= {SizeF | LeftAddr | RightRdwr}, + + [AIDIVB]= {SizeB | LeftRead | SetCarry, AX, AX}, + [AIDIVL]= {SizeL | LeftRead | SetCarry, AX|DX, AX|DX}, + [AIDIVW]= {SizeW | LeftRead | SetCarry, AX|DX, AX|DX}, + + [AIMULB]= {SizeB | LeftRead | SetCarry, AX, AX}, + [AIMULL]= {SizeL | LeftRead | ImulAXDX | SetCarry}, + [AIMULW]= {SizeW | LeftRead | ImulAXDX | SetCarry}, + + [AINCB]= {SizeB | RightRdwr}, + [AINCL]= {SizeL | RightRdwr}, + [AINCW]= {SizeW | RightRdwr}, + + [AJCC]= {Cjmp | UseCarry}, + [AJCS]= {Cjmp | UseCarry}, + [AJEQ]= {Cjmp | UseCarry}, + [AJGE]= {Cjmp | UseCarry}, + [AJGT]= {Cjmp | UseCarry}, + [AJHI]= {Cjmp | UseCarry}, + [AJLE]= {Cjmp | UseCarry}, + [AJLS]= {Cjmp | UseCarry}, + [AJLT]= {Cjmp | UseCarry}, + [AJMI]= {Cjmp | UseCarry}, + [AJNE]= {Cjmp | UseCarry}, + [AJOC]= {Cjmp | UseCarry}, + [AJOS]= {Cjmp | UseCarry}, + [AJPC]= {Cjmp | UseCarry}, + [AJPL]= {Cjmp | UseCarry}, + [AJPS]= {Cjmp | UseCarry}, + + [AJMP]= {Jump | Break | KillCarry}, + + [ALEAL]= {LeftAddr | RightWrite}, + + [AMOVBLSX]= {SizeL | LeftRead | RightWrite | Conv}, + [AMOVBLZX]= {SizeL | LeftRead | RightWrite | Conv}, + [AMOVBWSX]= {SizeW | LeftRead | RightWrite | Conv}, + [AMOVBWZX]= {SizeW | LeftRead | RightWrite | Conv}, + [AMOVWLSX]= {SizeL | LeftRead | RightWrite | Conv}, + [AMOVWLZX]= {SizeL | LeftRead | RightWrite | Conv}, + + [AMOVB]= {SizeB | LeftRead | RightWrite | Move}, + [AMOVL]= {SizeL | LeftRead | RightWrite | Move}, + [AMOVW]= {SizeW | LeftRead | RightWrite | Move}, + + [AMOVSB]= {OK, DI|SI, DI|SI}, + [AMOVSL]= {OK, DI|SI, DI|SI}, + [AMOVSW]= {OK, DI|SI, DI|SI}, + + [AMOVSD]= {SizeD | LeftRead | RightWrite | Move}, + [AMOVSS]= {SizeF | LeftRead | RightWrite | Move}, + + // We use MOVAPD as a faster synonym for MOVSD. + [AMOVAPD]= {SizeD | LeftRead | RightWrite | Move}, + + [AMULB]= {SizeB | LeftRead | SetCarry, AX, AX}, + [AMULL]= {SizeL | LeftRead | SetCarry, AX, AX|DX}, + [AMULW]= {SizeW | LeftRead | SetCarry, AX, AX|DX}, + + [AMULSD]= {SizeD | LeftRead | RightRdwr}, + [AMULSS]= {SizeF | LeftRead | RightRdwr}, + + [ANEGB]= {SizeB | RightRdwr | SetCarry}, + [ANEGL]= {SizeL | RightRdwr | SetCarry}, + [ANEGW]= {SizeW | RightRdwr | SetCarry}, + + [ANOTB]= {SizeB | RightRdwr}, + [ANOTL]= {SizeL | RightRdwr}, + [ANOTW]= {SizeW | RightRdwr}, + + [AORB]= {SizeB | LeftRead | RightRdwr | SetCarry}, + [AORL]= {SizeL | LeftRead | RightRdwr | SetCarry}, + [AORW]= {SizeW | LeftRead | RightRdwr | SetCarry}, + + [APOPL]= {SizeL | RightWrite}, + [APUSHL]= {SizeL | LeftRead}, + + [ARCLB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry}, + [ARCLL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry}, + [ARCLW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry}, + + [ARCRB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry}, + [ARCRL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry}, + [ARCRW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry}, + + [AREP]= {OK, CX, CX}, + [AREPN]= {OK, CX, CX}, + + [ARET]= {Break | KillCarry}, + + [AROLB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [AROLL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [AROLW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry}, + + [ARORB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ARORL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ARORW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry}, + + [ASAHF]= {OK, AX, AX}, + + [ASALB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ASALL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ASALW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry}, + + [ASARB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ASARL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ASARW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry}, + + [ASBBB]= {SizeB | LeftRead | RightRdwr | SetCarry | UseCarry}, + [ASBBL]= {SizeL | LeftRead | RightRdwr | SetCarry | UseCarry}, + [ASBBW]= {SizeW | LeftRead | RightRdwr | SetCarry | UseCarry}, + + [ASETCC]= {SizeB | RightRdwr | UseCarry}, + [ASETCS]= {SizeB | RightRdwr | UseCarry}, + [ASETEQ]= {SizeB | RightRdwr | UseCarry}, + [ASETGE]= {SizeB | RightRdwr | UseCarry}, + [ASETGT]= {SizeB | RightRdwr | UseCarry}, + [ASETHI]= {SizeB | RightRdwr | UseCarry}, + [ASETLE]= {SizeB | RightRdwr | UseCarry}, + [ASETLS]= {SizeB | RightRdwr | UseCarry}, + [ASETLT]= {SizeB | RightRdwr | UseCarry}, + [ASETMI]= {SizeB | RightRdwr | UseCarry}, + [ASETNE]= {SizeB | RightRdwr | UseCarry}, + [ASETOC]= {SizeB | RightRdwr | UseCarry}, + [ASETOS]= {SizeB | RightRdwr | UseCarry}, + [ASETPC]= {SizeB | RightRdwr | UseCarry}, + [ASETPL]= {SizeB | RightRdwr | UseCarry}, + [ASETPS]= {SizeB | RightRdwr | UseCarry}, + + [ASHLB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ASHLL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ASHLW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry}, + + [ASHRB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ASHRL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry}, + [ASHRW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry}, + + [ASTOSB]= {OK, AX|DI, DI}, + [ASTOSL]= {OK, AX|DI, DI}, + [ASTOSW]= {OK, AX|DI, DI}, + + [ASUBB]= {SizeB | LeftRead | RightRdwr | SetCarry}, + [ASUBL]= {SizeL | LeftRead | RightRdwr | SetCarry}, + [ASUBW]= {SizeW | LeftRead | RightRdwr | SetCarry}, + + [ASUBSD]= {SizeD | LeftRead | RightRdwr}, + [ASUBSS]= {SizeF | LeftRead | RightRdwr}, + + [ATESTB]= {SizeB | LeftRead | RightRead | SetCarry}, + [ATESTL]= {SizeL | LeftRead | RightRead | SetCarry}, + [ATESTW]= {SizeW | LeftRead | RightRead | SetCarry}, + + [AUCOMISD]= {SizeD | LeftRead | RightRead}, + [AUCOMISS]= {SizeF | LeftRead | RightRead}, + + [AXCHGB]= {SizeB | LeftRdwr | RightRdwr}, + [AXCHGL]= {SizeL | LeftRdwr | RightRdwr}, + [AXCHGW]= {SizeW | LeftRdwr | RightRdwr}, + + [AXORB]= {SizeB | LeftRead | RightRdwr | SetCarry}, + [AXORL]= {SizeL | LeftRead | RightRdwr | SetCarry}, + [AXORW]= {SizeW | LeftRead | RightRdwr | SetCarry}, +}; + +void +proginfo(ProgInfo *info, Prog *p) +{ + *info = progtable[p->as]; + if(info->flags == 0) + fatal("unknown instruction %P", p); + + if((info->flags & ShiftCX) && p->from.type != D_CONST) + info->reguse |= CX; + + if(info->flags & ImulAXDX) { + if(p->to.type == D_NONE) { + info->reguse |= AX; + info->regset |= AX | DX; + } else { + info->flags |= RightRdwr; + } + } + + // Addressing makes some registers used. + if(p->from.type >= D_INDIR) + info->regindex |= RtoB(p->from.type-D_INDIR); + if(p->from.index != D_NONE) + info->regindex |= RtoB(p->from.index); + if(p->to.type >= D_INDIR) + info->regindex |= RtoB(p->to.type-D_INDIR); + if(p->to.index != D_NONE) + info->regindex |= RtoB(p->to.index); +} diff --git a/src/cmd/8g/reg.c b/src/cmd/8g/reg.c index 985f6ccbc..a85c6608a 100644 --- a/src/cmd/8g/reg.c +++ b/src/cmd/8g/reg.c @@ -35,28 +35,10 @@ #define NREGVAR 16 /* 8 integer + 8 floating */ #define REGBITS ((uint32)0xffff) -#define P2R(p) (Reg*)(p->reg) +static Reg* firstr; static int first = 1; -static void fixjmp(Prog*); -static void fixtemp(Prog*); - -Reg* -rega(void) -{ - Reg *r; - - r = freer; - if(r == R) { - r = mal(sizeof(*r)); - } else - freer = r->link; - - *r = zreg; - return r; -} - int rcmp(const void *a1, const void *a2) { @@ -131,7 +113,9 @@ regopt(Prog *firstp) { Reg *r, *r1; Prog *p; - int i, z, nr; + Graph *g; + ProgInfo info; + int i, z; uint32 vreg; Bits bit; @@ -141,22 +125,9 @@ regopt(Prog *firstp) first = 0; } - fixtemp(firstp); fixjmp(firstp); + mergetemp(firstp); - // count instructions - nr = 0; - for(p=firstp; p!=P; p=p->link) - nr++; - // if too big dont bother - if(nr >= 10000) { -// print("********** %S is too big (%d)\n", curfn->nname->sym, nr); - return; - } - - firstr = R; - lastr = R; - /* * control flow is more complicated in generated go code * than in generated c code. define pseudo-variables for @@ -188,342 +159,44 @@ regopt(Prog *firstp) * allocate pcs * find use and set of variables */ - nr = 0; - for(p=firstp; p!=P; p=p->link) { - switch(p->as) { - case ADATA: - case AGLOBL: - case ANAME: - case ASIGNAME: - case ALOCALS: - case ATYPE: - continue; - } - r = rega(); - nr++; - if(firstr == R) { - firstr = r; - lastr = r; - } else { - lastr->link = r; - r->p1 = lastr; - lastr->s1 = r; - lastr = r; - } - r->prog = p; - p->reg = r; - - r1 = r->p1; - if(r1 != R) { - switch(r1->prog->as) { - case ARET: - case AJMP: - case AIRETL: - r->p1 = R; - r1->s1 = R; - } - } + g = flowstart(firstp, sizeof(Reg)); + if(g == nil) + return; + firstr = (Reg*)g->start; + + for(r = firstr; r != R; r = (Reg*)r->f.link) { + p = r->f.prog; + proginfo(&info, p); // Avoid making variables for direct-called functions. if(p->as == ACALL && p->to.type == D_EXTERN) continue; - // Addressing makes some registers used. - if(p->from.type >= D_INDIR) - r->use1.b[0] |= RtoB(p->from.type-D_INDIR); - if(p->from.index != D_NONE) - r->use1.b[0] |= RtoB(p->from.index); - if(p->to.type >= D_INDIR) - r->use2.b[0] |= RtoB(p->to.type-D_INDIR); - if(p->to.index != D_NONE) - r->use2.b[0] |= RtoB(p->to.index); + r->use1.b[0] |= info.reguse | info.regindex; + r->set.b[0] |= info.regset; bit = mkvar(r, &p->from); - if(bany(&bit)) - switch(p->as) { - /* - * funny - */ - case ALEAL: - case AFMOVD: - case AFMOVF: - case AFMOVL: - case AFMOVW: - case AFMOVV: - setaddrs(bit); - break; - - /* - * left side read - */ - default: - for(z=0; z<BITS; z++) - r->use1.b[z] |= bit.b[z]; - break; - - /* - * left side read+write - */ - case AXCHGB: - case AXCHGW: - case AXCHGL: - for(z=0; z<BITS; z++) { - r->use1.b[z] |= bit.b[z]; - r->set.b[z] |= bit.b[z]; - } - break; + if(bany(&bit)) { + if(info.flags & LeftAddr) + setaddrs(bit); + if(info.flags & LeftRead) + for(z=0; z<BITS; z++) + r->use1.b[z] |= bit.b[z]; + if(info.flags & LeftWrite) + for(z=0; z<BITS; z++) + r->set.b[z] |= bit.b[z]; } bit = mkvar(r, &p->to); - if(bany(&bit)) - switch(p->as) { - default: - yyerror("reg: unknown op: %A", p->as); - break; - - /* - * right side read - */ - case ACMPB: - case ACMPL: - case ACMPW: - case ACOMISS: - case ACOMISD: - case AUCOMISS: - case AUCOMISD: - case ATESTB: - case ATESTL: - case ATESTW: - for(z=0; z<BITS; z++) - r->use2.b[z] |= bit.b[z]; - break; - - /* - * right side write - */ - case AFSTSW: - case ALEAL: - case ANOP: - case AMOVL: - case AMOVB: - case AMOVW: - case AMOVBLSX: - case AMOVBLZX: - case AMOVBWSX: - case AMOVBWZX: - case AMOVWLSX: - case AMOVWLZX: - case APOPL: - - case AMOVSS: - case AMOVSD: - case ACVTSD2SL: - case ACVTSD2SS: - case ACVTSL2SD: - case ACVTSL2SS: - case ACVTSS2SD: - case ACVTSS2SL: - case ACVTTSD2SL: - case ACVTTSS2SL: - for(z=0; z<BITS; z++) - r->set.b[z] |= bit.b[z]; - break; - - /* - * right side read+write - */ - case AINCB: - case AINCL: - case AINCW: - case ADECB: - case ADECL: - case ADECW: - - case AADDB: - case AADDL: - case AADDW: - case AANDB: - case AANDL: - case AANDW: - case ASUBB: - case ASUBL: - case ASUBW: - case AORB: - case AORL: - case AORW: - case AXORB: - case AXORL: - case AXORW: - case ASALB: - case ASALL: - case ASALW: - case ASARB: - case ASARL: - case ASARW: - case ARCLB: - case ARCLL: - case ARCLW: - case ARCRB: - case ARCRL: - case ARCRW: - case AROLB: - case AROLL: - case AROLW: - case ARORB: - case ARORL: - case ARORW: - case ASHLB: - case ASHLL: - case ASHLW: - case ASHRB: - case ASHRL: - case ASHRW: - case AIMULL: - case AIMULW: - case ANEGB: - case ANEGL: - case ANEGW: - case ANOTB: - case ANOTL: - case ANOTW: - case AADCL: - case ASBBL: - - case ASETCC: - case ASETCS: - case ASETEQ: - case ASETGE: - case ASETGT: - case ASETHI: - case ASETLE: - case ASETLS: - case ASETLT: - case ASETMI: - case ASETNE: - case ASETOC: - case ASETOS: - case ASETPC: - case ASETPL: - case ASETPS: - - case AXCHGB: - case AXCHGW: - case AXCHGL: - - case AADDSD: - case AADDSS: - case ACMPSD: - case ACMPSS: - case ADIVSD: - case ADIVSS: - case AMAXSD: - case AMAXSS: - case AMINSD: - case AMINSS: - case AMULSD: - case AMULSS: - case ARCPSS: - case ARSQRTSS: - case ASQRTSD: - case ASQRTSS: - case ASUBSD: - case ASUBSS: - case AXORPD: - for(z=0; z<BITS; z++) { - r->set.b[z] |= bit.b[z]; - r->use2.b[z] |= bit.b[z]; - } - break; - - /* - * funny - */ - case AFMOVDP: - case AFMOVFP: - case AFMOVLP: - case AFMOVVP: - case AFMOVWP: - case ACALL: - setaddrs(bit); - break; - } - - switch(p->as) { - case AIMULL: - case AIMULW: - if(p->to.type != D_NONE) - break; - - case AIDIVL: - case AIDIVW: - case ADIVL: - case ADIVW: - case AMULL: - case AMULW: - r->set.b[0] |= RtoB(D_AX) | RtoB(D_DX); - r->use1.b[0] |= RtoB(D_AX) | RtoB(D_DX); - break; - - case AIDIVB: - case AIMULB: - case ADIVB: - case AMULB: - r->set.b[0] |= RtoB(D_AX); - r->use1.b[0] |= RtoB(D_AX); - break; - - case ACWD: - r->set.b[0] |= RtoB(D_AX) | RtoB(D_DX); - r->use1.b[0] |= RtoB(D_AX); - break; - - case ACDQ: - r->set.b[0] |= RtoB(D_DX); - r->use1.b[0] |= RtoB(D_AX); - break; - - case AREP: - case AREPN: - case ALOOP: - case ALOOPEQ: - case ALOOPNE: - r->set.b[0] |= RtoB(D_CX); - r->use1.b[0] |= RtoB(D_CX); - break; - - case AMOVSB: - case AMOVSL: - case AMOVSW: - case ACMPSB: - case ACMPSL: - case ACMPSW: - r->set.b[0] |= RtoB(D_SI) | RtoB(D_DI); - r->use1.b[0] |= RtoB(D_SI) | RtoB(D_DI); - break; - - case ASTOSB: - case ASTOSL: - case ASTOSW: - case ASCASB: - case ASCASL: - case ASCASW: - r->set.b[0] |= RtoB(D_DI); - r->use1.b[0] |= RtoB(D_AX) | RtoB(D_DI); - break; - - case AINSB: - case AINSL: - case AINSW: - r->set.b[0] |= RtoB(D_DX) | RtoB(D_DI); - r->use1.b[0] |= RtoB(D_DI); - break; - - case AOUTSB: - case AOUTSL: - case AOUTSW: - r->set.b[0] |= RtoB(D_DI); - r->use1.b[0] |= RtoB(D_DX) | RtoB(D_DI); - break; + if(bany(&bit)) { + if(info.flags & RightAddr) + setaddrs(bit); + if(info.flags & RightRead) + for(z=0; z<BITS; z++) + r->use2.b[z] |= bit.b[z]; + if(info.flags & RightWrite) + for(z=0; z<BITS; z++) + r->set.b[z] |= bit.b[z]; } } if(firstr == R) @@ -543,45 +216,16 @@ regopt(Prog *firstp) } if(debug['R'] && debug['v']) - dumpit("pass1", firstr); + dumpit("pass1", &firstr->f, 1); /* * pass 2 - * turn branch references to pointers - * build back pointers - */ - for(r=firstr; r!=R; r=r->link) { - p = r->prog; - if(p->to.type == D_BRANCH) { - if(p->to.u.branch == P) - fatal("pnil %P", p); - r1 = p->to.u.branch->reg; - if(r1 == R) - fatal("rnil %P", p); - if(r1 == r) { - //fatal("ref to self %P", p); - continue; - } - r->s2 = r1; - r->p2link = r1->p2; - r1->p2 = r; - } - } - - if(debug['R'] && debug['v']) - dumpit("pass2", firstr); - - /* - * pass 2.5 * find looping structure */ - for(r = firstr; r != R; r = r->link) - r->active = 0; - change = 0; - loopit(firstr, nr); + flowrpo(g); if(debug['R'] && debug['v']) - dumpit("pass2.5", firstr); + dumpit("pass2", &firstr->f, 1); /* * pass 3 @@ -590,17 +234,17 @@ regopt(Prog *firstp) */ loop1: change = 0; - for(r = firstr; r != R; r = r->link) - r->active = 0; - for(r = firstr; r != R; r = r->link) - if(r->prog->as == ARET) + for(r = firstr; r != R; r = (Reg*)r->f.link) + r->f.active = 0; + for(r = firstr; r != R; r = (Reg*)r->f.link) + if(r->f.prog->as == ARET) prop(r, zbits, zbits); loop11: /* pick up unreachable code */ i = 0; for(r = firstr; r != R; r = r1) { - r1 = r->link; - if(r1 && r1->active && !r->active) { + r1 = (Reg*)r->f.link; + if(r1 && r1->f.active && !r->f.active) { prop(r, zbits, zbits); i = 1; } @@ -611,7 +255,7 @@ loop11: goto loop1; if(debug['R'] && debug['v']) - dumpit("pass3", firstr); + dumpit("pass3", &firstr->f, 1); /* * pass 4 @@ -620,20 +264,20 @@ loop11: */ loop2: change = 0; - for(r = firstr; r != R; r = r->link) - r->active = 0; + for(r = firstr; r != R; r = (Reg*)r->f.link) + r->f.active = 0; synch(firstr, zbits); if(change) goto loop2; if(debug['R'] && debug['v']) - dumpit("pass4", firstr); + dumpit("pass4", &firstr->f, 1); /* * pass 4.5 * move register pseudo-variables into regu. */ - for(r = firstr; r != R; r = r->link) { + for(r = firstr; r != R; r = (Reg*)r->f.link) { r->regu = (r->refbehind.b[0] | r->set.b[0]) & REGBITS; r->set.b[0] &= ~REGBITS; @@ -657,26 +301,26 @@ loop2: for(z=0; z<BITS; z++) bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) & ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]); - if(bany(&bit) && !r->refset) { + if(bany(&bit) && !r->f.refset) { // should never happen - all variables are preset if(debug['w']) - print("%L: used and not set: %Q\n", r->prog->lineno, bit); - r->refset = 1; + print("%L: used and not set: %Q\n", r->f.prog->lineno, bit); + r->f.refset = 1; } } - for(r = firstr; r != R; r = r->link) + for(r = firstr; r != R; r = (Reg*)r->f.link) r->act = zbits; rgp = region; nregion = 0; - for(r = firstr; r != R; r = r->link) { + for(r = firstr; r != R; r = (Reg*)r->f.link) { for(z=0; z<BITS; z++) bit.b[z] = r->set.b[z] & ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]); - if(bany(&bit) && !r->refset) { + if(bany(&bit) && !r->f.refset) { if(debug['w']) - print("%L: set and not used: %Q\n", r->prog->lineno, bit); - r->refset = 1; - excise(r); + print("%L: set and not used: %Q\n", r->f.prog->lineno, bit); + r->f.refset = 1; + excise(&r->f); } for(z=0; z<BITS; z++) bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]); @@ -718,19 +362,23 @@ brk: } if(debug['R'] && debug['v']) - dumpit("pass6", firstr); + dumpit("pass6", &firstr->f, 1); + + /* + * free aux structures. peep allocates new ones. + */ + flowend(g); + firstr = R; /* * pass 7 * peep-hole on basic block */ - if(!debug['R'] || debug['P']) { - peep(); - } + if(!debug['R'] || debug['P']) + peep(firstp); /* * eliminate nops - * free aux structures */ for(p=firstp; p!=P; p=p->link) { while(p->link != P && p->link->as == ANOP) @@ -748,11 +396,6 @@ brk: fatal("invalid use of %R with GO386=387: %P", p->to.type, p); } - if(lastr != R) { - lastr->link = freer; - freer = firstr; - } - if(debug['R']) { if(ostats.ncvtreg || ostats.nspill || @@ -795,7 +438,7 @@ addmove(Reg *r, int bn, int rn, int f) clearp(p1); p1->loc = 9999; - p = r->prog; + p = r->f.prog; p1->link = p->link; p->link = p1; p1->lineno = p->lineno; @@ -1012,7 +655,7 @@ prop(Reg *r, Bits ref, Bits cal) Reg *r1, *r2; int z; - for(r1 = r; r1 != R; r1 = r1->p1) { + for(r1 = r; r1 != R; r1 = (Reg*)r1->f.p1) { for(z=0; z<BITS; z++) { ref.b[z] |= r1->refahead.b[z]; if(ref.b[z] != r1->refahead.b[z]) { @@ -1025,9 +668,9 @@ prop(Reg *r, Bits ref, Bits cal) change++; } } - switch(r1->prog->as) { + switch(r1->f.prog->as) { case ACALL: - if(noreturn(r1->prog)) + if(noreturn(r1->f.prog)) break; for(z=0; z<BITS; z++) { cal.b[z] |= ref.b[z] | externs.b[z]; @@ -1067,159 +710,22 @@ prop(Reg *r, Bits ref, Bits cal) r1->refbehind.b[z] = ref.b[z]; r1->calbehind.b[z] = cal.b[z]; } - if(r1->active) + if(r1->f.active) break; - r1->active = 1; + r1->f.active = 1; } - for(; r != r1; r = r->p1) - for(r2 = r->p2; r2 != R; r2 = r2->p2link) + for(; r != r1; r = (Reg*)r->f.p1) + for(r2 = (Reg*)r->f.p2; r2 != R; r2 = (Reg*)r2->f.p2link) prop(r2, r->refbehind, r->calbehind); } -/* - * find looping structure - * - * 1) find reverse postordering - * 2) find approximate dominators, - * the actual dominators if the flow graph is reducible - * otherwise, dominators plus some other non-dominators. - * See Matthew S. Hecht and Jeffrey D. Ullman, - * "Analysis of a Simple Algorithm for Global Data Flow Problems", - * Conf. Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts, - * Oct. 1-3, 1973, pp. 207-217. - * 3) find all nodes with a predecessor dominated by the current node. - * such a node is a loop head. - * recursively, all preds with a greater rpo number are in the loop - */ -int32 -postorder(Reg *r, Reg **rpo2r, int32 n) -{ - Reg *r1; - - r->rpo = 1; - r1 = r->s1; - if(r1 && !r1->rpo) - n = postorder(r1, rpo2r, n); - r1 = r->s2; - if(r1 && !r1->rpo) - n = postorder(r1, rpo2r, n); - rpo2r[n] = r; - n++; - return n; -} - -int32 -rpolca(int32 *idom, int32 rpo1, int32 rpo2) -{ - int32 t; - - if(rpo1 == -1) - return rpo2; - while(rpo1 != rpo2){ - if(rpo1 > rpo2){ - t = rpo2; - rpo2 = rpo1; - rpo1 = t; - } - while(rpo1 < rpo2){ - t = idom[rpo2]; - if(t >= rpo2) - fatal("bad idom"); - rpo2 = t; - } - } - return rpo1; -} - -int -doms(int32 *idom, int32 r, int32 s) -{ - while(s > r) - s = idom[s]; - return s == r; -} - -int -loophead(int32 *idom, Reg *r) -{ - int32 src; - - src = r->rpo; - if(r->p1 != R && doms(idom, src, r->p1->rpo)) - return 1; - for(r = r->p2; r != R; r = r->p2link) - if(doms(idom, src, r->rpo)) - return 1; - return 0; -} - -void -loopmark(Reg **rpo2r, int32 head, Reg *r) -{ - if(r->rpo < head || r->active == head) - return; - r->active = head; - r->loop += LOOP; - if(r->p1 != R) - loopmark(rpo2r, head, r->p1); - for(r = r->p2; r != R; r = r->p2link) - loopmark(rpo2r, head, r); -} - -void -loopit(Reg *r, int32 nr) -{ - Reg *r1; - int32 i, d, me; - - if(nr > maxnr) { - rpo2r = mal(nr * sizeof(Reg*)); - idom = mal(nr * sizeof(int32)); - maxnr = nr; - } - - d = postorder(r, rpo2r, 0); - if(d > nr) - fatal("too many reg nodes %d %d", d, nr); - nr = d; - for(i = 0; i < nr / 2; i++) { - r1 = rpo2r[i]; - rpo2r[i] = rpo2r[nr - 1 - i]; - rpo2r[nr - 1 - i] = r1; - } - for(i = 0; i < nr; i++) - rpo2r[i]->rpo = i; - - idom[0] = 0; - for(i = 0; i < nr; i++) { - r1 = rpo2r[i]; - me = r1->rpo; - d = -1; - // rpo2r[r->rpo] == r protects against considering dead code, - // which has r->rpo == 0. - if(r1->p1 != R && rpo2r[r1->p1->rpo] == r1->p1 && r1->p1->rpo < me) - d = r1->p1->rpo; - for(r1 = r1->p2; r1 != nil; r1 = r1->p2link) - if(rpo2r[r1->rpo] == r1 && r1->rpo < me) - d = rpolca(idom, d, r1->rpo); - idom[i] = d; - } - - for(i = 0; i < nr; i++) { - r1 = rpo2r[i]; - r1->loop++; - if(r1->p2 != R && loophead(idom, r1)) - loopmark(rpo2r, i, r1); - } -} - void synch(Reg *r, Bits dif) { Reg *r1; int z; - for(r1 = r; r1 != R; r1 = r1->s1) { + for(r1 = r; r1 != R; r1 = (Reg*)r1->f.s1) { for(z=0; z<BITS; z++) { dif.b[z] = (dif.b[z] & ~(~r1->refbehind.b[z] & r1->refahead.b[z])) | @@ -1229,13 +735,13 @@ synch(Reg *r, Bits dif) change++; } } - if(r1->active) + if(r1->f.active) break; - r1->active = 1; + r1->f.active = 1; for(z=0; z<BITS; z++) dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]); - if(r1->s2 != R) - synch(r1->s2, dif); + if((Reg*)r1->f.s2 != R) + synch((Reg*)r1->f.s2, dif); } } @@ -1301,7 +807,7 @@ paint1(Reg *r, int bn) for(;;) { if(!(r->refbehind.b[z] & bb)) break; - r1 = r->p1; + r1 = (Reg*)r->f.p1; if(r1 == R) break; if(!(r1->refahead.b[z] & bb)) @@ -1312,45 +818,45 @@ paint1(Reg *r, int bn) } if(LOAD(r) & ~(r->set.b[z]&~(r->use1.b[z]|r->use2.b[z])) & bb) { - change -= CLOAD * r->loop; + change -= CLOAD * r->f.loop; } for(;;) { r->act.b[z] |= bb; - p = r->prog; + p = r->f.prog; if(r->use1.b[z] & bb) { - change += CREF * r->loop; + change += CREF * r->f.loop; if(p->as == AFMOVL || p->as == AFMOVW) if(BtoR(bb) != D_F0) change = -CINF; } if((r->use2.b[z]|r->set.b[z]) & bb) { - change += CREF * r->loop; + change += CREF * r->f.loop; if(p->as == AFMOVL || p->as == AFMOVW) if(BtoR(bb) != D_F0) change = -CINF; } if(STORE(r) & r->regdiff.b[z] & bb) { - change -= CLOAD * r->loop; + change -= CLOAD * r->f.loop; if(p->as == AFMOVL || p->as == AFMOVW) if(BtoR(bb) != D_F0) change = -CINF; } if(r->refbehind.b[z] & bb) - for(r1 = r->p2; r1 != R; r1 = r1->p2link) + for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) if(r1->refahead.b[z] & bb) paint1(r1, bn); if(!(r->refahead.b[z] & bb)) break; - r1 = r->s2; + r1 = (Reg*)r->f.s2; if(r1 != R) if(r1->refbehind.b[z] & bb) paint1(r1, bn); - r = r->s1; + r = (Reg*)r->f.s1; if(r == R) break; if(r->act.b[z] & bb) @@ -1371,7 +877,7 @@ regset(Reg *r, uint32 bb) v = zprog.from; while(b = bb & ~(bb-1)) { v.type = b & 0xFF ? BtoR(b): BtoF(b); - c = copyu(r->prog, &v, A); + c = copyu(r->f.prog, &v, A); if(c == 3) set |= b; bb &= ~b; @@ -1390,7 +896,7 @@ reguse(Reg *r, uint32 bb) v = zprog.from; while(b = bb & ~(bb-1)) { v.type = b & 0xFF ? BtoR(b): BtoF(b); - c = copyu(r->prog, &v, A); + c = copyu(r->f.prog, &v, A); if(c == 1 || c == 2 || c == 4) set |= b; bb &= ~b; @@ -1413,7 +919,7 @@ paint2(Reg *r, int bn) for(;;) { if(!(r->refbehind.b[z] & bb)) break; - r1 = r->p1; + r1 = (Reg*)r->f.p1; if(r1 == R) break; if(!(r1->refahead.b[z] & bb)) @@ -1428,17 +934,17 @@ paint2(Reg *r, int bn) vreg |= r->regu; if(r->refbehind.b[z] & bb) - for(r1 = r->p2; r1 != R; r1 = r1->p2link) + for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) if(r1->refahead.b[z] & bb) vreg |= paint2(r1, bn); if(!(r->refahead.b[z] & bb)) break; - r1 = r->s2; + r1 = (Reg*)r->f.s2; if(r1 != R) if(r1->refbehind.b[z] & bb) vreg |= paint2(r1, bn); - r = r->s1; + r = (Reg*)r->f.s1; if(r == R) break; if(!(r->act.b[z] & bb)) @@ -1448,7 +954,7 @@ paint2(Reg *r, int bn) } bb = vreg; - for(; r; r=r->s1) { + for(; r; r=(Reg*)r->f.s1) { x = r->regu & ~bb; if(x) { vreg |= reguse(r, x); @@ -1473,7 +979,7 @@ paint3(Reg *r, int bn, int32 rb, int rn) for(;;) { if(!(r->refbehind.b[z] & bb)) break; - r1 = r->p1; + r1 = (Reg*)r->f.p1; if(r1 == R) break; if(!(r1->refahead.b[z] & bb)) @@ -1487,7 +993,7 @@ paint3(Reg *r, int bn, int32 rb, int rn) addmove(r, bn, rn, 0); for(;;) { r->act.b[z] |= bb; - p = r->prog; + p = r->f.prog; if(r->use1.b[z] & bb) { if(debug['R'] && debug['v']) @@ -1509,17 +1015,17 @@ paint3(Reg *r, int bn, int32 rb, int rn) r->regu |= rb; if(r->refbehind.b[z] & bb) - for(r1 = r->p2; r1 != R; r1 = r1->p2link) + for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) if(r1->refahead.b[z] & bb) paint3(r1, bn, rb, rn); if(!(r->refahead.b[z] & bb)) break; - r1 = r->s2; + r1 = (Reg*)r->f.s2; if(r1 != R) if(r1->refbehind.b[z] & bb) paint3(r1, bn, rb, rn); - r = r->s1; + r = (Reg*)r->f.s1; if(r == R) break; if(r->act.b[z] & bb) @@ -1577,65 +1083,69 @@ BtoF(int32 b) } void -dumpone(Reg *r) +dumpone(Flow *f, int isreg) { int z; Bits bit; + Reg *r; - print("%d:%P", r->loop, r->prog); - for(z=0; z<BITS; z++) - bit.b[z] = - r->set.b[z] | - r->use1.b[z] | - r->use2.b[z] | - r->refbehind.b[z] | - r->refahead.b[z] | - r->calbehind.b[z] | - r->calahead.b[z] | - r->regdiff.b[z] | - r->act.b[z] | - 0; - if(bany(&bit)) { - print("\t"); - if(bany(&r->set)) - print(" s:%Q", r->set); - if(bany(&r->use1)) - print(" u1:%Q", r->use1); - if(bany(&r->use2)) - print(" u2:%Q", r->use2); - if(bany(&r->refbehind)) - print(" rb:%Q ", r->refbehind); - if(bany(&r->refahead)) - print(" ra:%Q ", r->refahead); - if(bany(&r->calbehind)) - print(" cb:%Q ", r->calbehind); - if(bany(&r->calahead)) - print(" ca:%Q ", r->calahead); - if(bany(&r->regdiff)) - print(" d:%Q ", r->regdiff); - if(bany(&r->act)) - print(" a:%Q ", r->act); + print("%d:%P", f->loop, f->prog); + if(isreg) { + r = (Reg*)f; + for(z=0; z<BITS; z++) + bit.b[z] = + r->set.b[z] | + r->use1.b[z] | + r->use2.b[z] | + r->refbehind.b[z] | + r->refahead.b[z] | + r->calbehind.b[z] | + r->calahead.b[z] | + r->regdiff.b[z] | + r->act.b[z] | + 0; + if(bany(&bit)) { + print("\t"); + if(bany(&r->set)) + print(" s:%Q", r->set); + if(bany(&r->use1)) + print(" u1:%Q", r->use1); + if(bany(&r->use2)) + print(" u2:%Q", r->use2); + if(bany(&r->refbehind)) + print(" rb:%Q ", r->refbehind); + if(bany(&r->refahead)) + print(" ra:%Q ", r->refahead); + if(bany(&r->calbehind)) + print(" cb:%Q ", r->calbehind); + if(bany(&r->calahead)) + print(" ca:%Q ", r->calahead); + if(bany(&r->regdiff)) + print(" d:%Q ", r->regdiff); + if(bany(&r->act)) + print(" a:%Q ", r->act); + } } print("\n"); } void -dumpit(char *str, Reg *r0) +dumpit(char *str, Flow *r0, int isreg) { - Reg *r, *r1; + Flow *r, *r1; print("\n%s\n", str); - for(r = r0; r != R; r = r->link) { - dumpone(r); + for(r = r0; r != nil; r = r->link) { + dumpone(r, isreg); r1 = r->p2; - if(r1 != R) { + if(r1 != nil) { print(" pred:"); - for(; r1 != R; r1 = r1->p2link) + for(; r1 != nil; r1 = r->p2link) print(" %.4ud", r1->prog->loc); print("\n"); } // r1 = r->s1; -// if(r1 != R) { +// if(r1 != nil) { // print(" succ:"); // for(; r1 != R; r1 = r1->s1) // print(" %.4ud", r1->prog->loc); @@ -1643,276 +1153,3 @@ dumpit(char *str, Reg *r0) // } } } - -static Sym* symlist[10]; - -int -noreturn(Prog *p) -{ - Sym *s; - int i; - - if(symlist[0] == S) { - symlist[0] = pkglookup("panicindex", runtimepkg); - symlist[1] = pkglookup("panicslice", runtimepkg); - symlist[2] = pkglookup("throwinit", runtimepkg); - symlist[3] = pkglookup("panic", runtimepkg); - symlist[4] = pkglookup("panicwrap", runtimepkg); - } - - s = p->to.sym; - if(s == S) - return 0; - for(i=0; symlist[i]!=S; i++) - if(s == symlist[i]) - return 1; - return 0; -} - -/* - * the code generator depends on being able to write out JMP - * instructions that it can jump to now but fill in later. - * the linker will resolve them nicely, but they make the code - * longer and more difficult to follow during debugging. - * remove them. - */ - -/* what instruction does a JMP to p eventually land on? */ -static Prog* -chasejmp(Prog *p, int *jmploop) -{ - int n; - - n = 0; - while(p != P && p->as == AJMP && p->to.type == D_BRANCH) { - if(++n > 10) { - *jmploop = 1; - break; - } - p = p->to.u.branch; - } - return p; -} - -/* - * reuse reg pointer for mark/sweep state. - * leave reg==nil at end because alive==nil. - */ -#define alive ((void*)0) -#define dead ((void*)1) - -/* mark all code reachable from firstp as alive */ -static void -mark(Prog *firstp) -{ - Prog *p; - - for(p=firstp; p; p=p->link) { - if(p->reg != dead) - break; - p->reg = alive; - if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch) - mark(p->to.u.branch); - if(p->as == AJMP || p->as == ARET || p->as == AUNDEF) - break; - } -} - -static void -fixjmp(Prog *firstp) -{ - int jmploop; - Prog *p, *last; - - if(debug['R'] && debug['v']) - print("\nfixjmp\n"); - - // pass 1: resolve jump to AJMP, mark all code as dead. - jmploop = 0; - for(p=firstp; p; p=p->link) { - if(debug['R'] && debug['v']) - print("%P\n", p); - if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch && p->to.u.branch->as == AJMP) { - p->to.u.branch = chasejmp(p->to.u.branch, &jmploop); - if(debug['R'] && debug['v']) - print("->%P\n", p); - } - p->reg = dead; - } - if(debug['R'] && debug['v']) - print("\n"); - - // pass 2: mark all reachable code alive - mark(firstp); - - // pass 3: delete dead code (mostly JMPs). - last = nil; - for(p=firstp; p; p=p->link) { - if(p->reg == dead) { - if(p->link == P && p->as == ARET && last && last->as != ARET) { - // This is the final ARET, and the code so far doesn't have one. - // Let it stay. - } else { - if(debug['R'] && debug['v']) - print("del %P\n", p); - continue; - } - } - if(last) - last->link = p; - last = p; - } - last->link = P; - - // pass 4: elide JMP to next instruction. - // only safe if there are no jumps to JMPs anymore. - if(!jmploop) { - last = nil; - for(p=firstp; p; p=p->link) { - if(p->as == AJMP && p->to.type == D_BRANCH && p->to.u.branch == p->link) { - if(debug['R'] && debug['v']) - print("del %P\n", p); - continue; - } - if(last) - last->link = p; - last = p; - } - last->link = P; - } - - if(debug['R'] && debug['v']) { - print("\n"); - for(p=firstp; p; p=p->link) - print("%P\n", p); - print("\n"); - } -} - -static uint32 -fnv1(Sym *sym) -{ - uint32 h; - char *s; - - h = 2166136261U; - for(s=sym->name;*s;s++) { - h = (16777619 * h) ^ (uint32)(uint8)(*s); - } - return h; -} - -static uint16 -hash32to16(uint32 h) -{ - return (h & 0xffff) ^ (h >> 16); -} - -/* - * fixtemp eliminates sequences like: - * MOV reg1, mem - * OP mem, reg2 - * when mem is a stack variable which is not mentioned - * anywhere else. The instructions are replaced by - * OP reg1, reg2 - * this reduces the number of variables that the register optimizer - * sees, which lets it do a better job and makes it less likely to turn - * itself off. - */ -static void -fixtemp(Prog *firstp) -{ - static uint8 counts[1<<16]; // A hash table to count variable occurences. - int i; - Prog *p, *p2; - uint32 h; - - if(debug['R'] && debug['v']) - print("\nfixtemp\n"); - - // Count variable references. We actually use a hashtable so this - // is only approximate. - for(i=0; i<nelem(counts); i++) - counts[i] = 0; - for(p=firstp; p!=P; p=p->link) { - if(p->from.type == D_AUTO) { - h = hash32to16(fnv1(p->from.sym)); - //print("seen %S hash %d\n", p->from.sym, hash32to16(h)); - if(counts[h] < 10) - counts[h]++; - } - if(p->to.type == D_AUTO) { - h = hash32to16(fnv1(p->to.sym)); - //print("seen %S hash %d\n", p->to.sym, hash32to16(h)); - if(counts[h] < 10) - counts[h]++; - } - } - - // Eliminate single-write, single-read stack variables. - for(p=firstp; p!=P; p=p->link) { - if(debug['R'] && debug['v']) - print("%P\n", p); - if(p->link == P || p->to.type != D_AUTO) - continue; - if(isfloat[p->to.etype] && FtoB(p->from.type)) { - switch(p->as) { - case AMOVSS: - case AMOVSD: - break; - default: - continue; - } - } else if(!isfloat[p->to.etype] && RtoB(p->from.type)) { - switch(p->as) { - case AMOVB: - if(p->to.width == 1) - break; - case AMOVW: - if(p->to.width == 2) - break; - case AMOVL: - if(p->to.width == 4) - break; - default: - continue; - } - } else - continue; - // p is a MOV reg, mem. - p2 = p->link; - h = hash32to16(fnv1(p->to.sym)); - if(counts[h] != 2) { - continue; - } - switch(p2->as) { - case ALEAL: - case AFMOVD: - case AFMOVF: - case AFMOVL: - case AFMOVW: - case AFMOVV: - // funny - continue; - } - // p2 is OP mem, reg2 - // and OP is not a funny instruction. - if(p2->from.sym == p->to.sym - && p2->from.offset == p->to.offset - && p2->from.type == p->to.type) { - if(debug['R'] && debug['v']) { - print(" ===elide== %D\n", &p->to); - print("%P", p2); - } - // p2 is OP mem, reg2. - // change to OP reg, reg2 and - // eliminate the mov. - p2->from = p->from; - *p = *p2; - p->link = p2->link; - if(debug['R'] && debug['v']) { - print(" ===change== %P\n", p); - } - } - } -} diff --git a/src/cmd/8l/8.out.h b/src/cmd/8l/8.out.h index cf0bc9fee..988e50f3e 100644 --- a/src/cmd/8l/8.out.h +++ b/src/cmd/8l/8.out.h @@ -30,11 +30,7 @@ #define NSYM 50 #define NSNAME 8 -#define NOPROF (1<<0) -#define DUPOK (1<<1) -#define NOSPLIT (1<<2) -#define RODATA (1<<3) -#define NOPTR (1<<4) +#include "../ld/textflag.h" enum as { @@ -578,9 +574,11 @@ enum as APSHUFB, AUSEFIELD, - ALOCALS, ATYPE, - + AFUNCDATA, + APCDATA, + ACHECKNIL, + ALAST }; diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c index 18591cd2f..3be37ea22 100644 --- a/src/cmd/8l/asm.c +++ b/src/cmd/8l/asm.c @@ -41,6 +41,7 @@ char linuxdynld[] = "/lib/ld-linux.so.2"; char freebsddynld[] = "/usr/libexec/ld-elf.so.1"; char openbsddynld[] = "/usr/libexec/ld.so"; char netbsddynld[] = "/usr/libexec/ld.elf_so"; +char dragonflydynld[] = "/usr/libexec/ld-elf.so.2"; int32 entryvalue(void) @@ -95,12 +96,6 @@ int nelfsym = 1; static void addpltsym(Sym*); static void addgotsym(Sym*); -Sym * -lookuprel(void) -{ - return lookup(".rel", 0); -} - void adddynrela(Sym *rela, Sym *s, Reloc *r) { @@ -366,6 +361,8 @@ int archreloc(Reloc *r, Sym *s, vlong *val) { USED(s); + if(linkmode == LinkExternal) + return -1; switch(r->type) { case D_CONST: *val = r->add; @@ -595,12 +592,19 @@ asmb(void) sect = segtext.sect; cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); codeblk(sect->vaddr, sect->len); - - /* output read-only data in text segment (rodata, gosymtab, pclntab, ...) */ for(sect = sect->next; sect != nil; sect = sect->next) { cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); datblk(sect->vaddr, sect->len); } + + if(segrodata.filelen > 0) { + if(debug['v']) + Bprint(&bso, "%5.2f rodatblk\n", cputime()); + Bflush(&bso); + + cseek(segrodata.fileoff); + datblk(segrodata.vaddr, segrodata.filelen); + } if(debug['v']) Bprint(&bso, "%5.2f datblk\n", cputime()); @@ -655,7 +659,7 @@ asmb(void) symo = rnd(HEADR+segtext.filelen, INITRND)+rnd(segdata.filelen, INITRND)+machlink; break; Elfsym: - symo = rnd(HEADR+segtext.filelen, INITRND)+segdata.filelen; + symo = rnd(HEADR+segtext.filelen, INITRND)+rnd(HEADR+segrodata.filelen, INITRND)+segdata.filelen; symo = rnd(symo, INITRND); break; case Hwindows: @@ -843,6 +847,7 @@ asmb(void) case Hfreebsd: case Hnetbsd: case Hopenbsd: + case Hdragonfly: asmbelf(symo); break; case Hwindows: diff --git a/src/cmd/8l/l.h b/src/cmd/8l/l.h index ce12d59ba..814aa1458 100644 --- a/src/cmd/8l/l.h +++ b/src/cmd/8l/l.h @@ -143,7 +143,6 @@ struct Sym int32 got; int32 align; // if non-zero, required alignment in bytes int32 elfsym; - int32 locals; // size of stack frame locals area int32 args; // size of stack frame incoming arguments area Sym* hash; // in hash table Sym* allsym; // in all symbol list @@ -157,6 +156,7 @@ struct Sym char* dynimplib; char* dynimpvers; struct Section* sect; + struct Hist* hist; // for ATEXT // STEXT Auto* autom; @@ -169,14 +169,13 @@ struct Sym Reloc* r; int32 nr; int32 maxr; - int rel_ro; }; struct Optab { short as; uchar* ytab; uchar prefix; - uchar op[12]; + uchar op[13]; }; enum @@ -185,7 +184,7 @@ enum STRINGSZ = 200, MINLC = 1, MAXIO = 8192, - MAXHIST = 20, /* limit of path elements for history symbols */ + MAXHIST = 40, /* limit of path elements for history symbols */ Yxxx = 0, Ynone, @@ -285,7 +284,6 @@ EXTERN int32 INITRND; EXTERN int32 INITTEXT; EXTERN int32 INITDAT; EXTERN char* INITENTRY; /* entry point */ -EXTERN char* LIBINITENTRY; /* shared library entry point */ EXTERN char* pcstr; EXTERN Auto* curauto; EXTERN Auto* curhist; @@ -311,7 +309,6 @@ EXTERN Sym* symlist; EXTERN int32 symsize; EXTERN Sym* textp; EXTERN int32 textsize; -EXTERN int version; EXTERN Prog zprg; EXTERN int dtype; EXTERN int tlsoffset; diff --git a/src/cmd/8l/list.c b/src/cmd/8l/list.c index 0b544fbce..e2a2ec5ed 100644 --- a/src/cmd/8l/list.c +++ b/src/cmd/8l/list.c @@ -356,7 +356,7 @@ Iconv(Fmt *fp) void diag(char *fmt, ...) { - char buf[STRINGSZ], *tn, *sep; + char buf[1024], *tn, *sep; va_list arg; tn = ""; diff --git a/src/cmd/8l/obj.c b/src/cmd/8l/obj.c index c819b9936..3fdc41381 100644 --- a/src/cmd/8l/obj.c +++ b/src/cmd/8l/obj.c @@ -53,6 +53,7 @@ Header headers[] = { "msdoscom", Hmsdoscom, "msdosexe", Hmsdosexe, "darwin", Hdarwin, + "dragonfly", Hdragonfly, "linux", Hlinux, "freebsd", Hfreebsd, "netbsd", Hnetbsd, @@ -69,6 +70,7 @@ Header headers[] = { * -Hmsdoscom -Tx -Rx is MS-DOS .COM * -Hmsdosexe -Tx -Rx is fake MS-DOS .EXE * -Hdarwin -Tx -Rx is Apple Mach-O + * -Hdragonfly -Tx -Rx is DragonFly ELF32 * -Hlinux -Tx -Rx is Linux ELF32 * -Hfreebsd -Tx -Rx is FreeBSD ELF32 * -Hnetbsd -Tx -Rx is NetBSD ELF32 @@ -89,7 +91,6 @@ main(int argc, char *argv[]) INITDAT = -1; INITRND = -1; INITENTRY = 0; - LIBINITENTRY = 0; linkmode = LinkAuto; nuxiinit(); @@ -117,6 +118,7 @@ main(int argc, char *argv[]) flagstr("extldflags", "flags for external linker", &extldflags); flagcount("f", "ignore version mismatch", &debug['f']); flagcount("g", "disable go package data checks", &debug['g']); + flagstr("installsuffix", "pkg directory suffix", &flag_installsuffix); flagfn1("linkmode", "mode: set link mode (internal, external, auto)", setlinkmode); flagstr("k", "sym: set field tracking symbol", &tracksym); flagstr("o", "outfile: set output file", &outfile); @@ -154,6 +156,7 @@ main(int argc, char *argv[]) sysfatal("cannot use -linkmode=external with -H %s", headstr(HEADTYPE)); break; case Hdarwin: + case Hdragonfly: case Hfreebsd: case Hlinux: case Hnetbsd: @@ -243,6 +246,7 @@ main(int argc, char *argv[]) case Hfreebsd: case Hnetbsd: case Hopenbsd: + case Hdragonfly: /* * ELF uses TLS offsets negative from %gs. * Translate 0(GS) and 4(GS) into -8(GS) and -4(GS). @@ -366,18 +370,18 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[]) a->type = D_NONE; a->offset = 0; if(t & T_OFFSET) - a->offset = Bget4(f); + a->offset = BGETLE4(f); a->offset2 = 0; if(t & T_OFFSET2) { - a->offset2 = Bget4(f); + a->offset2 = BGETLE4(f); a->type = D_CONST2; } a->sym = S; if(t & T_SYM) a->sym = zsym(pn, f, h); if(t & T_FCONST) { - a->ieee.l = Bget4(f); - a->ieee.h = Bget4(f); + a->ieee.l = BGETLE4(f); + a->ieee.h = BGETLE4(f); a->type = D_FCONST; } else if(t & T_SCONST) { @@ -476,7 +480,7 @@ loop: if(o == ANAME || o == ASIGNAME) { sig = 0; if(o == ASIGNAME) - sig = Bget4(f); + sig = BGETLE4(f); v = BGETC(f); /* type */ o = BGETC(f); /* sym */ r = 0; @@ -531,7 +535,7 @@ loop: p = mal(sizeof(*p)); p->as = o; - p->line = Bget4(f); + p->line = BGETLE4(f); p->back = 2; zaddr(pn, f, &p->from, h); fromgotype = adrgotype; @@ -552,6 +556,7 @@ loop: addhist(p->line, D_FILE); /* 'z' */ if(p->to.offset) addhist(p->to.offset, D_FILE1); /* 'Z' */ + savehist(p->line, p->to.offset); histfrogp = 0; goto loop; @@ -613,13 +618,6 @@ loop: pc++; goto loop; - case ALOCALS: - if(skip) - goto casdef; - cursym->locals = p->to.offset; - pc++; - goto loop; - case ATYPE: if(skip) goto casdef; @@ -663,6 +661,7 @@ loop: diag("%s: redefinition: %s\n%P", pn, s->name, p); } s->type = STEXT; + s->hist = gethist(); s->value = pc; s->args = p->to.offset2; lastp = p; diff --git a/src/cmd/8l/optab.c b/src/cmd/8l/optab.c index 1d9d2f55f..a4c40e8e3 100644 --- a/src/cmd/8l/optab.c +++ b/src/cmd/8l/optab.c @@ -49,6 +49,16 @@ uchar ynop[] = Yrf, Ynone, Zpseudo,1, 0 }; +uchar yfuncdata[] = +{ + Yi32, Ym, Zpseudo, 0, + 0 +}; +uchar ypcdata[] = +{ + Yi32, Yi32, Zpseudo, 0, + 0, +}; uchar yxorb[] = { Yi32, Yal, Zib_, 1, @@ -142,6 +152,17 @@ uchar ymovb[] = Yi32, Ymb, Zibo_m, 2, 0 }; +uchar ymovw[] = +{ + Yrl, Yml, Zr_m, 1, + Yml, Yrl, Zm_r, 1, + Yi0, Yrl, Zclr, 1+2, +// Yi0, Yml, Zibo_m, 2, // shorter but slower AND $0,dst + Yi32, Yrl, Zil_rp, 1, + Yi32, Yml, Zilo_m, 2, + Yiauto, Yrl, Zaut_r, 1, + 0 +}; uchar ymovl[] = { Yrl, Yml, Zr_m, 1, @@ -152,7 +173,7 @@ uchar ymovl[] = Yi32, Yml, Zilo_m, 2, Yml, Yxr, Zm_r_xm, 2, // XMM MOVD (32 bit) Yxr, Yml, Zr_m_xm, 2, // XMM MOVD (32 bit) - Yiauto, Yrl, Zaut_r, 2, + Yiauto, Yrl, Zaut_r, 1, 0 }; uchar ymovq[] = @@ -196,8 +217,10 @@ uchar yml_mb[] = Ymb, Yrb, Zm_r, 1, 0 }; -uchar yml_ml[] = +uchar yxchg[] = { + Yax, Yrl, Z_rp, 1, + Yrl, Yax, Zrp_, 1, Yrl, Yml, Zr_m, 1, Yml, Yrl, Zm_r, 1, 0 @@ -580,8 +603,8 @@ Optab optab[] = { ALSLL, yml_rl, Pm, 0x03 }, { ALSLW, yml_rl, Pq, 0x03 }, { AMOVB, ymovb, Pb, 0x88,0x8a,0xb0,0xc6,(00) }, - { AMOVL, ymovl, Px, 0x89,0x8b,0x31,0x83,(04),0xb8,0xc7,(00),Pe,0x6e,Pe,0x7e }, - { AMOVW, ymovl, Pe, 0x89,0x8b,0x31,0x83,(04),0xb8,0xc7,(00) }, + { AMOVL, ymovl, Px, 0x89,0x8b,0x31,0x83,(04),0xb8,0xc7,(00),Pe,0x6e,Pe,0x7e,0 }, + { AMOVW, ymovw, Pe, 0x89,0x8b,0x31,0x83,(04),0xb8,0xc7,(00),0 }, { AMOVQ, ymovq, Pf3, 0x7e }, { AMOVBLSX, ymb_rl, Pm, 0xbe }, { AMOVBLZX, ymb_rl, Pm, 0xb6 }, @@ -696,8 +719,8 @@ Optab optab[] = { AWAIT, ynone, Px, 0x9b }, { AWORD, ybyte, Px, 2 }, { AXCHGB, yml_mb, Pb, 0x86,0x86 }, - { AXCHGL, yml_ml, Px, 0x87,0x87 }, - { AXCHGW, yml_ml, Pe, 0x87,0x87 }, + { AXCHGL, yxchg, Px, 0x90,0x90,0x87,0x87 }, + { AXCHGW, yxchg, Pe, 0x90,0x90,0x87,0x87 }, { AXLAT, ynone, Px, 0xd7 }, { AXORB, yxorb, Pb, 0x34,0x80,(06),0x30,0x32 }, { AXORL, yxorl, Px, 0x83,(06),0x35,0x81,(06),0x31,0x33 }, @@ -999,8 +1022,9 @@ Optab optab[] = { APSHUFB, ymshufb,Pq, 0x38, 0x00 }, { AUSEFIELD, ynop, Px, 0,0 }, - { ALOCALS }, { ATYPE }, + { AFUNCDATA, yfuncdata, Px, 0,0 }, + { APCDATA, ypcdata, Px, 0,0 }, 0 }; diff --git a/src/cmd/8l/pass.c b/src/cmd/8l/pass.c index 4871761ff..1eaf78fe0 100644 --- a/src/cmd/8l/pass.c +++ b/src/cmd/8l/pass.c @@ -329,7 +329,7 @@ patch(void) p->from.offset = 0; } } - if((p->as == ACALL && p->to.type != D_BRANCH) || (p->as == AJMP && p->to.type != D_BRANCH)) { + if((p->as == ACALL && p->to.type != D_BRANCH) || (p->as == AJMP && p->to.type != D_BRANCH) || (p->as == ARET && p->to.sym != nil)) { s = p->to.sym; if(p->to.type == D_INDIR+D_ADDR) { /* skip check if this is an indirect call (CALL *symbol(SB)) */ @@ -405,15 +405,19 @@ brloop(Prog *p) return q; } +static Prog* load_g_cx(Prog*); +static Prog* stacksplit(Prog*, int32, Prog**); + +static Sym *plan9_tos; +static Prog *pmorestack; +static Sym *symmorestack; + void dostkoff(void) { - Prog *p, *q, *q1; + Prog *p, *q; int32 autoffset, deltasp; int a; - Prog *pmorestack; - Sym *symmorestack; - Sym *plan9_tos; pmorestack = P; symmorestack = lookup("runtime.morestack", 0); @@ -439,154 +443,13 @@ dostkoff(void) autoffset = 0; q = P; - if(pmorestack != P) - if(!(p->from.scale & NOSPLIT)) { - p = appendp(p); // load g into CX - switch(HEADTYPE) { - case Hwindows: - p->as = AMOVL; - p->from.type = D_INDIR+D_FS; - p->from.offset = 0x14; - p->to.type = D_CX; - - p = appendp(p); - p->as = AMOVL; - p->from.type = D_INDIR+D_CX; - p->from.offset = 0; - p->to.type = D_CX; - break; - - case Hlinux: - if(linkmode != LinkExternal) { - p->as = AMOVL; - p->from.type = D_INDIR+D_GS; - p->from.offset = 0; - p->to.type = D_CX; - - p = appendp(p); - p->as = AMOVL; - p->from.type = D_INDIR+D_CX; - p->from.offset = tlsoffset + 0; - p->to.type = D_CX; - } else { - p->as = AMOVL; - p->from.type = D_INDIR+D_GS; - p->from.offset = tlsoffset + 0; - p->to.type = D_CX; - p->from.index = D_GS; - p->from.scale = 1; - } - break; - - case Hplan9x32: - p->as = AMOVL; - p->from.type = D_EXTERN; - p->from.sym = plan9_tos; - p->to.type = D_CX; - - p = appendp(p); - p->as = AMOVL; - p->from.type = D_INDIR+D_CX; - p->from.offset = tlsoffset + 0; - p->to.type = D_CX; - break; - - default: - p->as = AMOVL; - p->from.type = D_INDIR+D_GS; - p->from.offset = tlsoffset + 0; - p->to.type = D_CX; - } - - if(debug['K']) { - // 8l -K means check not only for stack - // overflow but stack underflow. - // On underflow, INT 3 (breakpoint). - // Underflow itself is rare but this also - // catches out-of-sync stack guard info. - p = appendp(p); - p->as = ACMPL; - p->from.type = D_INDIR+D_CX; - p->from.offset = 4; - p->to.type = D_SP; - - p = appendp(p); - p->as = AJCC; - p->to.type = D_BRANCH; - p->to.offset = 4; - q1 = p; - - p = appendp(p); - p->as = AINT; - p->from.type = D_CONST; - p->from.offset = 3; - - p = appendp(p); - p->as = ANOP; - q1->pcond = p; - } - - if(autoffset < StackBig) { // do we need to call morestack - if(autoffset <= StackSmall) { - // small stack - p = appendp(p); - p->as = ACMPL; - p->from.type = D_SP; - p->to.type = D_INDIR+D_CX; - } else { - // large stack - p = appendp(p); - p->as = ALEAL; - p->from.type = D_INDIR+D_SP; - p->from.offset = -(autoffset-StackSmall); - p->to.type = D_AX; - - p = appendp(p); - p->as = ACMPL; - p->from.type = D_AX; - p->to.type = D_INDIR+D_CX; - } - - // common - p = appendp(p); - p->as = AJHI; - p->to.type = D_BRANCH; - p->to.offset = 4; - q = p; - } - - p = appendp(p); // save frame size in DI - p->as = AMOVL; - p->to.type = D_DI; - p->from.type = D_CONST; - - // If we ask for more stack, we'll get a minimum of StackMin bytes. - // We need a stack frame large enough to hold the top-of-stack data, - // the function arguments+results, our caller's PC, our frame, - // a word for the return PC of the next call, and then the StackLimit bytes - // that must be available on entry to any function called from a function - // that did a stack check. If StackMin is enough, don't ask for a specific - // amount: then we can use the custom functions and save a few - // instructions. - if(StackTop + cursym->text->to.offset2 + PtrSize + autoffset + PtrSize + StackLimit >= StackMin) - p->from.offset = (autoffset+7) & ~7LL; - - p = appendp(p); // save arg size in AX - p->as = AMOVL; - p->to.type = D_AX; - p->from.type = D_CONST; - p->from.offset = cursym->text->to.offset2; + if(!(p->from.scale & NOSPLIT) || (p->from.scale & WRAPPER)) { p = appendp(p); - p->as = ACALL; - p->to.type = D_BRANCH; - p->pcond = pmorestack; - p->to.sym = symmorestack; - + p = load_g_cx(p); // load g into CX } - - if(q != P) - q->pcond = p->link; + if(!(cursym->text->from.scale & NOSPLIT)) + p = stacksplit(p, autoffset, &q); // emit split check if(autoffset) { p = appendp(p); @@ -594,8 +457,6 @@ dostkoff(void) p->from.type = D_CONST; p->from.offset = autoffset; p->spadj = autoffset; - if(q != P) - q->pcond = p; } else { // zero-byte stack adjustment. // Insert a fake non-zero adjustment so that stkcheck can @@ -607,8 +468,20 @@ dostkoff(void) p->as = ANOP; p->spadj = PtrSize; } + if(q != P) + q->pcond = p; deltasp = autoffset; + if(cursym->text->from.scale & WRAPPER) { + // g->panicwrap += autoffset + PtrSize; + p = appendp(p); + p->as = AADDL; + p->from.type = D_CONST; + p->from.offset = autoffset + PtrSize; + p->to.type = D_INDIR+D_CX; + p->to.offset = 2*PtrSize; + } + if(debug['Z'] && autoffset && !(cursym->text->from.scale&NOSPLIT)) { // 8l -Z means zero the stack frame on entry. // This slows down function calls but can help avoid @@ -678,6 +551,19 @@ dostkoff(void) if(autoffset != deltasp) diag("unbalanced PUSH/POP"); + + if(cursym->text->from.scale & WRAPPER) { + p = load_g_cx(p); + p = appendp(p); + // g->panicwrap -= autoffset + PtrSize; + p->as = ASUBL; + p->from.type = D_CONST; + p->from.offset = autoffset + PtrSize; + p->to.type = D_INDIR+D_CX; + p->to.offset = 2*PtrSize; + p = appendp(p); + p->as = ARET; + } if(autoffset) { p->as = AADJSP; @@ -692,8 +578,243 @@ dostkoff(void) // the cleanup. p->spadj = +autoffset; } + if(p->to.sym) // retjmp + p->as = AJMP; + } + } +} + +// Append code to p to load g into cx. +// Overwrites p with the first instruction (no first appendp). +// Overwriting p is unusual but it lets use this in both the +// prologue (caller must call appendp first) and in the epilogue. +// Returns last new instruction. +static Prog* +load_g_cx(Prog *p) +{ + switch(HEADTYPE) { + case Hwindows: + p->as = AMOVL; + p->from.type = D_INDIR+D_FS; + p->from.offset = 0x14; + p->to.type = D_CX; + + p = appendp(p); + p->as = AMOVL; + p->from.type = D_INDIR+D_CX; + p->from.offset = 0; + p->to.type = D_CX; + break; + + case Hlinux: + if(linkmode != LinkExternal) { + p->as = AMOVL; + p->from.type = D_INDIR+D_GS; + p->from.offset = 0; + p->to.type = D_CX; + + p = appendp(p); + p->as = AMOVL; + p->from.type = D_INDIR+D_CX; + p->from.offset = tlsoffset + 0; + p->to.type = D_CX; + } else { + p->as = AMOVL; + p->from.type = D_INDIR+D_GS; + p->from.offset = tlsoffset + 0; + p->to.type = D_CX; + p->from.index = D_GS; + p->from.scale = 1; } + break; + + case Hplan9x32: + p->as = AMOVL; + p->from.type = D_EXTERN; + p->from.sym = plan9_tos; + p->to.type = D_CX; + + p = appendp(p); + p->as = AMOVL; + p->from.type = D_INDIR+D_CX; + p->from.offset = tlsoffset + 0; + p->to.type = D_CX; + break; + + default: + p->as = AMOVL; + p->from.type = D_INDIR+D_GS; + p->from.offset = tlsoffset + 0; + p->to.type = D_CX; + } + return p; +} + +// Append code to p to check for stack split. +// Appends to (does not overwrite) p. +// Assumes g is in CX. +// Returns last new instruction. +// On return, *jmpok is the instruction that should jump +// to the stack frame allocation if no split is needed. +static Prog* +stacksplit(Prog *p, int32 framesize, Prog **jmpok) +{ + Prog *q, *q1; + int arg; + + if(debug['K']) { + // 8l -K means check not only for stack + // overflow but stack underflow. + // On underflow, INT 3 (breakpoint). + // Underflow itself is rare but this also + // catches out-of-sync stack guard info. + p = appendp(p); + p->as = ACMPL; + p->from.type = D_INDIR+D_CX; + p->from.offset = 4; + p->to.type = D_SP; + + p = appendp(p); + p->as = AJCC; + p->to.type = D_BRANCH; + p->to.offset = 4; + q1 = p; + + p = appendp(p); + p->as = AINT; + p->from.type = D_CONST; + p->from.offset = 3; + + p = appendp(p); + p->as = ANOP; + q1->pcond = p; } + q1 = P; + + if(framesize <= StackSmall) { + // small stack: SP <= stackguard + // CMPL SP, stackguard + p = appendp(p); + p->as = ACMPL; + p->from.type = D_SP; + p->to.type = D_INDIR+D_CX; + } else if(framesize <= StackBig) { + // large stack: SP-framesize <= stackguard-StackSmall + // LEAL -(framesize-StackSmall)(SP), AX + // CMPL AX, stackguard + p = appendp(p); + p->as = ALEAL; + p->from.type = D_INDIR+D_SP; + p->from.offset = -(framesize-StackSmall); + p->to.type = D_AX; + + p = appendp(p); + p->as = ACMPL; + p->from.type = D_AX; + p->to.type = D_INDIR+D_CX; + } else { + // Such a large stack we need to protect against wraparound + // if SP is close to zero. + // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall) + // The +StackGuard on both sides is required to keep the left side positive: + // SP is allowed to be slightly below stackguard. See stack.h. + // + // Preemption sets stackguard to StackPreempt, a very large value. + // That breaks the math above, so we have to check for that explicitly. + // MOVL stackguard, CX + // CMPL CX, $StackPreempt + // JEQ label-of-call-to-morestack + // LEAL StackGuard(SP), AX + // SUBL stackguard, AX + // CMPL AX, $(framesize+(StackGuard-StackSmall)) + p = appendp(p); + p->as = AMOVL; + p->from.type = D_INDIR+D_CX; + p->from.offset = 0; + p->to.type = D_SI; + + p = appendp(p); + p->as = ACMPL; + p->from.type = D_SI; + p->to.type = D_CONST; + p->to.offset = (uint32)StackPreempt; + + p = appendp(p); + p->as = AJEQ; + p->to.type = D_BRANCH; + q1 = p; + + p = appendp(p); + p->as = ALEAL; + p->from.type = D_INDIR+D_SP; + p->from.offset = StackGuard; + p->to.type = D_AX; + + p = appendp(p); + p->as = ASUBL; + p->from.type = D_SI; + p->from.offset = 0; + p->to.type = D_AX; + + p = appendp(p); + p->as = ACMPL; + p->from.type = D_AX; + p->to.type = D_CONST; + p->to.offset = framesize+(StackGuard-StackSmall); + } + + // common + p = appendp(p); + p->as = AJHI; + p->to.type = D_BRANCH; + p->to.offset = 4; + q = p; + + p = appendp(p); // save frame size in DI + p->as = AMOVL; + p->to.type = D_DI; + p->from.type = D_CONST; + + // If we ask for more stack, we'll get a minimum of StackMin bytes. + // We need a stack frame large enough to hold the top-of-stack data, + // the function arguments+results, our caller's PC, our frame, + // a word for the return PC of the next call, and then the StackLimit bytes + // that must be available on entry to any function called from a function + // that did a stack check. If StackMin is enough, don't ask for a specific + // amount: then we can use the custom functions and save a few + // instructions. + if(StackTop + cursym->text->to.offset2 + PtrSize + framesize + PtrSize + StackLimit >= StackMin) + p->from.offset = (framesize+7) & ~7LL; + + arg = cursym->text->to.offset2; + if(arg == 1) // special marker for known 0 + arg = 0; + if(arg&3) + diag("misaligned argument size in stack split"); + p = appendp(p); // save arg size in AX + p->as = AMOVL; + p->to.type = D_AX; + p->from.type = D_CONST; + p->from.offset = arg; + + p = appendp(p); + p->as = ACALL; + p->to.type = D_BRANCH; + p->pcond = pmorestack; + p->to.sym = symmorestack; + + p = appendp(p); + p->as = AJMP; + p->to.type = D_BRANCH; + p->pcond = cursym->text->link; + + if(q != P) + q->pcond = p->link; + if(q1 != P) + q1->pcond = q->link; + + *jmpok = q; + return p; } int32 diff --git a/src/cmd/8l/span.c b/src/cmd/8l/span.c index 980186b16..acf973cab 100644 --- a/src/cmd/8l/span.c +++ b/src/cmd/8l/span.c @@ -695,18 +695,11 @@ putrelv: r = addrel(cursym); r->off = curp->pc + andptr - and; - r->add = 0; - r->xadd = 0; + r->add = a->offset-tlsoffset; + r->xadd = r->add; r->siz = 4; r->type = D_TLS; - if(a->offset == tlsoffset+0) - s = lookup("runtime.g", 0); - else - s = lookup("runtime.m", 0); - s->type = STLSBSS; - s->reachable = 1; - s->hide = 1; - s->size = PtrSize; + s = lookup("runtime.tlsgm", 0); r->sym = s; r->xsym = s; v = 0; diff --git a/src/cmd/addr2line/main.c b/src/cmd/addr2line/main.c index 9faadc27b..54c4d90b5 100644 --- a/src/cmd/addr2line/main.c +++ b/src/cmd/addr2line/main.c @@ -31,7 +31,7 @@ void main(int argc, char **argv) { int fd; - char *p; + char *p, *q; uvlong pc; Symbol s; Fhdr fhdr; @@ -67,6 +67,17 @@ main(int argc, char **argv) if(p == nil) break; p[Blinelen(&bin)-1] = '\0'; + q = strchr(p, ':'); + if(q != nil) { + // reverse: translate file:line to pc + *q++ = '\0'; + pc = file2pc(p, atoi(q)); + if(pc == ~(uvlong)0) + Bprint(&bout, "!%r\n"); + else + Bprint(&bout, "0x%llux\n", pc); + continue; + } pc = strtoull(p, 0, 16); if(!findsym(pc, CTEXT, &s)) s.name = "??"; diff --git a/src/cmd/api/clone.go b/src/cmd/api/clone.go deleted file mode 100644 index 180215f4b..000000000 --- a/src/cmd/api/clone.go +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "fmt" - "go/ast" - "log" - "reflect" -) - -const debugClone = false - -// TODO(bradfitz): delete this function (and whole file) once -// http://golang.org/issue/4380 is fixed. -func clone(i interface{}) (cloned interface{}) { - if debugClone { - defer func() { - if !reflect.DeepEqual(i, cloned) { - log.Printf("cloned %T doesn't match: in=%#v out=%#v", i, i, cloned) - } - }() - } - switch v := i.(type) { - case nil: - return nil - case *ast.File: - o := &ast.File{ - Doc: v.Doc, // shallow - Package: v.Package, - Comments: v.Comments, // shallow - Name: v.Name, - Scope: v.Scope, - } - for _, x := range v.Decls { - o.Decls = append(o.Decls, clone(x).(ast.Decl)) - } - for _, x := range v.Imports { - o.Imports = append(o.Imports, clone(x).(*ast.ImportSpec)) - } - for _, x := range v.Unresolved { - o.Unresolved = append(o.Unresolved, x) - } - return o - case *ast.GenDecl: - o := new(ast.GenDecl) - *o = *v - o.Specs = nil - for _, x := range v.Specs { - o.Specs = append(o.Specs, clone(x).(ast.Spec)) - } - return o - case *ast.TypeSpec: - o := new(ast.TypeSpec) - *o = *v - o.Type = cloneExpr(v.Type) - return o - case *ast.InterfaceType: - o := new(ast.InterfaceType) - *o = *v - o.Methods = clone(v.Methods).(*ast.FieldList) - return o - case *ast.FieldList: - if v == nil { - return v - } - o := new(ast.FieldList) - *o = *v - o.List = nil - for _, x := range v.List { - o.List = append(o.List, clone(x).(*ast.Field)) - } - return o - case *ast.Field: - o := &ast.Field{ - Doc: v.Doc, // shallow - Type: cloneExpr(v.Type), - Tag: clone(v.Tag).(*ast.BasicLit), - Comment: v.Comment, // shallow - } - for _, x := range v.Names { - o.Names = append(o.Names, clone(x).(*ast.Ident)) - } - return o - case *ast.FuncType: - if v == nil { - return v - } - return &ast.FuncType{ - Func: v.Func, - Params: clone(v.Params).(*ast.FieldList), - Results: clone(v.Results).(*ast.FieldList), - } - case *ast.FuncDecl: - if v == nil { - return v - } - return &ast.FuncDecl{ - Recv: clone(v.Recv).(*ast.FieldList), - Name: v.Name, - Type: clone(v.Type).(*ast.FuncType), - Body: v.Body, // shallow - } - case *ast.ValueSpec: - if v == nil { - return v - } - o := &ast.ValueSpec{ - Type: cloneExpr(v.Type), - } - for _, x := range v.Names { - o.Names = append(o.Names, x) - } - for _, x := range v.Values { - o.Values = append(o.Values, cloneExpr(x)) - } - return o - case *ast.CallExpr: - if v == nil { - return v - } - o := &ast.CallExpr{} - *o = *v - o.Args = cloneExprs(v.Args) - o.Fun = cloneExpr(v.Fun) - return o - case *ast.SelectorExpr: - if v == nil { - return nil - } - return &ast.SelectorExpr{ - X: cloneExpr(v.X), - Sel: v.Sel, - } - case *ast.ArrayType: - return &ast.ArrayType{ - Lbrack: v.Lbrack, - Len: cloneExpr(v.Len), - Elt: cloneExpr(v.Elt), - } - case *ast.StructType: - return &ast.StructType{ - Struct: v.Struct, - Fields: clone(v.Fields).(*ast.FieldList), - Incomplete: v.Incomplete, - } - case *ast.StarExpr: - return &ast.StarExpr{ - Star: v.Star, - X: cloneExpr(v.X), - } - case *ast.CompositeLit: - return &ast.CompositeLit{ - Type: cloneExpr(v.Type), - Lbrace: v.Lbrace, - Elts: cloneExprs(v.Elts), - Rbrace: v.Rbrace, - } - case *ast.UnaryExpr: - return &ast.UnaryExpr{ - OpPos: v.OpPos, - Op: v.Op, - X: cloneExpr(v.X), - } - case *ast.BinaryExpr: - return &ast.BinaryExpr{ - OpPos: v.OpPos, - Op: v.Op, - X: cloneExpr(v.X), - Y: cloneExpr(v.Y), - } - case *ast.Ellipsis: - return &ast.Ellipsis{ - Ellipsis: v.Ellipsis, - Elt: cloneExpr(v.Elt), - } - case *ast.KeyValueExpr: - return &ast.KeyValueExpr{ - Key: cloneExpr(v.Key), - Colon: v.Colon, - Value: cloneExpr(v.Value), - } - case *ast.FuncLit: - return &ast.FuncLit{ - Type: clone(v.Type).(*ast.FuncType), - Body: v.Body, // shallow - } - case *ast.MapType: - return &ast.MapType{ - Map: v.Map, - Key: cloneExpr(v.Key), - Value: cloneExpr(v.Value), - } - case *ast.ParenExpr: - return &ast.ParenExpr{ - Lparen: v.Lparen, - X: cloneExpr(v.X), - Rparen: v.Rparen, - } - case *ast.Ident, *ast.BasicLit: - return v - case *ast.ImportSpec: - return &ast.ImportSpec{ - Doc: v.Doc, // shallow - Name: v.Name, - Path: clone(v.Path).(*ast.BasicLit), - Comment: v.Comment, // shallow - EndPos: v.EndPos, - } - case *ast.ChanType: - return &ast.ChanType{ - Begin: v.Begin, - Arrow: v.Arrow, - Dir: v.Dir, - Value: cloneExpr(v.Value), - } - case *ast.TypeAssertExpr: - return &ast.TypeAssertExpr{ - X: cloneExpr(v.X), - Type: cloneExpr(v.Type), - } - case *ast.IndexExpr: - return &ast.IndexExpr{ - X: cloneExpr(v.X), - Index: cloneExpr(v.Index), - Lbrack: v.Lbrack, - Rbrack: v.Rbrack, - } - } - panic(fmt.Sprintf("Uncloneable type %T", i)) -} - -func cloneExpr(x ast.Expr) ast.Expr { - if x == nil { - return nil - } - return clone(x).(ast.Expr) -} - -func cloneExprs(x []ast.Expr) []ast.Expr { - if x == nil { - return nil - } - o := make([]ast.Expr, len(x)) - for i, x := range x { - o[i] = cloneExpr(x) - } - return o -} diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go index ff75f00e3..a62c87421 100644 --- a/src/cmd/api/goapi.go +++ b/src/cmd/api/goapi.go @@ -2,38 +2,32 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Api computes the exported API of a set of Go packages. -// -// BUG(bradfitz): Note that this tool is only currently suitable -// for use on the Go standard library, not arbitrary packages. -// Once the Go AST has type information, this tool will be more -// reliable without hard-coded hacks throughout. +// +build api_tool + +// Binary api computes the exported API of a set of Go packages. package main import ( "bufio" "bytes" - "errors" "flag" "fmt" "go/ast" "go/build" - "go/doc" "go/parser" - "go/printer" "go/token" "io" "io/ioutil" "log" "os" "os/exec" - "path" "path/filepath" "regexp" "runtime" "sort" - "strconv" "strings" + + "code.google.com/p/go.tools/go/types" ) // Flags @@ -53,6 +47,7 @@ var contexts = []*build.Context{ {GOOS: "linux", GOARCH: "386"}, {GOOS: "linux", GOARCH: "amd64", CgoEnabled: true}, {GOOS: "linux", GOARCH: "amd64"}, + {GOOS: "linux", GOARCH: "arm", CgoEnabled: true}, {GOOS: "linux", GOARCH: "arm"}, {GOOS: "darwin", GOARCH: "386", CgoEnabled: true}, {GOOS: "darwin", GOARCH: "386"}, @@ -60,8 +55,22 @@ var contexts = []*build.Context{ {GOOS: "darwin", GOARCH: "amd64"}, {GOOS: "windows", GOARCH: "amd64"}, {GOOS: "windows", GOARCH: "386"}, - {GOOS: "freebsd", GOARCH: "amd64"}, + {GOOS: "freebsd", GOARCH: "386", CgoEnabled: true}, {GOOS: "freebsd", GOARCH: "386"}, + {GOOS: "freebsd", GOARCH: "amd64", CgoEnabled: true}, + {GOOS: "freebsd", GOARCH: "amd64"}, + {GOOS: "freebsd", GOARCH: "arm", CgoEnabled: true}, + {GOOS: "freebsd", GOARCH: "arm"}, + {GOOS: "netbsd", GOARCH: "386", CgoEnabled: true}, + {GOOS: "netbsd", GOARCH: "386"}, + {GOOS: "netbsd", GOARCH: "amd64", CgoEnabled: true}, + {GOOS: "netbsd", GOARCH: "amd64"}, + {GOOS: "netbsd", GOARCH: "arm", CgoEnabled: true}, + {GOOS: "netbsd", GOARCH: "arm"}, + {GOOS: "openbsd", GOARCH: "386", CgoEnabled: true}, + {GOOS: "openbsd", GOARCH: "386"}, + {GOOS: "openbsd", GOARCH: "amd64", CgoEnabled: true}, + {GOOS: "openbsd", GOARCH: "amd64"}, } func contextName(c *build.Context) string { @@ -115,35 +124,35 @@ func main() { c.Compiler = build.Default.Compiler } - var pkgs []string + var pkgNames []string if flag.NArg() > 0 { - pkgs = flag.Args() + pkgNames = flag.Args() } else { stds, err := exec.Command("go", "list", "std").Output() if err != nil { log.Fatal(err) } - pkgs = strings.Fields(string(stds)) + pkgNames = strings.Fields(string(stds)) } var featureCtx = make(map[string]map[string]bool) // feature -> context name -> true for _, context := range contexts { - w := NewWalker() - w.context = context - - for _, pkg := range pkgs { - w.wantedPkg[pkg] = true - } - - for _, pkg := range pkgs { - if strings.HasPrefix(pkg, "cmd/") { - continue - } - if fi, err := os.Stat(filepath.Join(w.root, pkg)); err != nil || !fi.IsDir() { - log.Fatalf("no source in tree for package %q", pkg) + w := NewWalker(context, filepath.Join(build.Default.GOROOT, "src/pkg")) + + for _, name := range pkgNames { + // - Package "unsafe" contains special signatures requiring + // extra care when printing them - ignore since it is not + // going to change w/o a language change. + // - We don't care about the API of commands. + if name != "unsafe" && !strings.HasPrefix(name, "cmd/") { + if name == "runtime/cgo" && !context.CgoEnabled { + // w.Import(name) will return nil + continue + } + w.export(w.Import(name)) } - w.WalkPackage(pkg) } + ctxName := contextName(context) for _, f := range w.Features() { if featureCtx[f] == nil { @@ -179,7 +188,7 @@ func main() { if *checkFile == "" { sort.Strings(features) for _, f := range features { - fmt.Fprintf(bw, "%s\n", f) + fmt.Fprintln(bw, f) } return } @@ -193,6 +202,22 @@ func main() { fail = !compareAPI(bw, features, required, optional, exception) } +// export emits the exported package features. +func (w *Walker) export(pkg *types.Package) { + if *verbose { + log.Println(pkg) + } + pop := w.pushScope("pkg " + pkg.Path()) + w.current = pkg + scope := pkg.Scope() + for _, name := range scope.Names() { + if ast.IsExported(name) { + w.emitObj(scope.Lookup(name)) + } + } + pop() +} + func set(items []string) map[string]bool { s := make(map[string]bool) for _, v := range items { @@ -231,7 +256,12 @@ func compareAPI(w io.Writer, features, required, optional, exception []string) ( case len(features) == 0 || (len(required) > 0 && required[0] < features[0]): feature := take(&required) if exceptionSet[feature] { - fmt.Fprintf(w, "~%s\n", feature) + // An "unfortunate" case: the feature was once + // included in the API (e.g. go1.txt), but was + // subsequently removed. These are already + // acknowledged by being in the file + // "api/except.txt". No need to print them out + // here. } else if featureSet[featureWithoutContext(feature)] { // okay. } else { @@ -284,53 +314,26 @@ func fileFeatures(filename string) []string { return strings.Split(text, "\n") } -// pkgSymbol represents a symbol in a package -type pkgSymbol struct { - pkg string // "net/http" - symbol string // "RoundTripper" -} - var fset = token.NewFileSet() type Walker struct { - context *build.Context - root string - scope []string - features map[string]bool // set - lastConstType string - curPackageName string - curPackage *ast.Package - prevConstType map[pkgSymbol]string - constDep map[string]string // key's const identifier has type of future value const identifier - packageState map[string]loadState - interfaces map[pkgSymbol]*ast.InterfaceType - functionTypes map[pkgSymbol]string // symbol => return type - selectorFullPkg map[string]string // "http" => "net/http", updated by imports - wantedPkg map[string]bool // packages requested on the command line + context *build.Context + root string + scope []string + current *types.Package + features map[string]bool // set + imported map[string]*types.Package // packages already imported } -func NewWalker() *Walker { +func NewWalker(context *build.Context, root string) *Walker { return &Walker{ - features: make(map[string]bool), - packageState: make(map[string]loadState), - interfaces: make(map[pkgSymbol]*ast.InterfaceType), - functionTypes: make(map[pkgSymbol]string), - selectorFullPkg: make(map[string]string), - wantedPkg: make(map[string]bool), - prevConstType: make(map[pkgSymbol]string), - root: filepath.Join(build.Default.GOROOT, "src/pkg"), + context: context, + root: root, + features: map[string]bool{}, + imported: map[string]*types.Package{"unsafe": types.Unsafe}, } } -// loadState is the state of a package's parsing. -type loadState int - -const ( - notLoaded loadState = iota - loading - loaded -) - func (w *Walker) Features() (fs []string) { for f := range w.features { fs = append(fs, f) @@ -339,127 +342,187 @@ func (w *Walker) Features() (fs []string) { return } -// fileDeps returns the imports in a file. -func fileDeps(f *ast.File) (pkgs []string) { - for _, is := range f.Imports { - fpkg, err := strconv.Unquote(is.Path.Value) +var parsedFileCache = make(map[string]*ast.File) + +func (w *Walker) parseFile(dir, file string) (*ast.File, error) { + filename := filepath.Join(dir, file) + f, _ := parsedFileCache[filename] + if f != nil { + return f, nil + } + + var err error + + // generate missing context-dependent files. + + if w.context != nil && file == fmt.Sprintf("zgoos_%s.go", w.context.GOOS) { + src := fmt.Sprintf("package runtime; const theGoos = `%s`", w.context.GOOS) + f, err = parser.ParseFile(fset, filename, src, 0) if err != nil { - log.Fatalf("error unquoting import string %q: %v", is.Path.Value, err) - } - if fpkg != "C" { - pkgs = append(pkgs, fpkg) + log.Fatalf("incorrect generated file: %s", err) } } - return -} -var parsedFileCache = make(map[string]*ast.File) + if w.context != nil && file == fmt.Sprintf("zgoarch_%s.go", w.context.GOARCH) { + src := fmt.Sprintf("package runtime; const theGoarch = `%s`", w.context.GOARCH) + f, err = parser.ParseFile(fset, filename, src, 0) + if err != nil { + log.Fatalf("incorrect generated file: %s", err) + } + } -func parseFile(filename string) (*ast.File, error) { - f, ok := parsedFileCache[filename] - if !ok { - var err error + if f == nil { f, err = parser.ParseFile(fset, filename, nil, 0) if err != nil { return nil, err } - parsedFileCache[filename] = f } - return clone(f).(*ast.File), nil + + parsedFileCache[filename] = f + return f, nil } -// WalkPackage walks all files in package `name'. -// WalkPackage does nothing if the package has already been loaded. -func (w *Walker) WalkPackage(name string) { - switch w.packageState[name] { - case loading: - log.Fatalf("import cycle loading package %q?", name) - case loaded: - return +func contains(list []string, s string) bool { + for _, t := range list { + if t == s { + return true + } } - w.packageState[name] = loading - defer func() { - w.packageState[name] = loaded - }() - dir := filepath.Join(w.root, filepath.FromSlash(name)) + return false +} - ctxt := w.context - if ctxt == nil { - ctxt = &build.Default +var ( + pkgCache = map[string]*types.Package{} // map tagKey to package + pkgTags = map[string][]string{} // map import dir to list of relevant tags +) + +// tagKey returns the tag-based key to use in the pkgCache. +// It is a comma-separated string; the first part is dir, the rest tags. +// The satisfied tags are derived from context but only those that +// matter (the ones listed in the tags argument) are used. +// The tags list, which came from go/build's Package.AllTags, +// is known to be sorted. +func tagKey(dir string, context *build.Context, tags []string) string { + ctags := map[string]bool{ + context.GOOS: true, + context.GOARCH: true, } - info, err := ctxt.ImportDir(dir, 0) - if err != nil { - if strings.Contains(err.Error(), "no Go source files") { - return - } - log.Fatalf("pkg %q, dir %q: ScanDir: %v", name, dir, err) + if context.CgoEnabled { + ctags["cgo"] = true } - - apkg := &ast.Package{ - Files: make(map[string]*ast.File), + for _, tag := range context.BuildTags { + ctags[tag] = true } - - files := append(append([]string{}, info.GoFiles...), info.CgoFiles...) - for _, file := range files { - f, err := parseFile(filepath.Join(dir, file)) - if err != nil { - log.Fatalf("error parsing package %s, file %s: %v", name, file, err) + // TODO: ReleaseTags (need to load default) + key := dir + for _, tag := range tags { + if ctags[tag] { + key += "," + tag } - apkg.Files[file] = f + } + return key +} - for _, dep := range fileDeps(f) { - w.WalkPackage(dep) +// Importing is a sentinel taking the place in Walker.imported +// for a package that is in the process of being imported. +var importing types.Package + +func (w *Walker) Import(name string) (pkg *types.Package) { + pkg = w.imported[name] + if pkg != nil { + if pkg == &importing { + log.Fatalf("cycle importing package %q", name) } + return pkg } + w.imported[name] = &importing - if *verbose { - log.Printf("package %s", name) + // Determine package files. + dir := filepath.Join(w.root, filepath.FromSlash(name)) + if fi, err := os.Stat(dir); err != nil || !fi.IsDir() { + log.Fatalf("no source in tree for package %q", pkg) } - pop := w.pushScope("pkg " + name) - defer pop() - w.curPackageName = name - w.curPackage = apkg - w.constDep = map[string]string{} + context := w.context + if context == nil { + context = &build.Default + } - for _, afile := range apkg.Files { - w.recordTypes(afile) + // Look in cache. + // If we've already done an import with the same set + // of relevant tags, reuse the result. + var key string + if tags, ok := pkgTags[dir]; ok { + key = tagKey(dir, context, tags) + if pkg := pkgCache[key]; pkg != nil { + w.imported[name] = pkg + return pkg + } } - // Register all function declarations first. - for _, afile := range apkg.Files { - for _, di := range afile.Decls { - if d, ok := di.(*ast.FuncDecl); ok { - w.peekFuncDecl(d) - } + info, err := context.ImportDir(dir, 0) + if err != nil { + if _, nogo := err.(*build.NoGoError); nogo { + return } + log.Fatalf("pkg %q, dir %q: ScanDir: %v", name, dir, err) } - for _, afile := range apkg.Files { - w.walkFile(afile) + // Save tags list first time we see a directory. + if _, ok := pkgTags[dir]; !ok { + pkgTags[dir] = info.AllTags + key = tagKey(dir, context, info.AllTags) } - w.resolveConstantDeps() + filenames := append(append([]string{}, info.GoFiles...), info.CgoFiles...) - // Now that we're done walking types, vars and consts - // in the *ast.Package, use go/doc to do the rest - // (functions and methods). This is done here because - // go/doc is destructive. We can't use the - // *ast.Package after this. - dpkg := doc.New(apkg, name, doc.AllMethods) + // Certain files only exist when building for the specified context. + // Add them manually. + if name == "runtime" { + n := fmt.Sprintf("zgoos_%s.go", w.context.GOOS) + if !contains(filenames, n) { + filenames = append(filenames, n) + } - for _, t := range dpkg.Types { - // Move funcs up to the top-level, not hiding in the Types. - dpkg.Funcs = append(dpkg.Funcs, t.Funcs...) + n = fmt.Sprintf("zgoarch_%s.go", w.context.GOARCH) + if !contains(filenames, n) { + filenames = append(filenames, n) + } + } - for _, m := range t.Methods { - w.walkFuncDecl(m.Decl) + // Parse package files. + var files []*ast.File + for _, file := range filenames { + f, err := w.parseFile(dir, file) + if err != nil { + log.Fatalf("error parsing package %s: %s", name, err) } + files = append(files, f) } - for _, f := range dpkg.Funcs { - w.walkFuncDecl(f.Decl) + // Type-check package files. + conf := types.Config{ + IgnoreFuncBodies: true, + FakeImportC: true, + Import: func(imports map[string]*types.Package, name string) (*types.Package, error) { + pkg := w.Import(name) + imports[name] = pkg + return pkg, nil + }, + } + pkg, err = conf.Check(name, fset, files, nil) + if err != nil { + ctxt := "<no context>" + if w.context != nil { + ctxt = fmt.Sprintf("%s-%s", w.context.GOOS, w.context.GOARCH) + } + log.Fatalf("error typechecking package %s: %s (%s)", name, err, ctxt) } + + pkgCache[key] = pkg + + w.imported[name] = pkg + return } // pushScope enters a new scope (walking a package, type, node, etc) @@ -478,707 +541,307 @@ func (w *Walker) pushScope(name string) (popFunc func()) { } } -func (w *Walker) recordTypes(file *ast.File) { - for _, di := range file.Decls { - switch d := di.(type) { - case *ast.GenDecl: - switch d.Tok { - case token.TYPE: - for _, sp := range d.Specs { - ts := sp.(*ast.TypeSpec) - name := ts.Name.Name - if ast.IsExported(name) { - if it, ok := ts.Type.(*ast.InterfaceType); ok { - w.noteInterface(name, it) - } - } - } - } - } - } -} - -func (w *Walker) walkFile(file *ast.File) { - // Not entering a scope here; file boundaries aren't interesting. - for _, di := range file.Decls { - switch d := di.(type) { - case *ast.GenDecl: - switch d.Tok { - case token.IMPORT: - for _, sp := range d.Specs { - is := sp.(*ast.ImportSpec) - fpath, err := strconv.Unquote(is.Path.Value) - if err != nil { - log.Fatal(err) - } - name := path.Base(fpath) - if is.Name != nil { - name = is.Name.Name - } - w.selectorFullPkg[name] = fpath - } - case token.CONST: - for _, sp := range d.Specs { - w.walkConst(sp.(*ast.ValueSpec)) - } - case token.TYPE: - for _, sp := range d.Specs { - w.walkTypeSpec(sp.(*ast.TypeSpec)) - } - case token.VAR: - for _, sp := range d.Specs { - w.walkVar(sp.(*ast.ValueSpec)) - } - default: - log.Fatalf("unknown token type %d in GenDecl", d.Tok) - } - case *ast.FuncDecl: - // Ignore. Handled in subsequent pass, by go/doc. +func sortedMethodNames(typ *types.Interface) []string { + n := typ.NumMethods() + list := make([]string, n) + for i := range list { + list[i] = typ.Method(i).Name() + } + sort.Strings(list) + return list +} + +func (w *Walker) writeType(buf *bytes.Buffer, typ types.Type) { + switch typ := typ.(type) { + case *types.Basic: + s := typ.Name() + switch typ.Kind() { + case types.UnsafePointer: + s = "unsafe.Pointer" + case types.UntypedBool: + s = "ideal-bool" + case types.UntypedInt: + s = "ideal-int" + case types.UntypedRune: + // "ideal-char" for compatibility with old tool + // TODO(gri) change to "ideal-rune" + s = "ideal-char" + case types.UntypedFloat: + s = "ideal-float" + case types.UntypedComplex: + s = "ideal-complex" + case types.UntypedString: + s = "ideal-string" + case types.UntypedNil: + panic("should never see untyped nil type") + default: + switch s { + case "byte": + s = "uint8" + case "rune": + s = "int32" + } + } + buf.WriteString(s) + + case *types.Array: + fmt.Fprintf(buf, "[%d]", typ.Len()) + w.writeType(buf, typ.Elem()) + + case *types.Slice: + buf.WriteString("[]") + w.writeType(buf, typ.Elem()) + + case *types.Struct: + buf.WriteString("struct") + + case *types.Pointer: + buf.WriteByte('*') + w.writeType(buf, typ.Elem()) + + case *types.Tuple: + panic("should never see a tuple type") + + case *types.Signature: + buf.WriteString("func") + w.writeSignature(buf, typ) + + case *types.Interface: + buf.WriteString("interface{") + if typ.NumMethods() > 0 { + buf.WriteByte(' ') + buf.WriteString(strings.Join(sortedMethodNames(typ), ", ")) + buf.WriteByte(' ') + } + buf.WriteString("}") + + case *types.Map: + buf.WriteString("map[") + w.writeType(buf, typ.Key()) + buf.WriteByte(']') + w.writeType(buf, typ.Elem()) + + case *types.Chan: + var s string + switch typ.Dir() { + case ast.SEND: + s = "chan<- " + case ast.RECV: + s = "<-chan " default: - log.Printf("unhandled %T, %#v\n", di, di) - printer.Fprint(os.Stderr, fset, di) - os.Stderr.Write([]byte("\n")) + s = "chan " } - } -} - -var constType = map[token.Token]string{ - token.INT: "ideal-int", - token.FLOAT: "ideal-float", - token.STRING: "ideal-string", - token.CHAR: "ideal-char", - token.IMAG: "ideal-imag", -} - -var varType = map[token.Token]string{ - token.INT: "int", - token.FLOAT: "float64", - token.STRING: "string", - token.CHAR: "rune", - token.IMAG: "complex128", -} - -var errTODO = errors.New("TODO") + buf.WriteString(s) + w.writeType(buf, typ.Elem()) -func (w *Walker) constValueType(vi interface{}) (string, error) { - switch v := vi.(type) { - case *ast.BasicLit: - litType, ok := constType[v.Kind] - if !ok { - return "", fmt.Errorf("unknown basic literal kind %#v", v) + case *types.Named: + obj := typ.Obj() + pkg := obj.Pkg() + if pkg != nil && pkg != w.current { + buf.WriteString(pkg.Name()) + buf.WriteByte('.') } - return litType, nil - case *ast.UnaryExpr: - return w.constValueType(v.X) - case *ast.SelectorExpr: - lhs := w.nodeString(v.X) - rhs := w.nodeString(v.Sel) - pkg, ok := w.selectorFullPkg[lhs] - if !ok { - return "", fmt.Errorf("unknown constant reference; unknown package in expression %s.%s", lhs, rhs) - } - if t, ok := w.prevConstType[pkgSymbol{pkg, rhs}]; ok { - return t, nil - } - return "", fmt.Errorf("unknown constant reference to %s.%s", lhs, rhs) - case *ast.Ident: - if v.Name == "iota" { - return "ideal-int", nil // hack. - } - if v.Name == "false" || v.Name == "true" { - return "bool", nil - } - if v.Name == "intSize" && w.curPackageName == "strconv" { - // Hack. - return "ideal-int", nil - } - if t, ok := w.prevConstType[pkgSymbol{w.curPackageName, v.Name}]; ok { - return t, nil - } - return constDepPrefix + v.Name, nil - case *ast.BinaryExpr: - switch v.Op { - case token.EQL, token.LSS, token.GTR, token.NOT, token.NEQ, token.LEQ, token.GEQ: - return "bool", nil - } - left, err := w.constValueType(v.X) - if err != nil { - return "", err - } - right, err := w.constValueType(v.Y) - if err != nil { - return "", err - } - if left != right { - // TODO(bradfitz): encode the real rules here, - // rather than this mess. - if left == "ideal-int" && right == "ideal-float" { - return "ideal-float", nil // math.Log2E - } - if left == "ideal-char" && right == "ideal-int" { - return "ideal-int", nil // math/big.MaxBase - } - if left == "ideal-int" && right == "ideal-char" { - return "ideal-int", nil // text/scanner.GoWhitespace - } - if left == "ideal-int" && right == "Duration" { - // Hack, for package time. - return "Duration", nil - } - if left == "ideal-int" && !strings.HasPrefix(right, "ideal-") { - return right, nil - } - if right == "ideal-int" && !strings.HasPrefix(left, "ideal-") { - return left, nil - } - if strings.HasPrefix(left, constDepPrefix) && strings.HasPrefix(right, constDepPrefix) { - // Just pick one. - // e.g. text/scanner GoTokens const-dependency:ScanIdents, const-dependency:ScanFloats - return left, nil - } - return "", fmt.Errorf("in BinaryExpr, unhandled type mismatch; left=%q, right=%q", left, right) - } - return left, nil - case *ast.CallExpr: - // Not a call, but a type conversion. - return w.nodeString(v.Fun), nil - case *ast.ParenExpr: - return w.constValueType(v.X) - } - return "", fmt.Errorf("unknown const value type %T", vi) -} + buf.WriteString(typ.Obj().Name()) -func (w *Walker) varValueType(vi interface{}) (string, error) { - switch v := vi.(type) { - case *ast.BasicLit: - litType, ok := varType[v.Kind] - if !ok { - return "", fmt.Errorf("unknown basic literal kind %#v", v) - } - return litType, nil - case *ast.CompositeLit: - return w.nodeString(v.Type), nil - case *ast.FuncLit: - return w.nodeString(w.namelessType(v.Type)), nil - case *ast.UnaryExpr: - if v.Op == token.AND { - typ, err := w.varValueType(v.X) - return "*" + typ, err - } - return "", fmt.Errorf("unknown unary expr: %#v", v) - case *ast.SelectorExpr: - return "", errTODO - case *ast.Ident: - node, _, ok := w.resolveName(v.Name) - if !ok { - return "", fmt.Errorf("unresolved identifier: %q", v.Name) - } - return w.varValueType(node) - case *ast.BinaryExpr: - left, err := w.varValueType(v.X) - if err != nil { - return "", err - } - right, err := w.varValueType(v.Y) - if err != nil { - return "", err - } - if left != right { - return "", fmt.Errorf("in BinaryExpr, unhandled type mismatch; left=%q, right=%q", left, right) - } - return left, nil - case *ast.ParenExpr: - return w.varValueType(v.X) - case *ast.CallExpr: - var funSym pkgSymbol - if selnode, ok := v.Fun.(*ast.SelectorExpr); ok { - // assume it is not a method. - pkg, ok := w.selectorFullPkg[w.nodeString(selnode.X)] - if !ok { - return "", fmt.Errorf("not a package: %s", w.nodeString(selnode.X)) - } - funSym = pkgSymbol{pkg, selnode.Sel.Name} - if retType, ok := w.functionTypes[funSym]; ok { - if ast.IsExported(retType) && pkg != w.curPackageName { - // otherpkg.F returning an exported type from otherpkg. - return pkg + "." + retType, nil - } else { - return retType, nil - } - } - } else { - funSym = pkgSymbol{w.curPackageName, w.nodeString(v.Fun)} - if retType, ok := w.functionTypes[funSym]; ok { - return retType, nil - } - } - // maybe a function call; maybe a conversion. Need to lookup type. - // TODO(bradfitz): this is a hack, but arguably most of this tool is, - // until the Go AST has type information. - nodeStr := w.nodeString(v.Fun) - switch nodeStr { - case "string", "[]byte": - return nodeStr, nil - } - return "", fmt.Errorf("not a known function %q", nodeStr) default: - return "", fmt.Errorf("unknown const value type %T", vi) + panic(fmt.Sprintf("unknown type %T", typ)) } } -// resolveName finds a top-level node named name and returns the node -// v and its type t, if known. -func (w *Walker) resolveName(name string) (v interface{}, t interface{}, ok bool) { - for _, file := range w.curPackage.Files { - for _, di := range file.Decls { - switch d := di.(type) { - case *ast.GenDecl: - switch d.Tok { - case token.VAR: - for _, sp := range d.Specs { - vs := sp.(*ast.ValueSpec) - for i, vname := range vs.Names { - if vname.Name == name { - if len(vs.Values) > i { - return vs.Values[i], vs.Type, true - } - return nil, vs.Type, true - } - } - } - } - } - } +func (w *Walker) writeSignature(buf *bytes.Buffer, sig *types.Signature) { + w.writeParams(buf, sig.Params(), sig.IsVariadic()) + switch res := sig.Results(); res.Len() { + case 0: + // nothing to do + case 1: + buf.WriteByte(' ') + w.writeType(buf, res.At(0).Type()) + default: + buf.WriteByte(' ') + w.writeParams(buf, res, false) } - return nil, nil, false } -// constDepPrefix is a magic prefix that is used by constValueType -// and walkConst to signal that a type isn't known yet. These are -// resolved at the end of walking of a package's files. -const constDepPrefix = "const-dependency:" - -func (w *Walker) walkConst(vs *ast.ValueSpec) { - for _, ident := range vs.Names { - litType := "" - if vs.Type != nil { - litType = w.nodeString(vs.Type) - } else { - litType = w.lastConstType - if vs.Values != nil { - if len(vs.Values) != 1 { - log.Fatalf("const %q, values: %#v", ident.Name, vs.Values) - } - var err error - litType, err = w.constValueType(vs.Values[0]) - if err != nil { - log.Fatalf("unknown kind in const %q (%T): %v", ident.Name, vs.Values[0], err) - } - } - } - if dep := strings.TrimPrefix(litType, constDepPrefix); dep != litType { - w.constDep[ident.Name] = dep - continue - } - if litType == "" { - log.Fatalf("unknown kind in const %q", ident.Name) +func (w *Walker) writeParams(buf *bytes.Buffer, t *types.Tuple, variadic bool) { + buf.WriteByte('(') + for i, n := 0, t.Len(); i < n; i++ { + if i > 0 { + buf.WriteString(", ") } - w.lastConstType = litType - - w.prevConstType[pkgSymbol{w.curPackageName, ident.Name}] = litType - - if ast.IsExported(ident.Name) { - w.emitFeature(fmt.Sprintf("const %s %s", ident, litType)) + typ := t.At(i).Type() + if variadic && i+1 == n { + buf.WriteString("...") + typ = typ.(*types.Slice).Elem() } + w.writeType(buf, typ) } + buf.WriteByte(')') } -func (w *Walker) resolveConstantDeps() { - var findConstType func(string) string - findConstType = func(ident string) string { - if dep, ok := w.constDep[ident]; ok { - return findConstType(dep) - } - if t, ok := w.prevConstType[pkgSymbol{w.curPackageName, ident}]; ok { - return t - } - return "" - } - for ident := range w.constDep { - if !ast.IsExported(ident) { - continue - } - t := findConstType(ident) - if t == "" { - log.Fatalf("failed to resolve constant %q", ident) - } - w.emitFeature(fmt.Sprintf("const %s %s", ident, t)) - } +func (w *Walker) typeString(typ types.Type) string { + var buf bytes.Buffer + w.writeType(&buf, typ) + return buf.String() } -func (w *Walker) walkVar(vs *ast.ValueSpec) { - for i, ident := range vs.Names { - if !ast.IsExported(ident.Name) { - continue - } - - typ := "" - if vs.Type != nil { - typ = w.nodeString(vs.Type) - } else { - if len(vs.Values) == 0 { - log.Fatalf("no values for var %q", ident.Name) - } - if len(vs.Values) > 1 { - log.Fatalf("more than 1 values in ValueSpec not handled, var %q", ident.Name) - } - var err error - typ, err = w.varValueType(vs.Values[i]) - if err != nil { - log.Fatalf("unknown type of variable %q, type %T, error = %v\ncode: %s", - ident.Name, vs.Values[i], err, w.nodeString(vs.Values[i])) - } - } - w.emitFeature(fmt.Sprintf("var %s %s", ident, typ)) - } -} - -func (w *Walker) nodeString(node interface{}) string { - if node == nil { - return "" - } - var b bytes.Buffer - printer.Fprint(&b, fset, node) - return b.String() +func (w *Walker) signatureString(sig *types.Signature) string { + var buf bytes.Buffer + w.writeSignature(&buf, sig) + return buf.String() } -func (w *Walker) nodeDebug(node interface{}) string { - if node == nil { - return "" +func (w *Walker) emitObj(obj types.Object) { + switch obj := obj.(type) { + case *types.Const: + w.emitf("const %s %s", obj.Name(), w.typeString(obj.Type())) + w.emitf("const %s = %s", obj.Name(), obj.Val()) + case *types.Var: + w.emitf("var %s %s", obj.Name(), w.typeString(obj.Type())) + case *types.TypeName: + w.emitType(obj) + case *types.Func: + w.emitFunc(obj) + default: + panic("unknown object: " + obj.String()) } - var b bytes.Buffer - ast.Fprint(&b, fset, node, nil) - return b.String() -} - -func (w *Walker) noteInterface(name string, it *ast.InterfaceType) { - w.interfaces[pkgSymbol{w.curPackageName, name}] = it } -func (w *Walker) walkTypeSpec(ts *ast.TypeSpec) { - name := ts.Name.Name - if !ast.IsExported(name) { - return - } - switch t := ts.Type.(type) { - case *ast.StructType: - w.walkStructType(name, t) - case *ast.InterfaceType: - w.walkInterfaceType(name, t) +func (w *Walker) emitType(obj *types.TypeName) { + name := obj.Name() + typ := obj.Type() + switch typ := typ.Underlying().(type) { + case *types.Struct: + w.emitStructType(name, typ) + case *types.Interface: + w.emitIfaceType(name, typ) + return // methods are handled by emitIfaceType default: - w.emitFeature(fmt.Sprintf("type %s %s", name, w.nodeString(w.namelessType(ts.Type)))) + w.emitf("type %s %s", name, w.typeString(typ.Underlying())) } -} -func (w *Walker) walkStructType(name string, t *ast.StructType) { - typeStruct := fmt.Sprintf("type %s struct", name) - w.emitFeature(typeStruct) - pop := w.pushScope(typeStruct) - defer pop() - for _, f := range t.Fields.List { - typ := f.Type - for _, name := range f.Names { - if ast.IsExported(name.Name) { - w.emitFeature(fmt.Sprintf("%s %s", name, w.nodeString(w.namelessType(typ)))) + // emit methods with value receiver + var methodNames map[string]bool + vset := typ.MethodSet() + for i, n := 0, vset.Len(); i < n; i++ { + m := vset.At(i) + if m.Obj().IsExported() { + w.emitMethod(m) + if methodNames == nil { + methodNames = make(map[string]bool) } + methodNames[m.Obj().Name()] = true } - if f.Names == nil { - switch v := typ.(type) { - case *ast.Ident: - if ast.IsExported(v.Name) { - w.emitFeature(fmt.Sprintf("embedded %s", v.Name)) - } - case *ast.StarExpr: - switch vv := v.X.(type) { - case *ast.Ident: - if ast.IsExported(vv.Name) { - w.emitFeature(fmt.Sprintf("embedded *%s", vv.Name)) - } - case *ast.SelectorExpr: - w.emitFeature(fmt.Sprintf("embedded %s", w.nodeString(typ))) - default: - log.Fatalf("unable to handle embedded starexpr before %T", typ) - } - case *ast.SelectorExpr: - w.emitFeature(fmt.Sprintf("embedded %s", w.nodeString(typ))) - default: - log.Fatalf("unable to handle embedded %T", typ) - } + } + + // emit methods with pointer receiver; exclude + // methods that we have emitted already + // (the method set of *T includes the methods of T) + pset := types.NewPointer(typ).MethodSet() + for i, n := 0, pset.Len(); i < n; i++ { + m := pset.At(i) + if m.Obj().IsExported() && !methodNames[m.Obj().Name()] { + w.emitMethod(m) } } } -// method is a method of an interface. -type method struct { - name string // "Read" - sig string // "([]byte) (int, error)", from funcSigString -} +func (w *Walker) emitStructType(name string, typ *types.Struct) { + typeStruct := fmt.Sprintf("type %s struct", name) + w.emitf(typeStruct) + defer w.pushScope(typeStruct)() -// interfaceMethods returns the expanded list of exported methods for an interface. -// The boolean complete reports whether the list contains all methods (that is, the -// interface has no unexported methods). -// pkg is the complete package name ("net/http") -// iname is the interface name. -func (w *Walker) interfaceMethods(pkg, iname string) (methods []method, complete bool) { - t, ok := w.interfaces[pkgSymbol{pkg, iname}] - if !ok { - log.Fatalf("failed to find interface %s.%s", pkg, iname) - } - - complete = true - for _, f := range t.Methods.List { - typ := f.Type - switch tv := typ.(type) { - case *ast.FuncType: - for _, mname := range f.Names { - if ast.IsExported(mname.Name) { - ft := typ.(*ast.FuncType) - methods = append(methods, method{ - name: mname.Name, - sig: w.funcSigString(ft), - }) - } else { - complete = false - } - } - case *ast.Ident: - embedded := typ.(*ast.Ident).Name - if embedded == "error" { - methods = append(methods, method{ - name: "Error", - sig: "() string", - }) - continue - } - if !ast.IsExported(embedded) { - log.Fatalf("unexported embedded interface %q in exported interface %s.%s; confused", - embedded, pkg, iname) - } - m, c := w.interfaceMethods(pkg, embedded) - methods = append(methods, m...) - complete = complete && c - case *ast.SelectorExpr: - lhs := w.nodeString(tv.X) - rhs := w.nodeString(tv.Sel) - fpkg, ok := w.selectorFullPkg[lhs] - if !ok { - log.Fatalf("can't resolve selector %q in interface %s.%s", lhs, pkg, iname) - } - m, c := w.interfaceMethods(fpkg, rhs) - methods = append(methods, m...) - complete = complete && c - default: - log.Fatalf("unknown type %T in interface field", typ) + for i := 0; i < typ.NumFields(); i++ { + f := typ.Field(i) + if !f.IsExported() { + continue + } + typ := f.Type() + if f.Anonymous() { + w.emitf("embedded %s", w.typeString(typ)) + continue } + w.emitf("%s %s", f.Name(), w.typeString(typ)) } - return } -func (w *Walker) walkInterfaceType(name string, t *ast.InterfaceType) { - methNames := []string{} +func (w *Walker) emitIfaceType(name string, typ *types.Interface) { pop := w.pushScope("type " + name + " interface") - methods, complete := w.interfaceMethods(w.curPackageName, name) - for _, m := range methods { - methNames = append(methNames, m.name) - w.emitFeature(fmt.Sprintf("%s%s", m.name, m.sig)) + + var methodNames []string + complete := true + mset := typ.MethodSet() + for i, n := 0, mset.Len(); i < n; i++ { + m := mset.At(i).Obj().(*types.Func) + if !m.IsExported() { + complete = false + continue + } + methodNames = append(methodNames, m.Name()) + w.emitf("%s%s", m.Name(), w.signatureString(m.Type().(*types.Signature))) } + if !complete { // The method set has unexported methods, so all the // implementations are provided by the same package, // so the method set can be extended. Instead of recording // the full set of names (below), record only that there were // unexported methods. (If the interface shrinks, we will notice - // because a method signature emitted during the last loop, + // because a method signature emitted during the last loop // will disappear.) - w.emitFeature("unexported methods") + w.emitf("unexported methods") } + pop() if !complete { return } - sort.Strings(methNames) - if len(methNames) == 0 { - w.emitFeature(fmt.Sprintf("type %s interface {}", name)) - } else { - w.emitFeature(fmt.Sprintf("type %s interface { %s }", name, strings.Join(methNames, ", "))) - } -} - -func (w *Walker) peekFuncDecl(f *ast.FuncDecl) { - if f.Recv != nil { + if len(methodNames) == 0 { + w.emitf("type %s interface {}", name) return } - // Record return type for later use. - if f.Type.Results != nil && len(f.Type.Results.List) == 1 { - retType := w.nodeString(w.namelessType(f.Type.Results.List[0].Type)) - w.functionTypes[pkgSymbol{w.curPackageName, f.Name.Name}] = retType - } -} -func (w *Walker) walkFuncDecl(f *ast.FuncDecl) { - if !ast.IsExported(f.Name.Name) { - return - } - if f.Recv != nil { - // Method. - recvType := w.nodeString(f.Recv.List[0].Type) - keep := ast.IsExported(recvType) || - (strings.HasPrefix(recvType, "*") && - ast.IsExported(recvType[1:])) - if !keep { - return - } - w.emitFeature(fmt.Sprintf("method (%s) %s%s", recvType, f.Name.Name, w.funcSigString(f.Type))) - return - } - // Else, a function - w.emitFeature(fmt.Sprintf("func %s%s", f.Name.Name, w.funcSigString(f.Type))) -} - -func (w *Walker) funcSigString(ft *ast.FuncType) string { - var b bytes.Buffer - writeField := func(b *bytes.Buffer, f *ast.Field) { - if n := len(f.Names); n > 1 { - for i := 0; i < n; i++ { - if i > 0 { - b.WriteString(", ") - } - b.WriteString(w.nodeString(w.namelessType(f.Type))) - } - } else { - b.WriteString(w.nodeString(w.namelessType(f.Type))) - } - } - b.WriteByte('(') - if ft.Params != nil { - for i, f := range ft.Params.List { - if i > 0 { - b.WriteString(", ") - } - writeField(&b, f) - } - } - b.WriteByte(')') - if ft.Results != nil { - nr := 0 - for _, f := range ft.Results.List { - if n := len(f.Names); n > 1 { - nr += n - } else { - nr++ - } - } - if nr > 0 { - b.WriteByte(' ') - if nr > 1 { - b.WriteByte('(') - } - for i, f := range ft.Results.List { - if i > 0 { - b.WriteString(", ") - } - writeField(&b, f) - } - if nr > 1 { - b.WriteByte(')') - } - } - } - return b.String() + sort.Strings(methodNames) + w.emitf("type %s interface { %s }", name, strings.Join(methodNames, ", ")) } -// namelessType returns a type node that lacks any variable names. -func (w *Walker) namelessType(t interface{}) interface{} { - ft, ok := t.(*ast.FuncType) - if !ok { - return t - } - return &ast.FuncType{ - Params: w.namelessFieldList(ft.Params), - Results: w.namelessFieldList(ft.Results), +func (w *Walker) emitFunc(f *types.Func) { + sig := f.Type().(*types.Signature) + if sig.Recv() != nil { + panic("method considered a regular function: " + f.String()) } + w.emitf("func %s%s", f.Name(), w.signatureString(sig)) } -// namelessFieldList returns a deep clone of fl, with the cloned fields -// lacking names. -func (w *Walker) namelessFieldList(fl *ast.FieldList) *ast.FieldList { - fl2 := &ast.FieldList{} - if fl != nil { - for _, f := range fl.List { - repeats := 1 - if len(f.Names) > 1 { - repeats = len(f.Names) - } - for i := 0; i < repeats; i++ { - fl2.List = append(fl2.List, w.namelessField(f)) - } +func (w *Walker) emitMethod(m *types.Selection) { + sig := m.Type().(*types.Signature) + recv := sig.Recv().Type() + // report exported methods with unexported reveiver base type + if true { + base := recv + if p, _ := recv.(*types.Pointer); p != nil { + base = p.Elem() + } + if obj := base.(*types.Named).Obj(); !obj.IsExported() { + log.Fatalf("exported method with unexported receiver base type: %s", m) } } - return fl2 + w.emitf("method (%s) %s%s", w.typeString(recv), m.Obj().Name(), w.signatureString(sig)) } -// namelessField clones f, but not preserving the names of fields. -// (comments and tags are also ignored) -func (w *Walker) namelessField(f *ast.Field) *ast.Field { - return &ast.Field{ - Type: f.Type, +func (w *Walker) emitf(format string, args ...interface{}) { + f := strings.Join(w.scope, ", ") + ", " + fmt.Sprintf(format, args...) + if strings.Contains(f, "\n") { + panic("feature contains newlines: " + f) } -} - -var ( - byteRx = regexp.MustCompile(`\bbyte\b`) - runeRx = regexp.MustCompile(`\brune\b`) -) -func (w *Walker) emitFeature(feature string) { - if !w.wantedPkg[w.curPackageName] { - return - } - if strings.Contains(feature, "byte") { - feature = byteRx.ReplaceAllString(feature, "uint8") - } - if strings.Contains(feature, "rune") { - feature = runeRx.ReplaceAllString(feature, "int32") - } - f := strings.Join(w.scope, ", ") + ", " + feature if _, dup := w.features[f]; dup { panic("duplicate feature inserted: " + f) } - - if strings.Contains(f, "\n") { - // TODO: for now, just skip over the - // runtime.MemStatsType.BySize type, which this tool - // doesn't properly handle. It's pretty low-level, - // though, so not super important to protect against. - if strings.HasPrefix(f, "pkg runtime") && strings.Contains(f, "BySize [61]struct") { - return - } - panic("feature contains newlines: " + f) - } - w.features[f] = true + if *verbose { log.Printf("feature: %s", f) } } - -func strListContains(l []string, s string) bool { - for _, v := range l { - if v == s { - return true - } - } - return false -} diff --git a/src/cmd/api/goapi_test.go b/src/cmd/api/goapi_test.go index 1a86c0ec7..b909c32b3 100644 --- a/src/cmd/api/goapi_test.go +++ b/src/cmd/api/goapi_test.go @@ -1,3 +1,5 @@ +// +build api_tool + // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -8,8 +10,10 @@ import ( "bytes" "flag" "fmt" + "go/build" "io/ioutil" "os" + "os/exec" "path/filepath" "sort" "strings" @@ -33,12 +37,10 @@ func TestGolden(t *testing.T) { if !fi.IsDir() { continue } - w := NewWalker() - w.wantedPkg[fi.Name()] = true - w.root = "testdata/src/pkg" goldenFile := filepath.Join("testdata", "src", "pkg", fi.Name(), "golden.txt") - w.WalkPackage(fi.Name()) + w := NewWalker(nil, "testdata/src/pkg") + w.export(w.Import(fi.Name())) if *updateGolden { os.Remove(goldenFile) @@ -110,7 +112,7 @@ func TestCompareAPI(t *testing.T) { features: []string{"A", "C"}, exception: []string{"B"}, ok: true, - out: "~B\n", + out: "", }, { // http://golang.org/issue/4303 @@ -139,3 +141,28 @@ func TestCompareAPI(t *testing.T) { } } } + +func BenchmarkAll(b *testing.B) { + stds, err := exec.Command("go", "list", "std").Output() + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + pkgNames := strings.Fields(string(stds)) + + for _, c := range contexts { + c.Compiler = build.Default.Compiler + } + + for i := 0; i < b.N; i++ { + for _, context := range contexts { + w := NewWalker(context, filepath.Join(build.Default.GOROOT, "src/pkg")) + for _, name := range pkgNames { + if name != "unsafe" && !strings.HasPrefix(name, "cmd/") { + w.export(w.Import(name)) + } + } + w.Features() + } + } +} diff --git a/src/cmd/api/run.go b/src/cmd/api/run.go new file mode 100644 index 000000000..1e10dc600 --- /dev/null +++ b/src/cmd/api/run.go @@ -0,0 +1,186 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// The run program is invoked via "go run" from src/run.bash or +// src/run.bat conditionally builds and runs the cmd/api tool. +// +// TODO(bradfitz): the "conditional" condition is always true. +// We should only do this if the user has the hg codereview extension +// enabled and verifies that the go.tools subrepo is checked out with +// a suitably recently version. In prep for the cmd/api rewrite. +package main + +import ( + "fmt" + "log" + "net/http" + "os" + "os/exec" + "os/user" + "path/filepath" + "strings" +) + +// goToolsVersion is the hg revision of the go.tools subrepo we need +// to build cmd/api. This only needs to be updated whenever a go/types +// bug fix is needed by the cmd/api tool. +const goToolsVersion = "6698ca2900e2" + +var goroot string + +func main() { + log.SetFlags(0) + goroot = os.Getenv("GOROOT") // should be set by run.{bash,bat} + if goroot == "" { + log.Fatal("No $GOROOT set.") + } + _, err := exec.LookPath("hg") + if err != nil { + fmt.Println("Skipping cmd/api checks; hg not available") + return + } + + gopath := prepGoPath() + + cmd := exec.Command("go", "install", "--tags=api_tool", "cmd/api") + cmd.Env = append([]string{"GOPATH=" + gopath}, filterOut(os.Environ(), "GOARCH")...) + out, err := cmd.CombinedOutput() + if err != nil { + log.Fatalf("Error installing cmd/api: %v\n%s", err, out) + } + + out, err = exec.Command("go", "tool", "api", + "-c", file("go1", "go1.1", "go1.2"), + "-next", file("next"), + "-except", file("except")).CombinedOutput() + if err != nil { + log.Fatalf("Error running API checker: %v\n%s", err, out) + } + fmt.Print(string(out)) +} + +// filterOut returns a copy of the src environment without environment +// variables from remove. +// TODO: delete when issue 6201 is fixed. +func filterOut(src []string, remove ...string) (out []string) { +S: + for _, s := range src { + for _, r := range remove { + if strings.HasPrefix(s, r) && strings.HasPrefix(s, r+"=") { + continue S + } + } + out = append(out, s) + } + return +} + +// file expands s to $GOROOT/api/s.txt. +// If there are more than 1, they're comma-separated. +func file(s ...string) string { + if len(s) > 1 { + return file(s[0]) + "," + file(s[1:]...) + } + return filepath.Join(goroot, "api", s[0]+".txt") +} + +// prepGoPath returns a GOPATH for the "go" tool to compile the API tool with. +// It tries to re-use a go.tools checkout from a previous run if possible, +// else it hg clones it. +func prepGoPath() string { + const tempBase = "go.tools.TMP" + + username := "" + u, err := user.Current() + if err == nil { + username = u.Username + } else { + // Only need to handle Unix here, as Windows's os/user uses + // native syscall and should work fine without cgo. + username = os.Getenv("USER") + if username == "" { + log.Fatalf("Error getting current user: %v", err) + } + } + + // The GOPATH we'll return + gopath := filepath.Join(os.TempDir(), "gopath-api-"+cleanUsername(username), goToolsVersion) + + // cloneDir is where we run "hg clone". + cloneDir := filepath.Join(gopath, "src", "code.google.com", "p") + + // The dir we clone into. We only atomically rename it to finalDir on + // clone success. + tmpDir := filepath.Join(cloneDir, tempBase) + + // finalDir is where the checkout will live once it's complete. + finalDir := filepath.Join(cloneDir, "go.tools") + + if goToolsCheckoutGood(finalDir) { + return gopath + } + os.RemoveAll(finalDir) // in case it's there but corrupt + os.RemoveAll(tmpDir) // in case of aborted hg clone before + + if err := os.MkdirAll(cloneDir, 0700); err != nil { + log.Fatal(err) + } + cmd := exec.Command("hg", + "clone", "--rev="+goToolsVersion, + "https://code.google.com/p/go.tools", + tempBase) + cmd.Dir = cloneDir + out, err := cmd.CombinedOutput() + if err != nil { + if _, err := http.Head("http://ip.appspot.com/"); err != nil { + log.Printf("# Skipping API check; network appears to be unavailable") + os.Exit(0) + } + log.Fatalf("Error running hg clone on go.tools: %v\n%s", err, out) + } + if err := os.Rename(tmpDir, finalDir); err != nil { + log.Fatal(err) + } + return gopath +} + +func cleanUsername(n string) string { + b := make([]rune, len(n)) + for i, r := range n { + if r == '\\' || r == '/' || r == ':' { + b[i] = '_' + } else { + b[i] = r + } + } + return string(b) +} + +func goToolsCheckoutGood(dir string) bool { + if _, err := os.Stat(dir); err != nil { + return false + } + + cmd := exec.Command("hg", "id", "--id") + cmd.Dir = dir + out, err := cmd.Output() + if err != nil { + return false + } + id := strings.TrimSpace(string(out)) + if id != goToolsVersion { + return false + } + + cmd = exec.Command("hg", "status") + cmd.Dir = dir + out, err = cmd.Output() + if err != nil || len(out) > 0 { + return false + } + + return true +} diff --git a/src/cmd/api/testdata/src/pkg/p1/golden.txt b/src/cmd/api/testdata/src/pkg/p1/golden.txt index abcc0ce6c..3c43a226f 100644 --- a/src/cmd/api/testdata/src/pkg/p1/golden.txt +++ b/src/cmd/api/testdata/src/pkg/p1/golden.txt @@ -1,15 +1,24 @@ +pkg p1, const A = 1 pkg p1, const A ideal-int +pkg p1, const A64 = 1 pkg p1, const A64 int64 +pkg p1, const AIsLowerA = 11 pkg p1, const AIsLowerA ideal-int -pkg p1, const B ideal-int +pkg p1, const B0 = 2 +pkg p1, const B0 ideal-int +pkg p1, const ConstChase2 = 11 pkg p1, const ConstChase2 ideal-int +pkg p1, const ConversionConst = 5 pkg p1, const ConversionConst MyInt +pkg p1, const FloatConst = 3/2 pkg p1, const FloatConst ideal-float +pkg p1, const StrConst = "foo" pkg p1, const StrConst ideal-string pkg p1, func Bar(int8, int16, int64) pkg p1, func Bar1(int8, int16, int64) uint64 pkg p1, func Bar2(int8, int16, int64) (uint8, uint64) pkg p1, func BarE() Error +pkg p1, func Now() Time pkg p1, func PlainFunc(int, int, string) (*B, error) pkg p1, func TakesFunc(func(int) int) pkg p1, method (*B) JustOnB() @@ -34,9 +43,9 @@ pkg p1, type ByteStruct struct, R int32 pkg p1, type Codec struct pkg p1, type Codec struct, Func func(int, int) int pkg p1, type EmbedSelector struct -pkg p1, type EmbedSelector struct, embedded time.Time +pkg p1, type EmbedSelector struct, embedded Time pkg p1, type EmbedURLPtr struct -pkg p1, type EmbedURLPtr struct, embedded *url.URL +pkg p1, type EmbedURLPtr struct, embedded *URL pkg p1, type Embedded struct pkg p1, type Error interface { Error, Temporary } pkg p1, type Error interface, Error() string @@ -58,7 +67,7 @@ pkg p1, type Public interface, X() pkg p1, type Public interface, Y() pkg p1, type S struct pkg p1, type S struct, Public *int -pkg p1, type S struct, PublicTime time.Time +pkg p1, type S struct, PublicTime Time pkg p1, type S2 struct pkg p1, type S2 struct, Extra bool pkg p1, type S2 struct, embedded S @@ -68,6 +77,8 @@ pkg p1, type T struct pkg p1, type TPtrExported struct pkg p1, type TPtrExported struct, embedded *Embedded pkg p1, type TPtrUnexported struct +pkg p1, type Time struct +pkg p1, type URL struct pkg p1, var Byte uint8 pkg p1, var ByteConv []uint8 pkg p1, var ByteFunc func(uint8) int32 @@ -81,5 +92,5 @@ pkg p1, var V1 uint64 pkg p1, var V2 p2.Twoer pkg p1, var VError Error pkg p1, var X I -pkg p1, var X int64 +pkg p1, var X0 int64 pkg p1, var Y int diff --git a/src/cmd/api/testdata/src/pkg/p1/p1.go b/src/cmd/api/testdata/src/pkg/p1/p1.go index f94c9ceeb..65181b248 100644 --- a/src/cmd/api/testdata/src/pkg/p1/p1.go +++ b/src/cmd/api/testdata/src/pkg/p1/p1.go @@ -35,7 +35,7 @@ var ( var ChecksumError = ptwo.NewError("gzip checksum error") -const B = 2 +const B0 = 2 const StrConst = "foo" const FloatConst = 1.5 @@ -43,14 +43,18 @@ type myInt int type MyInt int +type Time struct{} + type S struct { Public *int private *int - PublicTime time.Time + PublicTime Time } +type URL struct{} + type EmbedURLPtr struct { - *url.URL + *URL } type S2 struct { @@ -58,7 +62,7 @@ type S2 struct { Extra bool } -var X int64 +var X0 int64 var ( Y int @@ -163,7 +167,7 @@ func (*common) OnBothTandBPtr() {} func (common) OnBothTandBVal() {} type EmbedSelector struct { - time.Time + Time } const ( @@ -174,10 +178,15 @@ const ( func ellipsis(...string) {} +func Now() Time { + var now Time + return now +} + var x = &S{ Public: nil, private: nil, - publicTime: time.Now(), + PublicTime: Now(), } var parenExpr = (1 + 5) diff --git a/src/cmd/cc/bv.c b/src/cmd/cc/bv.c new file mode 100644 index 000000000..51b7f4076 --- /dev/null +++ b/src/cmd/cc/bv.c @@ -0,0 +1,45 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include <u.h> +#include "cc.h" + +enum { + WORDSIZE = sizeof(uint32), + WORDBITS = 32, +}; + +uintptr +bvsize(uintptr n) +{ + return ((n + WORDBITS - 1) / WORDBITS) * WORDSIZE; +} + +Bvec* +bvalloc(int32 n) +{ + Bvec *bv; + uintptr nbytes; + + if(n < 0) + fatal(Z, "bvalloc: initial size is negative\n"); + nbytes = sizeof(Bvec) + bvsize(n); + bv = malloc(nbytes); + if(bv == nil) + fatal(Z, "bvalloc: malloc failed\n"); + memset(bv, 0, nbytes); + bv->n = n; + return bv; +} + +void +bvset(Bvec *bv, int32 i) +{ + uint32 mask; + + if(i < 0 || i >= bv->n) + fatal(Z, "bvset: index %d is out of bounds with length %d\n", i, bv->n); + mask = 1 << (i % WORDBITS); + bv->b[i / WORDBITS] |= mask; +} diff --git a/src/cmd/cc/cc.h b/src/cmd/cc/cc.h index c8de94120..af2339c97 100644 --- a/src/cmd/cc/cc.h +++ b/src/cmd/cc/cc.h @@ -52,9 +52,12 @@ typedef struct Hist Hist; typedef struct Term Term; typedef struct Init Init; typedef struct Bits Bits; +typedef struct Bvec Bvec; typedef struct Dynimp Dynimp; typedef struct Dynexp Dynexp; +typedef Rune TRune; /* target system type */ + #define BUFSIZ 8192 #define NSYMB 500 #define NHASH 1024 @@ -74,6 +77,12 @@ struct Bits uint32 b[BITS]; }; +struct Bvec +{ + int32 n; // number of bits + uint32 b[]; +}; + struct Node { Node* left; @@ -85,7 +94,7 @@ struct Node double fconst; /* fp constant */ vlong vconst; /* non fp const */ char* cstring; /* character string */ - ushort* rstring; /* rune string */ + TRune* rstring; /* rune string */ Sym* sym; Type* type; @@ -367,6 +376,9 @@ enum TFILE, TOLD, NALLTYPES, + + /* adapt size of Rune to target system's size */ + TRUNE = sizeof(TRune)==4? TUINT: TUSHORT, }; enum { @@ -746,6 +758,12 @@ int beq(Bits, Bits); int bset(Bits, uint); /* + * bv.c + */ +Bvec* bvalloc(int32 n); +void bvset(Bvec *bv, int32 i); + +/* * dpchk.c */ void dpcheck(Node*); @@ -766,12 +784,13 @@ void gclean(void); void gextern(Sym*, Node*, int32, int32); void ginit(void); int32 outstring(char*, int32); -int32 outlstring(ushort*, int32); +int32 outlstring(TRune*, int32); void sextern(Sym*, Node*, int32, int32); void xcom(Node*); int32 exreg(Type*); int32 align(int32, Type*, int, int32*); int32 maxround(int32, int32); +int hasdotdotdot(void); extern schar ewidth[]; @@ -800,7 +819,6 @@ int machcap(Node*); #pragma varargck type "Q" int32 #pragma varargck type "O" int #pragma varargck type "O" uint -#pragma varargck type "S" ushort* #pragma varargck type "T" Type* #pragma varargck type "U" char* #pragma varargck type "|" int diff --git a/src/cmd/cc/cc.y b/src/cmd/cc/cc.y index 830dd21f8..11ee444b7 100644 --- a/src/cmd/cc/cc.y +++ b/src/cmd/cc/cc.y @@ -891,9 +891,9 @@ lstring: LLSTRING { $$ = new(OLSTRING, Z, Z); - $$->type = typ(TARRAY, types[TUSHORT]); - $$->type->width = $1.l + sizeof(ushort); - $$->rstring = (ushort*)$1.s; + $$->type = typ(TARRAY, types[TRUNE]); + $$->type->width = $1.l + sizeof(TRune); + $$->rstring = (TRune*)$1.s; $$->sym = symstring; $$->etype = TARRAY; $$->class = CSTATIC; @@ -903,16 +903,16 @@ lstring: char *s; int n; - n = $1->type->width - sizeof(ushort); + n = $1->type->width - sizeof(TRune); s = alloc(n+$2.l+MAXALIGN); memcpy(s, $1->rstring, n); memcpy(s+n, $2.s, $2.l); - *(ushort*)(s+n+$2.l) = 0; + *(TRune*)(s+n+$2.l) = 0; $$ = $1; $$->type->width += $2.l; - $$->rstring = (ushort*)s; + $$->rstring = (TRune*)s; } zelist: diff --git a/src/cmd/cc/com.c b/src/cmd/cc/com.c index 6e470ee64..4886b73eb 100644 --- a/src/cmd/cc/com.c +++ b/src/cmd/cc/com.c @@ -87,6 +87,7 @@ tcomo(Node *n, int f) Node *l, *r; Type *t; int o; + static TRune zer; if(n == Z) { diag(Z, "Z in tcom"); @@ -651,12 +652,10 @@ tcomo(Node *n, int f) break; case OLSTRING: - if(n->type->link != types[TUSHORT]) { + if(n->type->link != types[TRUNE]) { o = outstring(0, 0); while(o & 3) { - ushort a[1]; - a[0] = 0; - outlstring(a, sizeof(ushort)); + outlstring(&zer, sizeof(TRune)); o = outlstring(0, 0); } } @@ -1326,10 +1325,10 @@ compar(Node *n, int reverse) if(lt->width == 8) hi = big(0, ~0ULL); else - hi = big(0, (1LL<<(l->type->width*8))-1); + hi = big(0, (1ULL<<(l->type->width*8))-1); }else{ - lo = big(~0ULL, -(1LL<<(l->type->width*8-1))); - hi = big(0, (1LL<<(l->type->width*8-1))-1); + lo = big(~0ULL, -(1ULL<<(l->type->width*8-1))); + hi = big(0, (1ULL<<(l->type->width*8-1))-1); } switch(op){ diff --git a/src/cmd/cc/dcl.c b/src/cmd/cc/dcl.c index edfc7e75a..a7a942686 100644 --- a/src/cmd/cc/dcl.c +++ b/src/cmd/cc/dcl.c @@ -268,7 +268,7 @@ nextinit(void) a->cstring++; } if(a->op == OLSTRING) { - b->vconst = convvtox(*a->rstring, TUSHORT); + b->vconst = convvtox(*a->rstring, TRUNE); a->rstring++; } a->type->width -= b->type->width; @@ -554,6 +554,28 @@ newlist(Node *l, Node *r) return new(OLIST, l, r); } +static int +haspointers(Type *t) +{ + Type *fld; + + switch(t->etype) { + case TSTRUCT: + for(fld = t->link; fld != T; fld = fld->down) { + if(haspointers(fld)) + return 1; + } + return 0; + case TARRAY: + return haspointers(t->link); + case TFUNC: + case TIND: + return 1; + default: + return 0; + } +} + void sualign(Type *t) { @@ -608,6 +630,9 @@ sualign(Type *t) diag(Z, "incomplete union element"); l->offset = 0; l->shift = 0; + if((debug['q'] || debug['Q']) && haspointers(l)) + diag(Z, "precise garbage collector cannot handle unions with pointers"); + o = align(align(0, l, Ael1, &maxal), l, Ael2, &maxal); if(o > w) w = o; diff --git a/src/cmd/cc/dpchk.c b/src/cmd/cc/dpchk.c index 34163ff92..606bf40dd 100644 --- a/src/cmd/cc/dpchk.c +++ b/src/cmd/cc/dpchk.c @@ -567,7 +567,19 @@ pragfpround(void) void pragtextflag(void) { - textflag = getnsn(); + Sym *s; + + s = getsym(); + if(s == S) { + textflag = getnsn(); + } else { + if(s->macro) { + macexpand(s, symb); + } + if(symb[0] < '0' || symb[0] > '9') + yyerror("pragma textflag not an integer"); + textflag = atoi(symb); + } while(getnsc() != '\n') ; if(debug['f']) @@ -577,7 +589,19 @@ pragtextflag(void) void pragdataflag(void) { - dataflag = getnsn(); + Sym *s; + + s = getsym(); + if(s == S) { + dataflag = getnsn(); + } else { + if(s->macro) { + macexpand(s, symb); + } + if(symb[0] < '0' || symb[0] > '9') + yyerror("pragma dataflag not an integer"); + dataflag = atoi(symb); + } while(getnsc() != '\n') ; if(debug['f']) diff --git a/src/cmd/cc/funct.c b/src/cmd/cc/funct.c index 7921277b4..92c067db8 100644 --- a/src/cmd/cc/funct.c +++ b/src/cmd/cc/funct.c @@ -235,7 +235,7 @@ no: return 0; bad: - diag(n, "cant rewrite typestr for op %O\n", o); + diag(n, "can't rewrite typestr for op %O\n", o); prtree(n, "isfunct"); n->type = T; return 1; diff --git a/src/cmd/cc/lex.c b/src/cmd/cc/lex.c index 4fb0be9a4..049dc5196 100644 --- a/src/cmd/cc/lex.c +++ b/src/cmd/cc/lex.c @@ -118,6 +118,7 @@ main(int argc, char *argv[]) { int c; + quotefmtinstall(); // before cinit, which overrides %Q ensuresymb(NSYMB); memset(debug, 0, sizeof(debug)); tinit(); @@ -126,7 +127,6 @@ main(int argc, char *argv[]) arginit(); fmtstrinit(&pragcgobuf); - quotefmtinstall(); tufield = simplet((1L<<tfield->etype) | BUNSIGNED); ndef = 0; @@ -533,7 +533,7 @@ l1: yyerror("missing '"); peekc = c1; } - yylval.vval = convvtox(c, TUSHORT); + yylval.vval = convvtox(c, TRUNE); return LUCONST; } if(c == '"') { @@ -607,15 +607,15 @@ l1: c = escchar('"', 1, 0); if(c == EOF) break; - cp = allocn(cp, c1, sizeof(ushort)); - *(ushort*)(cp + c1) = c; - c1 += sizeof(ushort); + cp = allocn(cp, c1, sizeof(TRune)); + *(TRune*)(cp + c1) = c; + c1 += sizeof(TRune); } yylval.sval.l = c1; do { - cp = allocn(cp, c1, sizeof(ushort)); - *(ushort*)(cp + c1) = 0; - c1 += sizeof(ushort); + cp = allocn(cp, c1, sizeof(TRune)); + *(TRune*)(cp + c1) = 0; + c1 += sizeof(TRune); } while(c1 & MAXALIGN); yylval.sval.s = cp; return LLSTRING; @@ -1019,7 +1019,7 @@ hex: c += 10-'A'; else goto bad; - nn = n*16 + c; + nn = (uvlong)n*16 + c; if(n < 0 && nn >= 0) goto bad; n = nn; @@ -1093,7 +1093,7 @@ getnsc(void) } else c = GETC(); for(;;) { - if(!isspace(c)) + if(c >= Runeself || !isspace(c)) return c; if(c == '\n') { lineno++; @@ -1137,7 +1137,7 @@ loop: */ i = 2; if(longflg) - i = 4; + i = 6; l = 0; for(; i>0; i--) { c = getc(); @@ -1167,7 +1167,7 @@ loop: */ i = 2; if(longflg) - i = 5; + i = 8; l = c - '0'; for(; i>0; i--) { c = getc(); diff --git a/src/cmd/cc/lexbody b/src/cmd/cc/lexbody index f4a69739c..9d293b089 100644 --- a/src/cmd/cc/lexbody +++ b/src/cmd/cc/lexbody @@ -224,7 +224,7 @@ Sym* lookup(void) { Sym *s; - int32 h; + uint32 h; char *p; int c, l; char *r, *w; @@ -400,7 +400,7 @@ l1: if(c >= '0' && c <= '9') { if(c > '7' && c1 == 3) break; - yylval.lval <<= c1; + yylval.lval = (uvlong)yylval.lval << c1; yylval.lval += c - '0'; c = GETC(); continue; @@ -410,7 +410,7 @@ l1: if(c >= 'A' && c <= 'F') c += 'a' - 'A'; if(c >= 'a' && c <= 'f') { - yylval.lval <<= c1; + yylval.lval = (uvlong)yylval.lval << c1; yylval.lval += c - 'a' + 10; c = GETC(); continue; @@ -665,7 +665,7 @@ loop: goto pop; } fi.p = i->b + 1; - return i->b[0]; + return i->b[0] & 0xff; pop: iostack = i->link; @@ -678,7 +678,7 @@ pop: fi.c = i->c; if(--fi.c < 0) goto loop; - return *fi.p++; + return *fi.p++ & 0xff; } void @@ -762,7 +762,7 @@ ieeedtod(Ieee *ieee, double native) return; } fr = frexp(native, &exp); - f = 2097152L; /* shouldnt use fp constants here */ + f = 2097152L; /* shouldn't use fp constants here */ fr = modf(fr*f, &ho); ieee->h = ho; ieee->h &= 0xfffffL; @@ -770,6 +770,6 @@ ieeedtod(Ieee *ieee, double native) f = 65536L; fr = modf(fr*f, &ho); ieee->l = ho; - ieee->l <<= 16; + ieee->l = (uint32)ieee->l << 16; ieee->l |= (int32)(fr*f); } diff --git a/src/cmd/cc/pgen.c b/src/cmd/cc/pgen.c index b06aa996d..b82872bc5 100644 --- a/src/cmd/cc/pgen.c +++ b/src/cmd/cc/pgen.c @@ -29,6 +29,22 @@ // THE SOFTWARE. #include "gc.h" +#include "../../pkg/runtime/funcdata.h" + +enum { BitsPerPointer = 2 }; + +static void dumpgcargs(Type *fn, Sym *sym); + +int +hasdotdotdot(void) +{ + Type *t; + + for(t=thisfn->down; t!=T; t=t->down) + if(t->etype == TDOT) + return 1; + return 0; +} vlong argsize(void) @@ -43,9 +59,9 @@ argsize(void) case TVOID: break; case TDOT: - yyerror("function takes ... without textflag NOSPLIT"); - s += 64; - break; + if((textflag & NOSPLIT) == 0) + yyerror("function takes ... without textflag NOSPLIT"); + return ArgsSizeUnknown; default: s = align(s, t, Aarg1, nil); s = align(s, t, Aarg2, nil); @@ -64,7 +80,10 @@ void codgen(Node *n, Node *nn) { Prog *sp; - Node *n1, nod, nod1; + Node *n1, nod, nod1, nod2; + Sym *gcsym, *gclocalssym; + static int ngcsym, ngclocalssym; + static char namebuf[40]; cursafe = 0; curarg = 0; @@ -75,7 +94,7 @@ codgen(Node *n, Node *nn) */ for(n1 = nn;; n1 = n1->left) { if(n1 == Z) { - diag(nn, "cant find function name"); + diag(nn, "can't find function name"); return; } if(n1->op == ONAME) @@ -87,6 +106,30 @@ codgen(Node *n, Node *nn) sp = p; /* + * generate funcdata symbol for this function. + * data is filled in at the end of codgen(). + */ + snprint(namebuf, sizeof namebuf, "gc·%d", ngcsym++); + gcsym = slookup(namebuf); + gcsym->class = CSTATIC; + + memset(&nod, 0, sizeof nod); + nod.op = ONAME; + nod.sym = gcsym; + nod.class = CSTATIC; + gins(AFUNCDATA, nodconst(FUNCDATA_GCArgs), &nod); + + snprint(namebuf, sizeof(namebuf), "gclocalssym·%d", ngclocalssym++); + gclocalssym = slookup(namebuf); + gclocalssym->class = CSTATIC; + + memset(&nod2, 0, sizeof(nod2)); + nod2.op = ONAME; + nod2.sym = gclocalssym; + nod2.class = CSTATIC; + gins(AFUNCDATA, nodconst(FUNCDATA_GCLocals), &nod2); + + /* * isolate first argument */ if(REGARG >= 0) { @@ -122,6 +165,21 @@ codgen(Node *n, Node *nn) if(thechar=='6' || thechar=='7') /* [sic] */ maxargsafe = xround(maxargsafe, 8); sp->to.offset += maxargsafe; + + dumpgcargs(thisfn, gcsym); + + // TODO(rsc): "stkoff" is not right. It does not account for + // the possibility of data stored in .safe variables. + // Unfortunately those move up and down just like + // the argument frame (and in fact dovetail with it) + // so the number we need is not available or even + // well-defined. Probably we need to make the safe + // area its own section. + // That said, we've been using stkoff for months + // and nothing too terrible has happened. + gextern(gclocalssym, nodconst(-stkoff), 0, 4); // locals + gclocalssym->type = typ(0, T); + gclocalssym->type->width = 4; } void @@ -589,3 +647,107 @@ bcomplex(Node *n, Node *c) boolgen(n, 1, Z); return 0; } + +// Updates the bitvector with a set bit for each pointer containing +// value in the type description starting at offset. +static void +walktype1(Type *t, int32 offset, Bvec *bv, int param) +{ + Type *t1; + int32 o; + + switch(t->etype) { + case TCHAR: + case TUCHAR: + case TSHORT: + case TUSHORT: + case TINT: + case TUINT: + case TLONG: + case TULONG: + case TVLONG: + case TUVLONG: + case TFLOAT: + case TDOUBLE: + // non-pointer types + break; + + case TIND: + pointer: + // pointer types + if((offset + t->offset) % ewidth[TIND] != 0) + yyerror("unaligned pointer"); + bvset(bv, ((offset + t->offset) / ewidth[TIND])*BitsPerPointer); + break; + + case TARRAY: + if(param) // unlike Go, C passes arrays by reference + goto pointer; + // array in struct or union is an actual array + for(o = 0; o < t->width; o += t->link->width) + walktype1(t->link, offset+o, bv, 0); + break; + + case TSTRUCT: + // build map recursively + for(t1 = t->link; t1 != T; t1 = t1->down) + walktype1(t1, offset, bv, 0); + break; + + case TUNION: + walktype1(t->link, offset, bv, 0); + break; + + default: + yyerror("can't handle arg type %s\n", tnames[t->etype]); + } +} + +// Compute a bit vector to describe the pointer containing locations +// in the argument list. Adds the data to gcsym and returns the offset +// of end of the bit vector. +static void +dumpgcargs(Type *fn, Sym *sym) +{ + Bvec *bv; + Type *t; + int32 i; + int32 argbytes; + int32 symoffset, argoffset; + + if(hasdotdotdot()) { + // give up for C vararg functions. + // TODO: maybe make a map just for the args we do know? + gextern(sym, nodconst(0), 0, 4); // nptrs=0 + symoffset = 4; + } else { + argbytes = (argsize() + ewidth[TIND] - 1); + bv = bvalloc((argbytes / ewidth[TIND]) * BitsPerPointer); + argoffset = align(0, fn->link, Aarg0, nil); + if(argoffset > 0) { + // The C calling convention returns structs by + // copying them to a location pointed to by a + // hidden first argument. This first argument + // is a pointer. + if(argoffset != ewidth[TIND]) + yyerror("passbyptr arg not the right size"); + bvset(bv, 0); + } + for(t = fn->down; t != T; t = t->down) { + if(t->etype == TVOID) + continue; + argoffset = align(argoffset, t, Aarg1, nil); + walktype1(t, argoffset, bv, 1); + argoffset = align(argoffset, t, Aarg2, nil); + } + gextern(sym, nodconst(bv->n), 0, 4); + symoffset = 4; + for(i = 0; i < bv->n; i += 32) { + gextern(sym, nodconst(bv->b[i/32]), symoffset, 4); + symoffset += 4; + } + free(bv); + } + sym->type = typ(0, T); + sym->type->width = symoffset; +} diff --git a/src/cmd/cc/pswt.c b/src/cmd/cc/pswt.c index b94035faa..cc9c22763 100644 --- a/src/cmd/cc/pswt.c +++ b/src/cmd/cc/pswt.c @@ -102,28 +102,29 @@ newcase(void) } int32 -outlstring(ushort *s, int32 n) +outlstring(TRune *s, int32 n) { - char buf[2]; - int c; + char buf[sizeof(TRune)]; + uint c; + int i; int32 r; if(suppress) return nstring; - while(nstring & 1) + while(nstring & (sizeof(TRune)-1)) outstring("", 1); r = nstring; while(n > 0) { c = *s++; if(align(0, types[TCHAR], Aarg1, nil)) { - buf[0] = c>>8; - buf[1] = c; + for(i = 0; i < sizeof(TRune); i++) + buf[i] = c>>(8*(sizeof(TRune) - i - 1)); } else { - buf[0] = c; - buf[1] = c>>8; + for(i = 0; i < sizeof(TRune); i++) + buf[i] = c>>(8*i); } - outstring(buf, 2); - n -= sizeof(ushort); + outstring(buf, sizeof(TRune)); + n -= sizeof(TRune); } return r; } @@ -155,7 +156,7 @@ ieeedtod(Ieee *ieee, double native) return; } fr = frexp(native, &exp); - f = 2097152L; /* shouldnt use fp constants here */ + f = 2097152L; /* shouldn't use fp constants here */ fr = modf(fr*f, &ho); ieee->h = ho; ieee->h &= 0xfffffL; diff --git a/src/cmd/cc/scon.c b/src/cmd/cc/scon.c index f6031a5be..b0b909759 100644 --- a/src/cmd/cc/scon.c +++ b/src/cmd/cc/scon.c @@ -186,7 +186,7 @@ evconst(Node *n) break; case OASHL: - v = l->vconst << r->vconst; + v = (uvlong)l->vconst << r->vconst; break; case OLO: diff --git a/src/cmd/cc/sub.c b/src/cmd/cc/sub.c index 3a5576385..bed989102 100644 --- a/src/cmd/cc/sub.c +++ b/src/cmd/cc/sub.c @@ -116,7 +116,10 @@ prtree1(Node *n, int d, int f) break; case OLSTRING: - print(" \"%S\"", n->rstring); + if(sizeof(TRune) == sizeof(Rune)) + print(" \"%S\"", (Rune*)n->rstring); + else + print(" \"...\""); i = 0; break; diff --git a/src/cmd/cc/y.tab.c b/src/cmd/cc/y.tab.c index 10938c10b..8588515ab 100644 --- a/src/cmd/cc/y.tab.c +++ b/src/cmd/cc/y.tab.c @@ -1,24 +1,21 @@ -/* A Bison parser, made by GNU Bison 2.3. */ +/* A Bison parser, made by GNU Bison 2.7.12-4996. */ -/* Skeleton implementation for Bison's Yacc-like parsers in C - - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 - Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ + along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work @@ -29,7 +26,7 @@ special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. - + This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ @@ -47,7 +44,7 @@ #define YYBISON 1 /* Bison version. */ -#define YYBISON_VERSION "2.3" +#define YYBISON_VERSION "2.7.12-4996" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -55,11 +52,54 @@ /* Pure parsers. */ #define YYPURE 0 -/* Using locations. */ -#define YYLSP_NEEDED 0 +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + +/* Copy the first part of user declarations. */ +/* Line 371 of yacc.c */ +#line 31 "cc.y" + +#include <u.h> +#include <stdio.h> /* if we don't, bison will, and cc.h re-#defines getc */ +#include "cc.h" + +/* Line 371 of yacc.c */ +#line 74 "y.tab.c" + +# ifndef YY_NULL +# if defined __cplusplus && 201103L <= __cplusplus +# define YY_NULL nullptr +# else +# define YY_NULL 0 +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* In a future release of Bison, this section will be replaced + by #include "y.tab.h". */ +#ifndef YY_YY_Y_TAB_H_INCLUDED +# define YY_YY_Y_TAB_H_INCLUDED +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif + /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE @@ -216,37 +256,12 @@ - -/* Copy the first part of user declarations. */ -#line 31 "cc.y" - -#include <u.h> -#include <stdio.h> /* if we don't, bison will, and cc.h re-#defines getc */ -#include "cc.h" - - -/* Enabling traces. */ -#ifndef YYDEBUG -# define YYDEBUG 0 -#endif - -/* Enabling verbose error messages. */ -#ifdef YYERROR_VERBOSE -# undef YYERROR_VERBOSE -# define YYERROR_VERBOSE 1 -#else -# define YYERROR_VERBOSE 0 -#endif - -/* Enabling the token table. */ -#ifndef YYTOKEN_TABLE -# define YYTOKEN_TABLE 0 -#endif - #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE -#line 36 "cc.y" { +/* Line 387 of yacc.c */ +#line 36 "cc.y" + Node* node; Sym* sym; Type* type; @@ -270,22 +285,38 @@ typedef union YYSTYPE int32 lval; double dval; vlong vval; -} -/* Line 193 of yacc.c. */ -#line 276 "y.tab.c" - YYSTYPE; + + +/* Line 387 of yacc.c */ +#line 292 "y.tab.c" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 -# define YYSTYPE_IS_TRIVIAL 1 #endif +extern YYSTYPE yylval; +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ -/* Copy the second part of user declarations. */ +#endif /* !YY_YY_Y_TAB_H_INCLUDED */ +/* Copy the second part of user declarations. */ -/* Line 216 of yacc.c. */ -#line 289 "y.tab.c" +/* Line 390 of yacc.c */ +#line 320 "y.tab.c" #ifdef short # undef short @@ -338,36 +369,45 @@ typedef short int yytype_int16; # if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include <libintl.h> /* INFRINGES ON USER NAME SPACE */ -# define YY_(msgid) dgettext ("bison-runtime", msgid) +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) # endif # endif # ifndef YY_ -# define YY_(msgid) msgid +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef __attribute__ +/* This feature is available in gcc versions 2.5 and later. */ +# if (! defined __GNUC__ || __GNUC__ < 2 \ + || (__GNUC__ == 2 && __GNUC_MINOR__ < 5)) +# define __attribute__(Spec) /* empty */ # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ -# define YYUSE(e) ((void) (e)) +# define YYUSE(E) ((void) (E)) #else -# define YYUSE(e) /* empty */ +# define YYUSE(E) /* empty */ #endif + /* Identity function, used to suppress warnings about constant conditions. */ #ifndef lint -# define YYID(n) (n) +# define YYID(N) (N) #else #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static int -YYID (int i) +YYID (int yyi) #else static int -YYID (i) - int i; +YYID (yyi) + int yyi; #endif { - return i; + return yyi; } #endif @@ -388,11 +428,12 @@ YYID (i) # define alloca _alloca # else # define YYSTACK_ALLOC alloca -# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 # endif # endif # endif @@ -415,24 +456,24 @@ YYID (i) # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif -# if (defined __cplusplus && ! defined _STDLIB_H \ +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc -# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free -# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif @@ -448,9 +489,9 @@ void free (void *); /* INFRINGES ON USER NAME SPACE */ /* A type that is properly aligned for any stack member. */ union yyalloc { - yytype_int16 yyss; - YYSTYPE yyvs; - }; + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; +}; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) @@ -461,35 +502,19 @@ union yyalloc ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) -/* Copy COUNT objects from FROM to TO. The source and destination do - not overlap. */ -# ifndef YYCOPY -# if defined __GNUC__ && 1 < __GNUC__ -# define YYCOPY(To, From, Count) \ - __builtin_memcpy (To, From, (Count) * sizeof (*(From))) -# else -# define YYCOPY(To, From, Count) \ - do \ - { \ - YYSIZE_T yyi; \ - for (yyi = 0; yyi < (Count); yyi++) \ - (To)[yyi] = (From)[yyi]; \ - } \ - while (YYID (0)) -# endif -# endif +# define YYCOPY_NEEDED 1 /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ -# define YYSTACK_RELOCATE(Stack) \ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ do \ { \ YYSIZE_T yynewbytes; \ - YYCOPY (&yyptr->Stack, Stack, yysize); \ - Stack = &yyptr->Stack; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ @@ -497,6 +522,26 @@ union yyalloc #endif +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + /* YYFINAL -- State number of the termination state. */ #define YYFINAL 2 /* YYLAST -- Last index in YYTABLE. */ @@ -701,7 +746,7 @@ static const yytype_uint16 yyrline[] = }; #endif -#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +#if YYDEBUG || YYERROR_VERBOSE || 0 /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = @@ -719,16 +764,16 @@ static const char *const yytname[] = "LTYPEDEF", "LTYPESTR", "LUNION", "LUNSIGNED", "LWHILE", "LVOID", "LENUM", "LSIGNED", "LCONSTNT", "LVOLATILE", "LSET", "LSIGNOF", "LRESTRICT", "LINLINE", "')'", "']'", "'{'", "'}'", "'!'", "'~'", - "$accept", "prog", "xdecl", "@1", "@2", "xdlist", "@3", "xdecor", - "xdecor2", "adecl", "adlist", "@4", "pdecl", "pdlist", "edecl", "@5", - "@6", "zedlist", "edlist", "edecor", "abdecor", "abdecor1", "abdecor2", + "$accept", "prog", "xdecl", "$@1", "$@2", "xdlist", "$@3", "xdecor", + "xdecor2", "adecl", "adlist", "$@4", "pdecl", "pdlist", "edecl", "$@5", + "$@6", "zedlist", "edlist", "edecor", "abdecor", "abdecor1", "abdecor2", "abdecor3", "init", "qual", "qlist", "ilist", "zarglist", "arglist", - "block", "slist", "labels", "label", "stmnt", "forexpr", "ulstmnt", "@7", - "@8", "zcexpr", "zexpr", "lexpr", "cexpr", "expr", "xuexpr", "uexpr", - "pexpr", "string", "lstring", "zelist", "elist", "sbody", "@9", - "zctlist", "types", "tlist", "ctlist", "complex", "@10", "@11", "@12", - "@13", "@14", "gctnlist", "zgnlist", "gctname", "gcnlist", "gcname", - "enum", "tname", "cname", "gname", "name", "tag", "ltag", 0 + "block", "slist", "labels", "label", "stmnt", "forexpr", "ulstmnt", + "$@7", "$@8", "zcexpr", "zexpr", "lexpr", "cexpr", "expr", "xuexpr", + "uexpr", "pexpr", "string", "lstring", "zelist", "elist", "sbody", "@9", + "zctlist", "types", "tlist", "ctlist", "complex", "$@10", "$@11", "$@12", + "$@13", "$@14", "gctnlist", "zgnlist", "gctname", "gcnlist", "gcname", + "enum", "tname", "cname", "gname", "name", "tag", "ltag", YY_NULL }; #endif @@ -810,8 +855,8 @@ static const yytype_uint8 yyr2[] = 1, 1, 1, 1, 1, 1, 1, 1 }; -/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state - STATE-NUM when YYTABLE doesn't specify something else to do. Zero +/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { @@ -936,8 +981,7 @@ static const yytype_int16 yypgoto[] = /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which - number is the opposite. If zero, do what YYDEFACT says. - If YYTABLE_NINF, syntax error. */ + number is the opposite. If YYTABLE_NINF, syntax error. */ #define YYTABLE_NINF -205 static const yytype_int16 yytable[] = { @@ -1062,6 +1106,12 @@ static const yytype_int16 yytable[] = 178, 179, 180, 181, 182, 183, 184, 185, 186 }; +#define yypact_value_is_default(Yystate) \ + (!!((Yystate) == (-331))) + +#define yytable_value_is_error(Yytable_value) \ + YYID (0) + static const yytype_int16 yycheck[] = { 1, 27, 14, 91, 131, 17, 30, 58, 20, 33, @@ -1245,78 +1295,50 @@ static const yytype_uint8 yystos[] = /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. - Once GCC version 2 has supplanted version 1, this can go. */ + Once GCC version 2 has supplanted version 1, this can go. However, + YYFAIL appears to be in use. Nevertheless, it is formally deprecated + in Bison 2.4.2's NEWS entry, where a plan to phase it out is + discussed. */ #define YYFAIL goto yyerrlab +#if defined YYFAIL + /* This is here to suppress warnings from the GCC cpp's + -Wunused-macros. Normally we don't worry about that warning, but + some users do, and we want to make it easy for users to remove + YYFAIL uses, which will produce warnings from Bison 2.5. */ +#endif #define YYRECOVERING() (!!yyerrstatus) -#define YYBACKUP(Token, Value) \ -do \ - if (yychar == YYEMPTY && yylen == 1) \ - { \ - yychar = (Token); \ - yylval = (Value); \ - yytoken = YYTRANSLATE (yychar); \ - YYPOPSTACK (1); \ - goto yybackup; \ - } \ - else \ - { \ +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ yyerror (YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (YYID (0)) - +/* Error token number */ #define YYTERROR 1 #define YYERRCODE 256 -/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. - If N is 0, then set CURRENT to the empty location which ends - the previous symbol: RHS[0] (always defined). */ - -#define YYRHSLOC(Rhs, K) ((Rhs)[K]) -#ifndef YYLLOC_DEFAULT -# define YYLLOC_DEFAULT(Current, Rhs, N) \ - do \ - if (YYID (N)) \ - { \ - (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ - (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ - (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ - (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ - } \ - else \ - { \ - (Current).first_line = (Current).last_line = \ - YYRHSLOC (Rhs, 0).last_line; \ - (Current).first_column = (Current).last_column = \ - YYRHSLOC (Rhs, 0).last_column; \ - } \ - while (YYID (0)) -#endif - - -/* YY_LOCATION_PRINT -- Print the location on the stream. - This macro was not mandated originally: define only if we know - we won't break user code: when these are the locations we know. */ - +/* This macro is provided for backward compatibility. */ #ifndef YY_LOCATION_PRINT -# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL -# define YY_LOCATION_PRINT(File, Loc) \ - fprintf (File, "%d.%d-%d.%d", \ - (Loc).first_line, (Loc).first_column, \ - (Loc).last_line, (Loc).last_column) -# else -# define YY_LOCATION_PRINT(File, Loc) ((void) 0) -# endif +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) #endif /* YYLEX -- calling `yylex' with the right arguments. */ - #ifdef YYLEX_PARAM # define YYLEX yylex (YYLEX_PARAM) #else @@ -1366,6 +1388,8 @@ yy_symbol_value_print (yyoutput, yytype, yyvaluep) YYSTYPE const * const yyvaluep; #endif { + FILE *yyo = yyoutput; + YYUSE (yyo); if (!yyvaluep) return; # ifdef YYPRINT @@ -1374,11 +1398,7 @@ yy_symbol_value_print (yyoutput, yytype, yyvaluep) # else YYUSE (yyoutput); # endif - switch (yytype) - { - default: - break; - } + YYUSE (yytype); } @@ -1415,17 +1435,20 @@ yy_symbol_print (yyoutput, yytype, yyvaluep) #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void -yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) #else static void -yy_stack_print (bottom, top) - yytype_int16 *bottom; - yytype_int16 *top; +yy_stack_print (yybottom, yytop) + yytype_int16 *yybottom; + yytype_int16 *yytop; #endif { YYFPRINTF (stderr, "Stack now"); - for (; bottom <= top; ++bottom) - YYFPRINTF (stderr, " %d", *bottom); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } YYFPRINTF (stderr, "\n"); } @@ -1459,11 +1482,11 @@ yy_reduce_print (yyvsp, yyrule) /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { - fprintf (stderr, " $%d = ", yyi + 1); + YYFPRINTF (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], &(yyvsp[(yyi + 1) - (yynrhs)]) ); - fprintf (stderr, "\n"); + YYFPRINTF (stderr, "\n"); } } @@ -1500,7 +1523,6 @@ int yydebug; # define YYMAXDEPTH 10000 #endif - #if YYERROR_VERBOSE @@ -1603,115 +1625,145 @@ yytnamerr (char *yyres, const char *yystr) } # endif -/* Copy into YYRESULT an error message about the unexpected token - YYCHAR while in state YYSTATE. Return the number of bytes copied, - including the terminating null byte. If YYRESULT is null, do not - copy anything; just return the number of bytes that would be - copied. As a special case, return 0 if an ordinary "syntax error" - message will do. Return YYSIZE_MAXIMUM if overflow occurs during - size calculation. */ -static YYSIZE_T -yysyntax_error (char *yyresult, int yystate, int yychar) -{ - int yyn = yypact[yystate]; +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. - if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) - return 0; - else - { - int yytype = YYTRANSLATE (yychar); - YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); - YYSIZE_T yysize = yysize0; - YYSIZE_T yysize1; - int yysize_overflow = 0; - enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; - char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; - int yyx; - -# if 0 - /* This is so xgettext sees the translatable formats that are - constructed on the fly. */ - YY_("syntax error, unexpected %s"); - YY_("syntax error, unexpected %s, expecting %s"); - YY_("syntax error, unexpected %s, expecting %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); -# endif - char *yyfmt; - char const *yyf; - static char const yyunexpected[] = "syntax error, unexpected %s"; - static char const yyexpecting[] = ", expecting %s"; - static char const yyor[] = " or %s"; - char yyformat[sizeof yyunexpected - + sizeof yyexpecting - 1 - + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) - * (sizeof yyor - 1))]; - char const *yyprefix = yyexpecting; - - /* Start YYX at -YYN if negative to avoid negative indexes in - YYCHECK. */ - int yyxbegin = yyn < 0 ? -yyn : 0; - - /* Stay within bounds of both yycheck and yytname. */ - int yychecklim = YYLAST - yyn + 1; - int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; - int yycount = 1; - - yyarg[0] = yytname[yytype]; - yyfmt = yystpcpy (yyformat, yyunexpected); - - for (yyx = yyxbegin; yyx < yyxend; ++yyx) - if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) - { - if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) - { - yycount = 1; - yysize = yysize0; - yyformat[sizeof yyunexpected - 1] = '\0'; - break; - } - yyarg[yycount++] = yytname[yyx]; - yysize1 = yysize + yytnamerr (0, yytname[yyx]); - yysize_overflow |= (yysize1 < yysize); - yysize = yysize1; - yyfmt = yystpcpy (yyfmt, yyprefix); - yyprefix = yyor; - } + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) +{ + YYSIZE_T yysize0 = yytnamerr (YY_NULL, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULL; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; + + /* There are many possibilities here to consider: + - Assume YYFAIL is not used. It's too flawed to consider. See + <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html> + for details. YYERROR is fine as it does not invoke this + function. + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULL, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } + } - yyf = YY_(yyformat); - yysize1 = yysize + yystrlen (yyf); - yysize_overflow |= (yysize1 < yysize); - yysize = yysize1; + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } - if (yysize_overflow) - return YYSIZE_MAXIMUM; + { + YYSIZE_T yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } - if (yyresult) - { - /* Avoid sprintf, as that infringes on the user's name space. - Don't have undefined behavior even if the translation - produced a string with the wrong number of "%s"s. */ - char *yyp = yyresult; - int yyi = 0; - while ((*yyp = *yyf) != '\0') - { - if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) - { - yyp += yytnamerr (yyp, yyarg[yyi++]); - yyf += 2; - } - else - { - yyp++; - yyf++; - } - } - } - return yysize; + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; } #endif /* YYERROR_VERBOSE */ - /*-----------------------------------------------. | Release the memory associated to this symbol. | @@ -1736,44 +1788,31 @@ yydestruct (yymsg, yytype, yyvaluep) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); - switch (yytype) - { - - default: - break; - } + YYUSE (yytype); } - -/* Prevent warnings from -Wmissing-prototypes. */ - -#ifdef YYPARSE_PARAM -#if defined __STDC__ || defined __cplusplus -int yyparse (void *YYPARSE_PARAM); -#else -int yyparse (); -#endif -#else /* ! YYPARSE_PARAM */ -#if defined __STDC__ || defined __cplusplus -int yyparse (void); -#else -int yyparse (); -#endif -#endif /* ! YYPARSE_PARAM */ -/* The look-ahead symbol. */ +/* The lookahead symbol. */ int yychar; -/* The semantic value of the look-ahead symbol. */ -YYSTYPE yylval; + +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval YY_INITIAL_VALUE(yyval_default); /* Number of syntax errors so far. */ int yynerrs; - /*----------. | yyparse. | `----------*/ @@ -1800,14 +1839,37 @@ yyparse () #endif #endif { - - int yystate; + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + `yyss': related to states. + `yyvs': related to semantic values. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + YYSIZE_T yystacksize; + int yyn; int yyresult; - /* Number of tokens to shift before error messages enabled. */ - int yyerrstatus; - /* Look-ahead token as an internal (translated) token number. */ + /* Lookahead token as an internal (translated) token number. */ int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; @@ -1815,54 +1877,22 @@ yyparse () YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif - /* Three stacks and their tools: - `yyss': related to states, - `yyvs': related to semantic values, - `yyls': related to locations. - - Refer to the stacks thru separate pointers, to allow yyoverflow - to reallocate them elsewhere. */ - - /* The state stack. */ - yytype_int16 yyssa[YYINITDEPTH]; - yytype_int16 *yyss = yyssa; - yytype_int16 *yyssp; - - /* The semantic value stack. */ - YYSTYPE yyvsa[YYINITDEPTH]; - YYSTYPE *yyvs = yyvsa; - YYSTYPE *yyvsp; - - - #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) - YYSIZE_T yystacksize = YYINITDEPTH; - - /* The variables used to return semantic value and location from the - action routines. */ - YYSTYPE yyval; - - /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yystacksize = YYINITDEPTH; + YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; - yychar = YYEMPTY; /* Cause a token to be read. */ - - /* Initialize stack pointers. - Waste one element of value and location stack - so that they stay on the same level as the state stack. - The wasted elements are never initialized. */ - - yyssp = yyss; - yyvsp = yyvs; - + yychar = YYEMPTY; /* Cause a token to be read. */ goto yysetstate; /*------------------------------------------------------------. @@ -1889,7 +1919,6 @@ yyparse () YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; - /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might @@ -1897,7 +1926,6 @@ yyparse () yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), - &yystacksize); yyss = yyss1; @@ -1920,9 +1948,8 @@ yyparse () (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; - YYSTACK_RELOCATE (yyss); - YYSTACK_RELOCATE (yyvs); - + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); @@ -1933,7 +1960,6 @@ yyparse () yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; - YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); @@ -1943,6 +1969,9 @@ yyparse () YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + if (yystate == YYFINAL) + YYACCEPT; + goto yybackup; /*-----------. @@ -1951,16 +1980,16 @@ yyparse () yybackup: /* Do appropriate processing given the current state. Read a - look-ahead token if we need one and don't already have one. */ + lookahead token if we need one and don't already have one. */ - /* First try to decide what to do without reference to look-ahead token. */ + /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; - if (yyn == YYPACT_NINF) + if (yypact_value_is_default (yyn)) goto yydefault; - /* Not known => get a look-ahead token if don't already have one. */ + /* Not known => get a lookahead token if don't already have one. */ - /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); @@ -1986,29 +2015,27 @@ yybackup: yyn = yytable[yyn]; if (yyn <= 0) { - if (yyn == 0 || yyn == YYTABLE_NINF) - goto yyerrlab; + if (yytable_value_is_error (yyn)) + goto yyerrlab; yyn = -yyn; goto yyreduce; } - if (yyn == YYFINAL) - YYACCEPT; - /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; - /* Shift the look-ahead token. */ + /* Shift the lookahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); - /* Discard the shifted token unless it is eof. */ - if (yychar != YYEOF) - yychar = YYEMPTY; + /* Discard the shifted token. */ + yychar = YYEMPTY; yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END goto yynewstate; @@ -2045,6 +2072,7 @@ yyreduce: switch (yyn) { case 4: +/* Line 1787 of yacc.c */ #line 109 "cc.y" { dodecl(xdecl, lastclass, lasttype, Z); @@ -2052,6 +2080,7 @@ yyreduce: break; case 6: +/* Line 1787 of yacc.c */ #line 114 "cc.y" { lastdcl = T; @@ -2069,6 +2098,7 @@ yyreduce: break; case 7: +/* Line 1787 of yacc.c */ #line 128 "cc.y" { argmark((yyvsp[(2) - (4)].node), 1); @@ -2076,6 +2106,7 @@ yyreduce: break; case 8: +/* Line 1787 of yacc.c */ #line 132 "cc.y" { Node *n; @@ -2089,6 +2120,7 @@ yyreduce: break; case 9: +/* Line 1787 of yacc.c */ #line 144 "cc.y" { dodecl(xdecl, lastclass, lasttype, (yyvsp[(1) - (1)].node)); @@ -2096,6 +2128,7 @@ yyreduce: break; case 10: +/* Line 1787 of yacc.c */ #line 148 "cc.y" { (yyvsp[(1) - (1)].node) = dodecl(xdecl, lastclass, lasttype, (yyvsp[(1) - (1)].node)); @@ -2103,6 +2136,7 @@ yyreduce: break; case 11: +/* Line 1787 of yacc.c */ #line 152 "cc.y" { doinit((yyvsp[(1) - (4)].node)->sym, (yyvsp[(1) - (4)].node)->type, 0L, (yyvsp[(4) - (4)].node)); @@ -2110,6 +2144,7 @@ yyreduce: break; case 14: +/* Line 1787 of yacc.c */ #line 160 "cc.y" { (yyval.node) = new(OIND, (yyvsp[(3) - (3)].node), Z); @@ -2118,6 +2153,7 @@ yyreduce: break; case 16: +/* Line 1787 of yacc.c */ #line 168 "cc.y" { (yyval.node) = (yyvsp[(2) - (3)].node); @@ -2125,6 +2161,7 @@ yyreduce: break; case 17: +/* Line 1787 of yacc.c */ #line 172 "cc.y" { (yyval.node) = new(OFUNC, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node)); @@ -2132,6 +2169,7 @@ yyreduce: break; case 18: +/* Line 1787 of yacc.c */ #line 176 "cc.y" { (yyval.node) = new(OARRAY, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node)); @@ -2139,6 +2177,7 @@ yyreduce: break; case 19: +/* Line 1787 of yacc.c */ #line 185 "cc.y" { (yyval.node) = dodecl(adecl, lastclass, lasttype, Z); @@ -2146,6 +2185,7 @@ yyreduce: break; case 20: +/* Line 1787 of yacc.c */ #line 189 "cc.y" { (yyval.node) = (yyvsp[(2) - (3)].node); @@ -2153,6 +2193,7 @@ yyreduce: break; case 21: +/* Line 1787 of yacc.c */ #line 195 "cc.y" { dodecl(adecl, lastclass, lasttype, (yyvsp[(1) - (1)].node)); @@ -2161,6 +2202,7 @@ yyreduce: break; case 22: +/* Line 1787 of yacc.c */ #line 200 "cc.y" { (yyvsp[(1) - (1)].node) = dodecl(adecl, lastclass, lasttype, (yyvsp[(1) - (1)].node)); @@ -2168,6 +2210,7 @@ yyreduce: break; case 23: +/* Line 1787 of yacc.c */ #line 204 "cc.y" { int32 w; @@ -2179,6 +2222,7 @@ yyreduce: break; case 24: +/* Line 1787 of yacc.c */ #line 212 "cc.y" { (yyval.node) = (yyvsp[(1) - (3)].node); @@ -2191,6 +2235,7 @@ yyreduce: break; case 27: +/* Line 1787 of yacc.c */ #line 229 "cc.y" { dodecl(pdecl, lastclass, lasttype, (yyvsp[(1) - (1)].node)); @@ -2198,6 +2243,7 @@ yyreduce: break; case 29: +/* Line 1787 of yacc.c */ #line 239 "cc.y" { lasttype = (yyvsp[(1) - (1)].type); @@ -2205,6 +2251,7 @@ yyreduce: break; case 31: +/* Line 1787 of yacc.c */ #line 244 "cc.y" { lasttype = (yyvsp[(2) - (2)].type); @@ -2212,6 +2259,7 @@ yyreduce: break; case 33: +/* Line 1787 of yacc.c */ #line 250 "cc.y" { lastfield = 0; @@ -2220,6 +2268,7 @@ yyreduce: break; case 35: +/* Line 1787 of yacc.c */ #line 258 "cc.y" { dodecl(edecl, CXXX, lasttype, (yyvsp[(1) - (1)].node)); @@ -2227,6 +2276,7 @@ yyreduce: break; case 37: +/* Line 1787 of yacc.c */ #line 265 "cc.y" { lastbit = 0; @@ -2235,6 +2285,7 @@ yyreduce: break; case 38: +/* Line 1787 of yacc.c */ #line 270 "cc.y" { (yyval.node) = new(OBIT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2242,6 +2293,7 @@ yyreduce: break; case 39: +/* Line 1787 of yacc.c */ #line 274 "cc.y" { (yyval.node) = new(OBIT, Z, (yyvsp[(2) - (2)].node)); @@ -2249,6 +2301,7 @@ yyreduce: break; case 40: +/* Line 1787 of yacc.c */ #line 282 "cc.y" { (yyval.node) = (Z); @@ -2256,6 +2309,7 @@ yyreduce: break; case 42: +/* Line 1787 of yacc.c */ #line 289 "cc.y" { (yyval.node) = new(OIND, (Z), Z); @@ -2264,6 +2318,7 @@ yyreduce: break; case 43: +/* Line 1787 of yacc.c */ #line 294 "cc.y" { (yyval.node) = new(OIND, (yyvsp[(3) - (3)].node), Z); @@ -2272,6 +2327,7 @@ yyreduce: break; case 46: +/* Line 1787 of yacc.c */ #line 303 "cc.y" { (yyval.node) = new(OFUNC, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node)); @@ -2279,6 +2335,7 @@ yyreduce: break; case 47: +/* Line 1787 of yacc.c */ #line 307 "cc.y" { (yyval.node) = new(OARRAY, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node)); @@ -2286,6 +2343,7 @@ yyreduce: break; case 48: +/* Line 1787 of yacc.c */ #line 313 "cc.y" { (yyval.node) = new(OFUNC, (Z), Z); @@ -2293,6 +2351,7 @@ yyreduce: break; case 49: +/* Line 1787 of yacc.c */ #line 317 "cc.y" { (yyval.node) = new(OARRAY, (Z), (yyvsp[(2) - (3)].node)); @@ -2300,6 +2359,7 @@ yyreduce: break; case 50: +/* Line 1787 of yacc.c */ #line 321 "cc.y" { (yyval.node) = (yyvsp[(2) - (3)].node); @@ -2307,6 +2367,7 @@ yyreduce: break; case 52: +/* Line 1787 of yacc.c */ #line 328 "cc.y" { (yyval.node) = new(OINIT, invert((yyvsp[(2) - (3)].node)), Z); @@ -2314,6 +2375,7 @@ yyreduce: break; case 53: +/* Line 1787 of yacc.c */ #line 334 "cc.y" { (yyval.node) = new(OARRAY, (yyvsp[(2) - (3)].node), Z); @@ -2321,6 +2383,7 @@ yyreduce: break; case 54: +/* Line 1787 of yacc.c */ #line 338 "cc.y" { (yyval.node) = new(OELEM, Z, Z); @@ -2329,6 +2392,7 @@ yyreduce: break; case 57: +/* Line 1787 of yacc.c */ #line 347 "cc.y" { (yyval.node) = new(OLIST, (yyvsp[(1) - (3)].node), (yyvsp[(2) - (3)].node)); @@ -2336,6 +2400,7 @@ yyreduce: break; case 59: +/* Line 1787 of yacc.c */ #line 352 "cc.y" { (yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node)); @@ -2343,6 +2408,7 @@ yyreduce: break; case 62: +/* Line 1787 of yacc.c */ #line 360 "cc.y" { (yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node)); @@ -2350,6 +2416,7 @@ yyreduce: break; case 63: +/* Line 1787 of yacc.c */ #line 365 "cc.y" { (yyval.node) = Z; @@ -2357,6 +2424,7 @@ yyreduce: break; case 64: +/* Line 1787 of yacc.c */ #line 369 "cc.y" { (yyval.node) = invert((yyvsp[(1) - (1)].node)); @@ -2364,6 +2432,7 @@ yyreduce: break; case 66: +/* Line 1787 of yacc.c */ #line 377 "cc.y" { (yyval.node) = new(OPROTO, (yyvsp[(2) - (2)].node), Z); @@ -2372,6 +2441,7 @@ yyreduce: break; case 67: +/* Line 1787 of yacc.c */ #line 382 "cc.y" { (yyval.node) = new(OPROTO, (yyvsp[(2) - (2)].node), Z); @@ -2380,6 +2450,7 @@ yyreduce: break; case 68: +/* Line 1787 of yacc.c */ #line 387 "cc.y" { (yyval.node) = new(ODOTDOT, Z, Z); @@ -2387,6 +2458,7 @@ yyreduce: break; case 69: +/* Line 1787 of yacc.c */ #line 391 "cc.y" { (yyval.node) = new(OLIST, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2394,6 +2466,7 @@ yyreduce: break; case 70: +/* Line 1787 of yacc.c */ #line 397 "cc.y" { (yyval.node) = invert((yyvsp[(2) - (3)].node)); @@ -2405,6 +2478,7 @@ yyreduce: break; case 71: +/* Line 1787 of yacc.c */ #line 406 "cc.y" { (yyval.node) = Z; @@ -2412,6 +2486,7 @@ yyreduce: break; case 72: +/* Line 1787 of yacc.c */ #line 410 "cc.y" { (yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node)); @@ -2419,6 +2494,7 @@ yyreduce: break; case 73: +/* Line 1787 of yacc.c */ #line 414 "cc.y" { (yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node)); @@ -2426,6 +2502,7 @@ yyreduce: break; case 75: +/* Line 1787 of yacc.c */ #line 421 "cc.y" { (yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node)); @@ -2433,6 +2510,7 @@ yyreduce: break; case 76: +/* Line 1787 of yacc.c */ #line 427 "cc.y" { (yyval.node) = new(OCASE, (yyvsp[(2) - (3)].node), Z); @@ -2440,6 +2518,7 @@ yyreduce: break; case 77: +/* Line 1787 of yacc.c */ #line 431 "cc.y" { (yyval.node) = new(OCASE, Z, Z); @@ -2447,6 +2526,7 @@ yyreduce: break; case 78: +/* Line 1787 of yacc.c */ #line 435 "cc.y" { (yyval.node) = new(OLABEL, dcllabel((yyvsp[(1) - (2)].sym), 1), Z); @@ -2454,6 +2534,7 @@ yyreduce: break; case 79: +/* Line 1787 of yacc.c */ #line 441 "cc.y" { (yyval.node) = Z; @@ -2461,6 +2542,7 @@ yyreduce: break; case 81: +/* Line 1787 of yacc.c */ #line 446 "cc.y" { (yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node)); @@ -2468,6 +2550,7 @@ yyreduce: break; case 83: +/* Line 1787 of yacc.c */ #line 453 "cc.y" { (yyval.node) = (yyvsp[(2) - (2)].node); @@ -2475,6 +2558,7 @@ yyreduce: break; case 85: +/* Line 1787 of yacc.c */ #line 459 "cc.y" { markdcl(); @@ -2482,6 +2566,7 @@ yyreduce: break; case 86: +/* Line 1787 of yacc.c */ #line 463 "cc.y" { (yyval.node) = revertdcl(); @@ -2493,6 +2578,7 @@ yyreduce: break; case 87: +/* Line 1787 of yacc.c */ #line 471 "cc.y" { (yyval.node) = new(OIF, (yyvsp[(3) - (5)].node), new(OLIST, (yyvsp[(5) - (5)].node), Z)); @@ -2502,6 +2588,7 @@ yyreduce: break; case 88: +/* Line 1787 of yacc.c */ #line 477 "cc.y" { (yyval.node) = new(OIF, (yyvsp[(3) - (7)].node), new(OLIST, (yyvsp[(5) - (7)].node), (yyvsp[(7) - (7)].node))); @@ -2513,11 +2600,13 @@ yyreduce: break; case 89: +/* Line 1787 of yacc.c */ #line 484 "cc.y" { markdcl(); } break; case 90: +/* Line 1787 of yacc.c */ #line 485 "cc.y" { (yyval.node) = revertdcl(); @@ -2532,6 +2621,7 @@ yyreduce: break; case 91: +/* Line 1787 of yacc.c */ #line 496 "cc.y" { (yyval.node) = new(OWHILE, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node)); @@ -2539,6 +2629,7 @@ yyreduce: break; case 92: +/* Line 1787 of yacc.c */ #line 500 "cc.y" { (yyval.node) = new(ODWHILE, (yyvsp[(5) - (7)].node), (yyvsp[(2) - (7)].node)); @@ -2546,6 +2637,7 @@ yyreduce: break; case 93: +/* Line 1787 of yacc.c */ #line 504 "cc.y" { (yyval.node) = new(ORETURN, (yyvsp[(2) - (3)].node), Z); @@ -2554,6 +2646,7 @@ yyreduce: break; case 94: +/* Line 1787 of yacc.c */ #line 509 "cc.y" { (yyval.node) = new(OCONST, Z, Z); @@ -2571,6 +2664,7 @@ yyreduce: break; case 95: +/* Line 1787 of yacc.c */ #line 523 "cc.y" { (yyval.node) = new(OBREAK, Z, Z); @@ -2578,6 +2672,7 @@ yyreduce: break; case 96: +/* Line 1787 of yacc.c */ #line 527 "cc.y" { (yyval.node) = new(OCONTINUE, Z, Z); @@ -2585,6 +2680,7 @@ yyreduce: break; case 97: +/* Line 1787 of yacc.c */ #line 531 "cc.y" { (yyval.node) = new(OGOTO, dcllabel((yyvsp[(2) - (3)].sym), 0), Z); @@ -2592,6 +2688,7 @@ yyreduce: break; case 98: +/* Line 1787 of yacc.c */ #line 535 "cc.y" { (yyval.node) = new(OUSED, (yyvsp[(3) - (5)].node), Z); @@ -2599,6 +2696,7 @@ yyreduce: break; case 99: +/* Line 1787 of yacc.c */ #line 539 "cc.y" { (yyval.node) = new(OPREFETCH, (yyvsp[(3) - (5)].node), Z); @@ -2606,6 +2704,7 @@ yyreduce: break; case 100: +/* Line 1787 of yacc.c */ #line 543 "cc.y" { (yyval.node) = new(OSET, (yyvsp[(3) - (5)].node), Z); @@ -2613,6 +2712,7 @@ yyreduce: break; case 101: +/* Line 1787 of yacc.c */ #line 548 "cc.y" { (yyval.node) = Z; @@ -2620,6 +2720,7 @@ yyreduce: break; case 103: +/* Line 1787 of yacc.c */ #line 554 "cc.y" { (yyval.node) = Z; @@ -2627,6 +2728,7 @@ yyreduce: break; case 105: +/* Line 1787 of yacc.c */ #line 561 "cc.y" { (yyval.node) = new(OCAST, (yyvsp[(1) - (1)].node), Z); @@ -2635,6 +2737,7 @@ yyreduce: break; case 107: +/* Line 1787 of yacc.c */ #line 569 "cc.y" { (yyval.node) = new(OCOMMA, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2642,6 +2745,7 @@ yyreduce: break; case 109: +/* Line 1787 of yacc.c */ #line 576 "cc.y" { (yyval.node) = new(OMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2649,6 +2753,7 @@ yyreduce: break; case 110: +/* Line 1787 of yacc.c */ #line 580 "cc.y" { (yyval.node) = new(ODIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2656,6 +2761,7 @@ yyreduce: break; case 111: +/* Line 1787 of yacc.c */ #line 584 "cc.y" { (yyval.node) = new(OMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2663,6 +2769,7 @@ yyreduce: break; case 112: +/* Line 1787 of yacc.c */ #line 588 "cc.y" { (yyval.node) = new(OADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2670,6 +2777,7 @@ yyreduce: break; case 113: +/* Line 1787 of yacc.c */ #line 592 "cc.y" { (yyval.node) = new(OSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2677,6 +2785,7 @@ yyreduce: break; case 114: +/* Line 1787 of yacc.c */ #line 596 "cc.y" { (yyval.node) = new(OASHR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2684,6 +2793,7 @@ yyreduce: break; case 115: +/* Line 1787 of yacc.c */ #line 600 "cc.y" { (yyval.node) = new(OASHL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2691,6 +2801,7 @@ yyreduce: break; case 116: +/* Line 1787 of yacc.c */ #line 604 "cc.y" { (yyval.node) = new(OLT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2698,6 +2809,7 @@ yyreduce: break; case 117: +/* Line 1787 of yacc.c */ #line 608 "cc.y" { (yyval.node) = new(OGT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2705,6 +2817,7 @@ yyreduce: break; case 118: +/* Line 1787 of yacc.c */ #line 612 "cc.y" { (yyval.node) = new(OLE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2712,6 +2825,7 @@ yyreduce: break; case 119: +/* Line 1787 of yacc.c */ #line 616 "cc.y" { (yyval.node) = new(OGE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2719,6 +2833,7 @@ yyreduce: break; case 120: +/* Line 1787 of yacc.c */ #line 620 "cc.y" { (yyval.node) = new(OEQ, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2726,6 +2841,7 @@ yyreduce: break; case 121: +/* Line 1787 of yacc.c */ #line 624 "cc.y" { (yyval.node) = new(ONE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2733,6 +2849,7 @@ yyreduce: break; case 122: +/* Line 1787 of yacc.c */ #line 628 "cc.y" { (yyval.node) = new(OAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2740,6 +2857,7 @@ yyreduce: break; case 123: +/* Line 1787 of yacc.c */ #line 632 "cc.y" { (yyval.node) = new(OXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2747,6 +2865,7 @@ yyreduce: break; case 124: +/* Line 1787 of yacc.c */ #line 636 "cc.y" { (yyval.node) = new(OOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2754,6 +2873,7 @@ yyreduce: break; case 125: +/* Line 1787 of yacc.c */ #line 640 "cc.y" { (yyval.node) = new(OANDAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2761,6 +2881,7 @@ yyreduce: break; case 126: +/* Line 1787 of yacc.c */ #line 644 "cc.y" { (yyval.node) = new(OOROR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2768,6 +2889,7 @@ yyreduce: break; case 127: +/* Line 1787 of yacc.c */ #line 648 "cc.y" { (yyval.node) = new(OCOND, (yyvsp[(1) - (5)].node), new(OLIST, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node))); @@ -2775,6 +2897,7 @@ yyreduce: break; case 128: +/* Line 1787 of yacc.c */ #line 652 "cc.y" { (yyval.node) = new(OAS, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2782,6 +2905,7 @@ yyreduce: break; case 129: +/* Line 1787 of yacc.c */ #line 656 "cc.y" { (yyval.node) = new(OASADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2789,6 +2913,7 @@ yyreduce: break; case 130: +/* Line 1787 of yacc.c */ #line 660 "cc.y" { (yyval.node) = new(OASSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2796,6 +2921,7 @@ yyreduce: break; case 131: +/* Line 1787 of yacc.c */ #line 664 "cc.y" { (yyval.node) = new(OASMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2803,6 +2929,7 @@ yyreduce: break; case 132: +/* Line 1787 of yacc.c */ #line 668 "cc.y" { (yyval.node) = new(OASDIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2810,6 +2937,7 @@ yyreduce: break; case 133: +/* Line 1787 of yacc.c */ #line 672 "cc.y" { (yyval.node) = new(OASMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2817,6 +2945,7 @@ yyreduce: break; case 134: +/* Line 1787 of yacc.c */ #line 676 "cc.y" { (yyval.node) = new(OASASHL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2824,6 +2953,7 @@ yyreduce: break; case 135: +/* Line 1787 of yacc.c */ #line 680 "cc.y" { (yyval.node) = new(OASASHR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2831,6 +2961,7 @@ yyreduce: break; case 136: +/* Line 1787 of yacc.c */ #line 684 "cc.y" { (yyval.node) = new(OASAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2838,6 +2969,7 @@ yyreduce: break; case 137: +/* Line 1787 of yacc.c */ #line 688 "cc.y" { (yyval.node) = new(OASXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2845,6 +2977,7 @@ yyreduce: break; case 138: +/* Line 1787 of yacc.c */ #line 692 "cc.y" { (yyval.node) = new(OASOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2852,6 +2985,7 @@ yyreduce: break; case 140: +/* Line 1787 of yacc.c */ #line 699 "cc.y" { (yyval.node) = new(OCAST, (yyvsp[(5) - (5)].node), Z); @@ -2862,6 +2996,7 @@ yyreduce: break; case 141: +/* Line 1787 of yacc.c */ #line 706 "cc.y" { (yyval.node) = new(OSTRUCT, (yyvsp[(6) - (7)].node), Z); @@ -2871,6 +3006,7 @@ yyreduce: break; case 143: +/* Line 1787 of yacc.c */ #line 715 "cc.y" { (yyval.node) = new(OIND, (yyvsp[(2) - (2)].node), Z); @@ -2878,6 +3014,7 @@ yyreduce: break; case 144: +/* Line 1787 of yacc.c */ #line 719 "cc.y" { (yyval.node) = new(OADDR, (yyvsp[(2) - (2)].node), Z); @@ -2885,6 +3022,7 @@ yyreduce: break; case 145: +/* Line 1787 of yacc.c */ #line 723 "cc.y" { (yyval.node) = new(OPOS, (yyvsp[(2) - (2)].node), Z); @@ -2892,6 +3030,7 @@ yyreduce: break; case 146: +/* Line 1787 of yacc.c */ #line 727 "cc.y" { (yyval.node) = new(ONEG, (yyvsp[(2) - (2)].node), Z); @@ -2899,6 +3038,7 @@ yyreduce: break; case 147: +/* Line 1787 of yacc.c */ #line 731 "cc.y" { (yyval.node) = new(ONOT, (yyvsp[(2) - (2)].node), Z); @@ -2906,6 +3046,7 @@ yyreduce: break; case 148: +/* Line 1787 of yacc.c */ #line 735 "cc.y" { (yyval.node) = new(OCOM, (yyvsp[(2) - (2)].node), Z); @@ -2913,6 +3054,7 @@ yyreduce: break; case 149: +/* Line 1787 of yacc.c */ #line 739 "cc.y" { (yyval.node) = new(OPREINC, (yyvsp[(2) - (2)].node), Z); @@ -2920,6 +3062,7 @@ yyreduce: break; case 150: +/* Line 1787 of yacc.c */ #line 743 "cc.y" { (yyval.node) = new(OPREDEC, (yyvsp[(2) - (2)].node), Z); @@ -2927,6 +3070,7 @@ yyreduce: break; case 151: +/* Line 1787 of yacc.c */ #line 747 "cc.y" { (yyval.node) = new(OSIZE, (yyvsp[(2) - (2)].node), Z); @@ -2934,6 +3078,7 @@ yyreduce: break; case 152: +/* Line 1787 of yacc.c */ #line 751 "cc.y" { (yyval.node) = new(OSIGN, (yyvsp[(2) - (2)].node), Z); @@ -2941,6 +3086,7 @@ yyreduce: break; case 153: +/* Line 1787 of yacc.c */ #line 757 "cc.y" { (yyval.node) = (yyvsp[(2) - (3)].node); @@ -2948,6 +3094,7 @@ yyreduce: break; case 154: +/* Line 1787 of yacc.c */ #line 761 "cc.y" { (yyval.node) = new(OSIZE, Z, Z); @@ -2957,6 +3104,7 @@ yyreduce: break; case 155: +/* Line 1787 of yacc.c */ #line 767 "cc.y" { (yyval.node) = new(OSIGN, Z, Z); @@ -2966,6 +3114,7 @@ yyreduce: break; case 156: +/* Line 1787 of yacc.c */ #line 773 "cc.y" { (yyval.node) = new(OFUNC, (yyvsp[(1) - (4)].node), Z); @@ -2977,6 +3126,7 @@ yyreduce: break; case 157: +/* Line 1787 of yacc.c */ #line 781 "cc.y" { (yyval.node) = new(OIND, new(OADD, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node)), Z); @@ -2984,6 +3134,7 @@ yyreduce: break; case 158: +/* Line 1787 of yacc.c */ #line 785 "cc.y" { (yyval.node) = new(ODOT, new(OIND, (yyvsp[(1) - (3)].node), Z), Z); @@ -2992,6 +3143,7 @@ yyreduce: break; case 159: +/* Line 1787 of yacc.c */ #line 790 "cc.y" { (yyval.node) = new(ODOT, (yyvsp[(1) - (3)].node), Z); @@ -3000,6 +3152,7 @@ yyreduce: break; case 160: +/* Line 1787 of yacc.c */ #line 795 "cc.y" { (yyval.node) = new(OPOSTINC, (yyvsp[(1) - (2)].node), Z); @@ -3007,6 +3160,7 @@ yyreduce: break; case 161: +/* Line 1787 of yacc.c */ #line 799 "cc.y" { (yyval.node) = new(OPOSTDEC, (yyvsp[(1) - (2)].node), Z); @@ -3014,6 +3168,7 @@ yyreduce: break; case 163: +/* Line 1787 of yacc.c */ #line 804 "cc.y" { (yyval.node) = new(OCONST, Z, Z); @@ -3024,6 +3179,7 @@ yyreduce: break; case 164: +/* Line 1787 of yacc.c */ #line 811 "cc.y" { (yyval.node) = new(OCONST, Z, Z); @@ -3034,6 +3190,7 @@ yyreduce: break; case 165: +/* Line 1787 of yacc.c */ #line 818 "cc.y" { (yyval.node) = new(OCONST, Z, Z); @@ -3044,6 +3201,7 @@ yyreduce: break; case 166: +/* Line 1787 of yacc.c */ #line 825 "cc.y" { (yyval.node) = new(OCONST, Z, Z); @@ -3054,6 +3212,7 @@ yyreduce: break; case 167: +/* Line 1787 of yacc.c */ #line 832 "cc.y" { (yyval.node) = new(OCONST, Z, Z); @@ -3064,6 +3223,7 @@ yyreduce: break; case 168: +/* Line 1787 of yacc.c */ #line 839 "cc.y" { (yyval.node) = new(OCONST, Z, Z); @@ -3074,6 +3234,7 @@ yyreduce: break; case 169: +/* Line 1787 of yacc.c */ #line 846 "cc.y" { (yyval.node) = new(OCONST, Z, Z); @@ -3084,6 +3245,7 @@ yyreduce: break; case 170: +/* Line 1787 of yacc.c */ #line 853 "cc.y" { (yyval.node) = new(OCONST, Z, Z); @@ -3094,6 +3256,7 @@ yyreduce: break; case 173: +/* Line 1787 of yacc.c */ #line 864 "cc.y" { (yyval.node) = new(OSTRING, Z, Z); @@ -3107,6 +3270,7 @@ yyreduce: break; case 174: +/* Line 1787 of yacc.c */ #line 874 "cc.y" { char *s; @@ -3126,12 +3290,13 @@ yyreduce: break; case 175: +/* Line 1787 of yacc.c */ #line 892 "cc.y" { (yyval.node) = new(OLSTRING, Z, Z); - (yyval.node)->type = typ(TARRAY, types[TUSHORT]); - (yyval.node)->type->width = (yyvsp[(1) - (1)].sval).l + sizeof(ushort); - (yyval.node)->rstring = (ushort*)(yyvsp[(1) - (1)].sval).s; + (yyval.node)->type = typ(TARRAY, types[TRUNE]); + (yyval.node)->type->width = (yyvsp[(1) - (1)].sval).l + sizeof(TRune); + (yyval.node)->rstring = (TRune*)(yyvsp[(1) - (1)].sval).s; (yyval.node)->sym = symstring; (yyval.node)->etype = TARRAY; (yyval.node)->class = CSTATIC; @@ -3139,25 +3304,27 @@ yyreduce: break; case 176: +/* Line 1787 of yacc.c */ #line 902 "cc.y" { char *s; int n; - n = (yyvsp[(1) - (2)].node)->type->width - sizeof(ushort); + n = (yyvsp[(1) - (2)].node)->type->width - sizeof(TRune); s = alloc(n+(yyvsp[(2) - (2)].sval).l+MAXALIGN); memcpy(s, (yyvsp[(1) - (2)].node)->rstring, n); memcpy(s+n, (yyvsp[(2) - (2)].sval).s, (yyvsp[(2) - (2)].sval).l); - *(ushort*)(s+n+(yyvsp[(2) - (2)].sval).l) = 0; + *(TRune*)(s+n+(yyvsp[(2) - (2)].sval).l) = 0; (yyval.node) = (yyvsp[(1) - (2)].node); (yyval.node)->type->width += (yyvsp[(2) - (2)].sval).l; - (yyval.node)->rstring = (ushort*)s; + (yyval.node)->rstring = (TRune*)s; } break; case 177: +/* Line 1787 of yacc.c */ #line 919 "cc.y" { (yyval.node) = Z; @@ -3165,6 +3332,7 @@ yyreduce: break; case 180: +/* Line 1787 of yacc.c */ #line 927 "cc.y" { (yyval.node) = new(OLIST, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -3172,6 +3340,7 @@ yyreduce: break; case 181: +/* Line 1787 of yacc.c */ #line 933 "cc.y" { (yyval.tyty).t1 = strf; @@ -3188,6 +3357,7 @@ yyreduce: break; case 182: +/* Line 1787 of yacc.c */ #line 946 "cc.y" { (yyval.type) = strf; @@ -3199,6 +3369,7 @@ yyreduce: break; case 183: +/* Line 1787 of yacc.c */ #line 955 "cc.y" { lastclass = CXXX; @@ -3207,6 +3378,7 @@ yyreduce: break; case 185: +/* Line 1787 of yacc.c */ #line 963 "cc.y" { (yyval.tycl).t = (yyvsp[(1) - (1)].type); @@ -3215,6 +3387,7 @@ yyreduce: break; case 186: +/* Line 1787 of yacc.c */ #line 968 "cc.y" { (yyval.tycl).t = simplet((yyvsp[(1) - (1)].lval)); @@ -3223,6 +3396,7 @@ yyreduce: break; case 187: +/* Line 1787 of yacc.c */ #line 973 "cc.y" { (yyval.tycl).t = simplet((yyvsp[(1) - (1)].lval)); @@ -3232,6 +3406,7 @@ yyreduce: break; case 188: +/* Line 1787 of yacc.c */ #line 979 "cc.y" { (yyval.tycl).t = (yyvsp[(1) - (2)].type); @@ -3243,6 +3418,7 @@ yyreduce: break; case 189: +/* Line 1787 of yacc.c */ #line 987 "cc.y" { (yyval.tycl).t = simplet(typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval))); @@ -3252,6 +3428,7 @@ yyreduce: break; case 190: +/* Line 1787 of yacc.c */ #line 993 "cc.y" { (yyval.tycl).t = (yyvsp[(2) - (3)].type); @@ -3261,6 +3438,7 @@ yyreduce: break; case 191: +/* Line 1787 of yacc.c */ #line 999 "cc.y" { (yyval.tycl).t = simplet((yyvsp[(2) - (2)].lval)); @@ -3270,6 +3448,7 @@ yyreduce: break; case 192: +/* Line 1787 of yacc.c */ #line 1005 "cc.y" { (yyval.tycl).t = simplet(typebitor((yyvsp[(2) - (3)].lval), (yyvsp[(3) - (3)].lval))); @@ -3279,6 +3458,7 @@ yyreduce: break; case 193: +/* Line 1787 of yacc.c */ #line 1013 "cc.y" { (yyval.type) = (yyvsp[(1) - (1)].tycl).t; @@ -3288,6 +3468,7 @@ yyreduce: break; case 194: +/* Line 1787 of yacc.c */ #line 1021 "cc.y" { lasttype = (yyvsp[(1) - (1)].tycl).t; @@ -3296,6 +3477,7 @@ yyreduce: break; case 195: +/* Line 1787 of yacc.c */ #line 1028 "cc.y" { dotag((yyvsp[(2) - (2)].sym), TSTRUCT, 0); @@ -3304,6 +3486,7 @@ yyreduce: break; case 196: +/* Line 1787 of yacc.c */ #line 1033 "cc.y" { dotag((yyvsp[(2) - (2)].sym), TSTRUCT, autobn); @@ -3311,6 +3494,7 @@ yyreduce: break; case 197: +/* Line 1787 of yacc.c */ #line 1037 "cc.y" { (yyval.type) = (yyvsp[(2) - (4)].sym)->suetag; @@ -3322,6 +3506,7 @@ yyreduce: break; case 198: +/* Line 1787 of yacc.c */ #line 1045 "cc.y" { taggen++; @@ -3333,6 +3518,7 @@ yyreduce: break; case 199: +/* Line 1787 of yacc.c */ #line 1053 "cc.y" { dotag((yyvsp[(2) - (2)].sym), TUNION, 0); @@ -3341,6 +3527,7 @@ yyreduce: break; case 200: +/* Line 1787 of yacc.c */ #line 1058 "cc.y" { dotag((yyvsp[(2) - (2)].sym), TUNION, autobn); @@ -3348,6 +3535,7 @@ yyreduce: break; case 201: +/* Line 1787 of yacc.c */ #line 1062 "cc.y" { (yyval.type) = (yyvsp[(2) - (4)].sym)->suetag; @@ -3359,6 +3547,7 @@ yyreduce: break; case 202: +/* Line 1787 of yacc.c */ #line 1070 "cc.y" { taggen++; @@ -3370,6 +3559,7 @@ yyreduce: break; case 203: +/* Line 1787 of yacc.c */ #line 1078 "cc.y" { dotag((yyvsp[(2) - (2)].sym), TENUM, 0); @@ -3381,6 +3571,7 @@ yyreduce: break; case 204: +/* Line 1787 of yacc.c */ #line 1086 "cc.y" { dotag((yyvsp[(2) - (2)].sym), TENUM, autobn); @@ -3388,6 +3579,7 @@ yyreduce: break; case 205: +/* Line 1787 of yacc.c */ #line 1090 "cc.y" { en.tenum = T; @@ -3396,6 +3588,7 @@ yyreduce: break; case 206: +/* Line 1787 of yacc.c */ #line 1095 "cc.y" { (yyval.type) = (yyvsp[(2) - (7)].sym)->suetag; @@ -3411,6 +3604,7 @@ yyreduce: break; case 207: +/* Line 1787 of yacc.c */ #line 1107 "cc.y" { en.tenum = T; @@ -3419,6 +3613,7 @@ yyreduce: break; case 208: +/* Line 1787 of yacc.c */ #line 1112 "cc.y" { (yyval.type) = en.tenum; @@ -3426,6 +3621,7 @@ yyreduce: break; case 209: +/* Line 1787 of yacc.c */ #line 1116 "cc.y" { (yyval.type) = tcopy((yyvsp[(1) - (1)].sym)->type); @@ -3433,6 +3629,7 @@ yyreduce: break; case 211: +/* Line 1787 of yacc.c */ #line 1123 "cc.y" { (yyval.lval) = typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval)); @@ -3440,6 +3637,7 @@ yyreduce: break; case 212: +/* Line 1787 of yacc.c */ #line 1128 "cc.y" { (yyval.lval) = 0; @@ -3447,6 +3645,7 @@ yyreduce: break; case 213: +/* Line 1787 of yacc.c */ #line 1132 "cc.y" { (yyval.lval) = typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval)); @@ -3454,6 +3653,7 @@ yyreduce: break; case 218: +/* Line 1787 of yacc.c */ #line 1144 "cc.y" { (yyval.lval) = typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval)); @@ -3461,6 +3661,7 @@ yyreduce: break; case 221: +/* Line 1787 of yacc.c */ #line 1154 "cc.y" { doenum((yyvsp[(1) - (1)].sym), Z); @@ -3468,6 +3669,7 @@ yyreduce: break; case 222: +/* Line 1787 of yacc.c */ #line 1158 "cc.y" { doenum((yyvsp[(1) - (3)].sym), (yyvsp[(3) - (3)].node)); @@ -3475,101 +3677,121 @@ yyreduce: break; case 225: +/* Line 1787 of yacc.c */ #line 1165 "cc.y" { (yyval.lval) = BCHAR; } break; case 226: +/* Line 1787 of yacc.c */ #line 1166 "cc.y" { (yyval.lval) = BSHORT; } break; case 227: +/* Line 1787 of yacc.c */ #line 1167 "cc.y" { (yyval.lval) = BINT; } break; case 228: +/* Line 1787 of yacc.c */ #line 1168 "cc.y" { (yyval.lval) = BLONG; } break; case 229: +/* Line 1787 of yacc.c */ #line 1169 "cc.y" { (yyval.lval) = BSIGNED; } break; case 230: +/* Line 1787 of yacc.c */ #line 1170 "cc.y" { (yyval.lval) = BUNSIGNED; } break; case 231: +/* Line 1787 of yacc.c */ #line 1171 "cc.y" { (yyval.lval) = BFLOAT; } break; case 232: +/* Line 1787 of yacc.c */ #line 1172 "cc.y" { (yyval.lval) = BDOUBLE; } break; case 233: +/* Line 1787 of yacc.c */ #line 1173 "cc.y" { (yyval.lval) = BVOID; } break; case 234: +/* Line 1787 of yacc.c */ #line 1176 "cc.y" { (yyval.lval) = BAUTO; } break; case 235: +/* Line 1787 of yacc.c */ #line 1177 "cc.y" { (yyval.lval) = BSTATIC; } break; case 236: +/* Line 1787 of yacc.c */ #line 1178 "cc.y" { (yyval.lval) = BEXTERN; } break; case 237: +/* Line 1787 of yacc.c */ #line 1179 "cc.y" { (yyval.lval) = BTYPEDEF; } break; case 238: +/* Line 1787 of yacc.c */ #line 1180 "cc.y" { (yyval.lval) = BTYPESTR; } break; case 239: +/* Line 1787 of yacc.c */ #line 1181 "cc.y" { (yyval.lval) = BREGISTER; } break; case 240: +/* Line 1787 of yacc.c */ #line 1182 "cc.y" { (yyval.lval) = 0; } break; case 241: +/* Line 1787 of yacc.c */ #line 1185 "cc.y" { (yyval.lval) = BCONSTNT; } break; case 242: +/* Line 1787 of yacc.c */ #line 1186 "cc.y" { (yyval.lval) = BVOLATILE; } break; case 243: +/* Line 1787 of yacc.c */ #line 1187 "cc.y" { (yyval.lval) = 0; } break; case 244: +/* Line 1787 of yacc.c */ #line 1191 "cc.y" { (yyval.node) = new(ONAME, Z, Z); @@ -3587,6 +3809,7 @@ yyreduce: break; case 245: +/* Line 1787 of yacc.c */ #line 1206 "cc.y" { (yyval.node) = new(ONAME, Z, Z); @@ -3601,10 +3824,21 @@ yyreduce: break; -/* Line 1267 of yacc.c. */ -#line 3606 "y.tab.c" +/* Line 1787 of yacc.c */ +#line 3829 "y.tab.c" default: break; } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); @@ -3613,7 +3847,6 @@ yyreduce: *++yyvsp = yyval; - /* Now `shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ @@ -3633,6 +3866,10 @@ yyreduce: | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { @@ -3640,37 +3877,36 @@ yyerrlab: #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) { - YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); - if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) - { - YYSIZE_T yyalloc = 2 * yysize; - if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) - yyalloc = YYSTACK_ALLOC_MAXIMUM; - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); - yymsg = (char *) YYSTACK_ALLOC (yyalloc); - if (yymsg) - yymsg_alloc = yyalloc; - else - { - yymsg = yymsgbuf; - yymsg_alloc = sizeof yymsgbuf; - } - } - - if (0 < yysize && yysize <= yymsg_alloc) - { - (void) yysyntax_error (yymsg, yystate, yychar); - yyerror (yymsg); - } - else - { - yyerror (YY_("syntax error")); - if (yysize != 0) - goto yyexhaustedlab; - } + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; } +# undef YYSYNTAX_ERROR #endif } @@ -3678,7 +3914,7 @@ yyerrlab: if (yyerrstatus == 3) { - /* If just tried and failed to reuse look-ahead token after an + /* If just tried and failed to reuse lookahead token after an error, discard it. */ if (yychar <= YYEOF) @@ -3695,7 +3931,7 @@ yyerrlab: } } - /* Else will try to reuse look-ahead token after shifting the error + /* Else will try to reuse lookahead token after shifting the error token. */ goto yyerrlab1; @@ -3729,7 +3965,7 @@ yyerrlab1: for (;;) { yyn = yypact[yystate]; - if (yyn != YYPACT_NINF) + if (!yypact_value_is_default (yyn)) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) @@ -3752,10 +3988,9 @@ yyerrlab1: YY_STACK_PRINT (yyss, yyssp); } - if (yyn == YYFINAL) - YYACCEPT; - + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END /* Shift the error token. */ @@ -3779,7 +4014,7 @@ yyabortlab: yyresult = 1; goto yyreturn; -#ifndef yyoverflow +#if !defined yyoverflow || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ @@ -3790,9 +4025,14 @@ yyexhaustedlab: #endif yyreturn: - if (yychar != YYEOF && yychar != YYEMPTY) - yydestruct ("Cleanup: discarding lookahead", - yytoken, &yylval); + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + } /* Do not reclaim the symbols of the rule which action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); @@ -3816,6 +4056,6 @@ yyreturn: } +/* Line 2050 of yacc.c */ #line 1219 "cc.y" - diff --git a/src/cmd/cc/y.tab.h b/src/cmd/cc/y.tab.h index 32daca9b6..b26d659ef 100644 --- a/src/cmd/cc/y.tab.h +++ b/src/cmd/cc/y.tab.h @@ -1,24 +1,21 @@ -/* A Bison parser, made by GNU Bison 2.3. */ +/* A Bison parser, made by GNU Bison 2.7.12-4996. */ -/* Skeleton interface for Bison's Yacc-like parsers in C - - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 - Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ + along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work @@ -29,10 +26,20 @@ special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. - + This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ +#ifndef YY_YY_Y_TAB_H_INCLUDED +# define YY_YY_Y_TAB_H_INCLUDED +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif + /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE @@ -189,11 +196,12 @@ - #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE -#line 36 "cc.y" { +/* Line 2053 of yacc.c */ +#line 36 "cc.y" + Node* node; Sym* sym; Type* type; @@ -217,14 +225,30 @@ typedef union YYSTYPE int32 lval; double dval; vlong vval; -} -/* Line 1529 of yacc.c. */ -#line 223 "y.tab.h" - YYSTYPE; + + +/* Line 2053 of yacc.c */ +#line 232 "y.tab.h" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 -# define YYSTYPE_IS_TRIVIAL 1 #endif extern YYSTYPE yylval; +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + +#endif /* !YY_YY_Y_TAB_H_INCLUDED */ diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go index dbae3b7b1..7757efa1b 100644 --- a/src/cmd/cgo/ast.go +++ b/src/cmd/cgo/ast.go @@ -13,6 +13,7 @@ import ( "go/scanner" "go/token" "os" + "path/filepath" "strings" ) @@ -44,6 +45,13 @@ func sourceLine(n ast.Node) int { // a list of exported functions, and the actual AST, to be rewritten and // printed. func (f *File) ReadGo(name string) { + // Create absolute path for file, so that it will be used in error + // messages and recorded in debug line number information. + // This matches the rest of the toolchain. See golang.org/issue/5122. + if aname, err := filepath.Abs(name); err == nil { + name = aname + } + // Two different parses: once with comments, once without. // The printer is not good enough at printing comments in the // right place when we start editing the AST behind its back, @@ -179,6 +187,13 @@ func (f *File) saveRef(x interface{}, context string) { error_(sel.Pos(), "cannot refer to errno directly; see documentation") return } + if goname == "_CMalloc" { + error_(sel.Pos(), "cannot refer to C._CMalloc; use C.malloc") + return + } + if goname == "malloc" { + goname = "_CMalloc" + } name := f.Name[goname] if name == nil { name = &Name{ diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go index a1b02d4be..605bab6d2 100644 --- a/src/cmd/cgo/doc.go +++ b/src/cmd/cgo/doc.go @@ -6,15 +6,11 @@ Cgo enables the creation of Go packages that call C code. -Usage: - go tool cgo [compiler options] file.go - -The compiler options are passed through uninterpreted when -invoking gcc to compile the C parts of the package. +Using cgo with the go command -The input file.go is a syntactically valid Go source file that imports -the pseudo-package "C" and then refers to types such as C.size_t, -variables such as C.stdout, or functions such as C.putchar. +To use cgo write normal Go code that imports a pseudo-package "C". +The Go code can then refer to types such as C.size_t, variables such +as C.stdout, or functions such as C.putchar. If the import of "C" is immediately preceded by a comment, that comment, called the preamble, is used as a header when compiling @@ -24,19 +20,25 @@ the C parts of the package. For example: // #include <errno.h> import "C" -CFLAGS and LDFLAGS may be defined with pseudo #cgo directives -within these comments to tweak the behavior of gcc. Values defined -in multiple directives are concatenated together. Options prefixed -by $GOOS, $GOARCH, or $GOOS/$GOARCH are only defined in matching -systems. For example: +See $GOROOT/misc/cgo/stdio and $GOROOT/misc/cgo/gmp for examples. See +"C? Go? Cgo!" for an introduction to using cgo: +http://golang.org/doc/articles/c_go_cgo.html. + +CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS may be defined with pseudo #cgo +directives within these comments to tweak the behavior of the C or C++ +compiler. Values defined in multiple directives are concatenated +together. The directive can include a list of build constraints limiting its +effect to systems satisfying one of the constraints +(see http://golang.org/pkg/go/build/#hdr-Build_Constraints for details about the constraint syntax). +For example: // #cgo CFLAGS: -DPNG_DEBUG=1 - // #cgo linux CFLAGS: -DLINUX=1 + // #cgo amd64 386 CFLAGS: -DX86=1 // #cgo LDFLAGS: -lpng // #include <png.h> import "C" -Alternatively, CFLAGS and LDFLAGS may be obtained via the pkg-config +Alternatively, CPPFLAGS and LDFLAGS may be obtained via the pkg-config tool using a '#cgo pkg-config:' directive followed by the package names. For example: @@ -44,12 +46,26 @@ For example: // #include <png.h> import "C" -The CGO_CFLAGS and CGO_LDFLAGS environment variables are added -to the flags derived from these directives. Package-specific flags should -be set using the directives, not the environment variables, so that builds -work in unmodified environments. - -Within the Go file, C identifiers or field names that are keywords in Go +When building, the CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS and +CGO_LDFLAGS environment variables are added to the flags derived from +these directives. Package-specific flags should be set using the +directives, not the environment variables, so that builds work in +unmodified environments. + +When the Go tool sees that one or more Go files use the special import +"C", it will look for other non-Go files in the directory and compile +them as part of the Go package. Any .c, .s, or .S files will be +compiled with the C compiler. Any .cc, .cpp, or .cxx files will be +compiled with the C++ compiler. Any .h, .hh, .hpp, or .hxx files will +not be compiled separately, but, if these header files are changed, +the C and C++ files will be recompiled. The default C and C++ +compilers may be changed by the CC and CXX environment variables, +respectively; those environment variables may include command line +options. + +Go references to C + +Within the Go file, C's struct field names that are keywords in Go can be accessed by prefixing them with an underscore: if x points at a C struct with a field named "type", x._type accesses the field. @@ -63,6 +79,9 @@ The C type void* is represented by Go's unsafe.Pointer. To access a struct, union, or enum type directly, prefix it with struct_, union_, or enum_, as in C.struct_stat. +As Go doesn't have support for C's union type in the general case, +C's union types are represented as a Go byte array with the same length. + Go structs cannot embed fields with C types. Any C function (even void functions) may be called in a multiple @@ -73,6 +92,34 @@ function returns void). For example: n, err := C.sqrt(-1) _, err := C.voidFunc() +Calling C function pointers is currently not supported, however you can +declare Go variables which hold C function pointers and pass them +back and forth between Go and C. C code may call function pointers +received from Go. For example: + + package main + + // typedef int (*intFunc) (); + // + // int + // bridge_int_func(intFunc f) + // { + // return f(); + // } + // + // int fortytwo() + // { + // return 42; + // } + import "C" + import "fmt" + + func main() { + f := C.intFunc(C.fortytwo) + fmt.Println(int(C.bridge_int_func(f))) + // Output: 42 + } + In C, a function argument written as a fixed size array actually requires a pointer to the first element of the array. C compilers are aware of this calling convention and adjust @@ -98,6 +145,8 @@ by making copies of the data. In pseudo-Go definitions: // C pointer, length to Go []byte func C.GoBytes(unsafe.Pointer, C.int) []byte +C references to Go + Go functions can be exported for use by C code in the following way: //export MyFunction @@ -111,7 +160,7 @@ They will be available in the C code as: extern int64 MyFunction(int arg1, int arg2, GoString arg3); extern struct MyFunction2_return MyFunction2(int arg1, int arg2, GoString arg3); -found in _cgo_export.h generated header, after any preambles +found in the _cgo_export.h generated header, after any preambles copied from the cgo input files. Functions with multiple return values are mapped to functions returning a struct. Not all Go types can be mapped to C types in a useful way. @@ -121,17 +170,54 @@ since it is copied into two different C output files, it must not contain any definitions, only declarations. Definitions must be placed in preambles in other files, or in C source files. -Cgo transforms the input file into four output files: two Go source -files, a C file for 6c (or 8c or 5c), and a C file for gcc. +Using cgo directly -The standard package construction rules of the go command -automate the process of using cgo. See $GOROOT/misc/cgo/stdio -and $GOROOT/misc/cgo/gmp for examples. +Usage: + go tool cgo [cgo options] [-- compiler options] file.go -Cgo does not yet work with gccgo. +Cgo transforms the input file.go into four output files: two Go source +files, a C file for 6c (or 8c or 5c), and a C file for gcc. -See "C? Go? Cgo!" for an introduction to using cgo: -http://golang.org/doc/articles/c_go_cgo.html +The compiler options are passed through uninterpreted when +invoking the C compiler to compile the C parts of the package. + +The following options are available when running cgo directly: + + -dynimport file + Write list of symbols imported by file. Write to + -dynout argument or to standard output. Used by go + build when building a cgo package. + -dynout file + Write -dynimport output to file. + -dynlinker + Write dynamic linker as part of -dynimport output. + -godefs + Write out input file in Go syntax replacing C package + names with real values. Used to generate files in the + syscall package when bootstrapping a new target. + -cdefs + Like -godefs, but write file in C syntax. + Used to generate files in the runtime package when + bootstrapping a new target. + -objdir directory + Put all generated files in directory. + -gccgo + Generate output for the gccgo compiler rather than the + gc compiler. + -gccgoprefix prefix + The -fgo-prefix option to be used with gccgo. + -gccgopkgpath path + The -fgo-pkgpath option to be used with gccgo. + -import_runtime_cgo + If set (which it is by default) import runtime/cgo in + generated output. + -import_syscall + If set (which it is by default) import syscall in + generated output. + -debug-define + Debugging option. Print #defines. + -debug-gcc + Debugging option. Trace C compiler execution and output. */ package main @@ -183,29 +269,30 @@ Next, cgo needs to identify the kinds for each identifier. For the identifiers C.foo and C.bar, cgo generates this C program: <preamble> - void __cgo__f__(void) { - #line 1 "cgo-test" - foo; - enum { _cgo_enum_0 = foo }; - bar; - enum { _cgo_enum_1 = bar }; - } - -This program will not compile, but cgo can look at the error messages -to infer the kind of each identifier. The line number given in the -error tells cgo which identifier is involved. - -An error like "unexpected type name" or "useless type name in empty -declaration" or "declaration does not declare anything" tells cgo that -the identifier is a type. - -An error like "statement with no effect" or "expression result unused" -tells cgo that the identifier is not a type, but not whether it is a -constant, function, or global variable. - -An error like "not an integer constant" tells cgo that the identifier -is not a constant. If it is also not a type, it must be a function or -global variable. For now, those can be treated the same. + #line 1 "not-declared" + void __cgo_f_xxx_1(void) { __typeof__(foo) *__cgo_undefined__; } + #line 1 "not-type" + void __cgo_f_xxx_2(void) { foo *__cgo_undefined__; } + #line 1 "not-const" + void __cgo_f_xxx_3(void) { enum { __cgo_undefined__ = (foo)*1 }; } + #line 2 "not-declared" + void __cgo_f_xxx_1(void) { __typeof__(bar) *__cgo_undefined__; } + #line 2 "not-type" + void __cgo_f_xxx_2(void) { bar *__cgo_undefined__; } + #line 2 "not-const" + void __cgo_f_xxx_3(void) { enum { __cgo_undefined__ = (bar)*1 }; } + +This program will not compile, but cgo can use the presence or absence +of an error message on a given line to deduce the information it +needs. The program is syntactically valid regardless of whether each +name is a type or an ordinary identifier, so there will be no syntax +errors that might stop parsing early. + +An error on not-declared:1 indicates that foo is undeclared. +An error on not-type:1 indicates that foo is not a type (if declared at all, it is an identifier). +An error on not-const:1 indicates that foo is not an integer constant. + +The line number specifies the name involved. In the example, 1 is foo and 2 is bar. Next, cgo must learn the details of each type, variable, function, or constant. It can do this by reading object files. If cgo has decided @@ -213,14 +300,14 @@ that t1 is a type, v2 and v3 are variables or functions, and c4, c5, and c6 are constants, it generates: <preamble> - typeof(t1) *__cgo__1; - typeof(v2) *__cgo__2; - typeof(v3) *__cgo__3; - typeof(c4) *__cgo__4; + __typeof__(t1) *__cgo__1; + __typeof__(v2) *__cgo__2; + __typeof__(v3) *__cgo__3; + __typeof__(c4) *__cgo__4; enum { __cgo_enum__4 = c4 }; - typeof(c5) *__cgo__5; + __typeof__(c5) *__cgo__5; enum { __cgo_enum__5 = c5 }; - typeof(c6) *__cgo__6; + __typeof__(c6) *__cgo__6; enum { __cgo_enum__6 = c6 }; long long __cgo_debug_data[] = { @@ -310,7 +397,7 @@ file compiled by gcc, the file x.cgo2.c: char* p0; int r; char __pad12[4]; - } __attribute__((__packed__)) *a = v; + } __attribute__((__packed__, __gcc_struct__)) *a = v; a->r = puts((void*)a->p0); } @@ -396,8 +483,6 @@ and libcgo_thread_start to a gcc-compiled function that can be used to create a new thread, in place of the runtime's usual direct system calls. -[NOTE: From here down is planned but not yet implemented.] - Internal and External Linking The text above describes "internal" linking, in which 6l parses and @@ -615,15 +700,18 @@ the godoc binary, which uses net but no other cgo, can run without needing gcc available. The second rule means that a build of a cgo-wrapped library like sqlite3 can generate a standalone executable instead of needing to refer to a dynamic library. The specific choice -can be overridden using a command line flag: 6l -cgolink=internal or -6l -cgolink=external. +can be overridden using a command line flag: 6l -linkmode=internal or +6l -linkmode=external. In an external link, 6l will create a temporary directory, write any host object files found in package archives to that directory (renamed to avoid conflicts), write the go.o file to that directory, and invoke the host linker. The default value for the host linker is $CC, split into fields, or else "gcc". The specific host linker command line can -be overridden using a command line flag: 6l -hostld='gcc -ggdb' +be overridden using command line flags: 6l -extld=clang +-extldflags='-ggdb -O3'. If any package in a build includes a .cc or +other file compiled by the C++ compiler, the go tool will use the +-extld option to set the host linker to the C++ compiler. These defaults mean that Go-aware build systems can ignore the linking changes and keep running plain '6l' and get reasonable results, but diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index bc7a6472f..3e1837ebf 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -76,6 +76,8 @@ func (f *File) DiscardCgoDirectives() { l := strings.TrimSpace(line) if len(l) < 5 || l[:4] != "#cgo" || !unicode.IsSpace(rune(l[4])) { linesOut = append(linesOut, line) + } else { + linesOut = append(linesOut, "") } } f.Preamble = strings.Join(linesOut, "\n") @@ -186,8 +188,8 @@ func (p *Package) Translate(f *File) { // in the file f and saves relevant renamings in f.Name[name].Define. func (p *Package) loadDefines(f *File) { var b bytes.Buffer - b.WriteString(builtinProlog) b.WriteString(f.Preamble) + b.WriteString(builtinProlog) stdout := p.gccDefines(b.Bytes()) for _, line := range strings.Split(stdout, "\n") { @@ -224,42 +226,22 @@ func (p *Package) loadDefines(f *File) { // name xxx for the references C.xxx in the Go input. // The kind is either a constant, type, or variable. func (p *Package) guessKinds(f *File) []*Name { - // Coerce gcc into telling us whether each name is - // a type, a value, or undeclared. We compile a function - // containing the line: - // name; - // If name is a type, gcc will print: - // cgo-test:2: warning: useless type name in empty declaration - // If name is a value, gcc will print - // cgo-test:2: warning: statement with no effect - // If name is undeclared, gcc will print - // cgo-test:2: error: 'name' undeclared (first use in this function) - // A line number directive causes the line number to - // correspond to the index in the names array. - // - // The line also has an enum declaration: - // name; enum { _cgo_enum_1 = name }; - // If name is not a constant, gcc will print: - // cgo-test:4: error: enumerator value for '_cgo_enum_4' is not an integer constant - // we assume lines without that error are constants. - - // Make list of names that need sniffing, type lookup. - toSniff := make([]*Name, 0, len(f.Name)) - needType := make([]*Name, 0, len(f.Name)) - + // Determine kinds for names we already know about, + // like #defines or 'struct foo', before bothering with gcc. + var names, needType []*Name for _, n := range f.Name { // If we've already found this name as a #define // and we can translate it as a constant value, do so. if n.Define != "" { - ok := false + isConst := false if _, err := strconv.Atoi(n.Define); err == nil { - ok = true + isConst = true } else if n.Define[0] == '"' || n.Define[0] == '\'' { if _, err := parser.ParseExpr(n.Define); err == nil { - ok = true + isConst = true } } - if ok { + if isConst { n.Kind = "const" // Turn decimal into hex, just for consistency // with enum-derived constants. Otherwise @@ -279,113 +261,139 @@ func (p *Package) guessKinds(f *File) []*Name { } } - // If this is a struct, union, or enum type name, - // record the kind but also that we need type information. + needType = append(needType, n) + + // If this is a struct, union, or enum type name, no need to guess the kind. if strings.HasPrefix(n.C, "struct ") || strings.HasPrefix(n.C, "union ") || strings.HasPrefix(n.C, "enum ") { n.Kind = "type" - i := len(needType) - needType = needType[0 : i+1] - needType[i] = n continue } - i := len(toSniff) - toSniff = toSniff[0 : i+1] - toSniff[i] = n + // Otherwise, we'll need to find out from gcc. + names = append(names, n) } - if len(toSniff) == 0 { + // Bypass gcc if there's nothing left to find out. + if len(names) == 0 { return needType } + // Coerce gcc into telling us whether each name is a type, a value, or undeclared. + // For names, find out whether they are integer constants. + // We used to look at specific warning or error messages here, but that tied the + // behavior too closely to specific versions of the compilers. + // Instead, arrange that we can infer what we need from only the presence or absence + // of an error on a specific line. + // + // For each name, we generate these lines, where xxx is the index in toSniff plus one. + // + // #line xxx "not-declared" + // void __cgo_f_xxx_1(void) { __typeof__(name) *__cgo_undefined__; } + // #line xxx "not-type" + // void __cgo_f_xxx_2(void) { name *__cgo_undefined__; } + // #line xxx "not-const" + // void __cgo_f_xxx_3(void) { enum { __cgo_undefined__ = (name)*1 }; } + // + // If we see an error at not-declared:xxx, the corresponding name is not declared. + // If we see an error at not-type:xxx, the corresponding name is a type. + // If we see an error at not-const:xxx, the corresponding name is not an integer constant. + // If we see no errors, we assume the name is an expression but not a constant + // (so a variable or a function). + // + // The specific input forms are chosen so that they are valid C syntax regardless of + // whether name denotes a type or an expression. + var b bytes.Buffer - b.WriteString(builtinProlog) b.WriteString(f.Preamble) - b.WriteString("void __cgo__f__(void) {\n") - b.WriteString("#line 1 \"cgo-test\"\n") - for i, n := range toSniff { - fmt.Fprintf(&b, "%s; /* #%d */\nenum { _cgo_enum_%d = %s }; /* #%d */\n", n.C, i, i, n.C, i) - } - b.WriteString("}\n") - stderr := p.gccErrors(b.Bytes()) - if stderr == "" { - fatalf("gcc produced no output\non input:\n%s", b.Bytes()) - } + b.WriteString(builtinProlog) - names := make([]*Name, len(toSniff)) - copy(names, toSniff) + for i, n := range names { + fmt.Fprintf(&b, "#line %d \"not-declared\"\n"+ + "void __cgo_f_%d_1(void) { __typeof__(%s) *__cgo_undefined__; }\n"+ + "#line %d \"not-type\"\n"+ + "void __cgo_f_%d_2(void) { %s *__cgo_undefined__; }\n"+ + "#line %d \"not-const\"\n"+ + "void __cgo_f_%d_3(void) { enum { __cgo__undefined__ = (%s)*1 }; }\n", + i+1, i+1, n.C, + i+1, i+1, n.C, + i+1, i+1, n.C) + } + fmt.Fprintf(&b, "#line 1 \"completed\"\n"+ + "int __cgo__1 = __cgo__2;\n") - isConst := make([]bool, len(toSniff)) - for i := range isConst { - isConst[i] = true // until proven otherwise + stderr := p.gccErrors(b.Bytes()) + if stderr == "" { + fatalf("%s produced no output\non input:\n%s", p.gccBaseCmd()[0], b.Bytes()) } + completed := false + sniff := make([]int, len(names)) + const ( + notType = 1 << iota + notConst + ) for _, line := range strings.Split(stderr, "\n") { - if len(line) < 9 || line[0:9] != "cgo-test:" { - // the user will see any compiler errors when the code is compiled later. + if !strings.Contains(line, ": error:") { + // we only care about errors. + // we tried to turn off warnings on the command line, but one never knows. continue } - line = line[9:] - colon := strings.Index(line, ":") - if colon < 0 { - continue - } - i, err := strconv.Atoi(line[0:colon]) - if err != nil { + + c1 := strings.Index(line, ":") + if c1 < 0 { continue } - i = (i - 1) / 2 - what := "" - switch { - default: - continue - case strings.Contains(line, ": useless type name in empty declaration"), - strings.Contains(line, ": declaration does not declare anything"), - strings.Contains(line, ": unexpected type name"): - what = "type" - isConst[i] = false - case strings.Contains(line, ": statement with no effect"), - strings.Contains(line, ": expression result unused"): - what = "not-type" // const or func or var - case strings.Contains(line, "undeclared"): - error_(token.NoPos, "%s", strings.TrimSpace(line[colon+1:])) - case strings.Contains(line, "is not an integer constant"): - isConst[i] = false + c2 := strings.Index(line[c1+1:], ":") + if c2 < 0 { continue } - n := toSniff[i] - if n == nil { + c2 += c1 + 1 + + filename := line[:c1] + i, _ := strconv.Atoi(line[c1+1 : c2]) + i-- + if i < 0 || i >= len(names) { continue } - toSniff[i] = nil - n.Kind = what - j := len(needType) - needType = needType[0 : j+1] - needType[j] = n - } - for i, b := range isConst { - if b { - names[i].Kind = "const" - if toSniff[i] != nil && names[i].Const == "" { - j := len(needType) - needType = needType[0 : j+1] - needType[j] = names[i] - } + switch filename { + case "completed": + // Strictly speaking, there is no guarantee that seeing the error at completed:1 + // (at the end of the file) means we've seen all the errors from earlier in the file, + // but usually it does. Certainly if we don't see the completed:1 error, we did + // not get all the errors we expected. + completed = true + + case "not-declared": + error_(token.NoPos, "%s", strings.TrimSpace(line[c2+1:])) + case "not-type": + sniff[i] |= notType + case "not-const": + sniff[i] |= notConst } } - for _, n := range toSniff { - if n == nil { - continue - } - if n.Kind != "" { - continue + + if !completed { + fatalf("%s did not produce error at completed:1\non input:\n%s", p.gccBaseCmd()[0], b.Bytes()) + } + + for i, n := range names { + switch sniff[i] { + case 0: + error_(token.NoPos, "could not determine kind of name for C.%s", fixGo(n.Go)) + case notType: + n.Kind = "const" + case notConst: + n.Kind = "type" + case notConst | notType: + n.Kind = "not-type" } - error_(token.NoPos, "could not determine kind of name for C.%s", n.Go) } if nerrors > 0 { fatalf("unresolved names") } + + needType = append(needType, names...) return needType } @@ -398,14 +406,14 @@ func (p *Package) loadDWARF(f *File, names []*Name) { // for symbols in the object file, so it is not enough to print the // preamble and hope the symbols we care about will be there. // Instead, emit - // typeof(names[i]) *__cgo__i; + // __typeof__(names[i]) *__cgo__i; // for each entry in names and then dereference the type we // learn for __cgo__i. var b bytes.Buffer - b.WriteString(builtinProlog) b.WriteString(f.Preamble) + b.WriteString(builtinProlog) for i, n := range names { - fmt.Fprintf(&b, "typeof(%s) *__cgo__%d;\n", n.C, i) + fmt.Fprintf(&b, "__typeof__(%s) *__cgo__%d;\n", n.C, i) if n.Kind == "const" { fmt.Fprintf(&b, "enum { __cgo_enum__%d = %s };\n", i, n.C) } @@ -548,25 +556,40 @@ func (p *Package) loadDWARF(f *File, names []*Name) { } +// mangleName does name mangling to translate names +// from the original Go source files to the names +// used in the final Go files generated by cgo. +func (p *Package) mangleName(n *Name) { + // When using gccgo variables have to be + // exported so that they become global symbols + // that the C code can refer to. + prefix := "_C" + if *gccgo && n.IsVar() { + prefix = "C" + } + n.Mangle = prefix + n.Kind + "_" + n.Go +} + // rewriteRef rewrites all the C.xxx references in f.AST to refer to the // Go equivalents, now that we have figured out the meaning of all // the xxx. In *godefs or *cdefs mode, rewriteRef replaces the names // with full definitions instead of mangled names. func (p *Package) rewriteRef(f *File) { + // Keep a list of all the functions, to remove the ones + // only used as expressions and avoid generating bridge + // code for them. + functions := make(map[string]bool) + // Assign mangled names. for _, n := range f.Name { if n.Kind == "not-type" { n.Kind = "var" } if n.Mangle == "" { - // When using gccgo variables have to be - // exported so that they become global symbols - // that the C code can refer to. - prefix := "_C" - if *gccgo && n.Kind == "var" { - prefix = "C" - } - n.Mangle = prefix + n.Kind + "_" + n.Go + p.mangleName(n) + } + if n.Kind == "func" { + functions[n.Go] = false } } @@ -576,7 +599,7 @@ func (p *Package) rewriteRef(f *File) { // functions are only used in calls. for _, r := range f.Ref { if r.Name.Kind == "const" && r.Name.Const == "" { - error_(r.Pos(), "unable to find value of constant C.%s", r.Name.Go) + error_(r.Pos(), "unable to find value of constant C.%s", fixGo(r.Name.Go)) } var expr ast.Expr = ast.NewIdent(r.Name.Mangle) // default switch r.Context { @@ -587,10 +610,15 @@ func (p *Package) rewriteRef(f *File) { expr = r.Name.Type.Go break } - error_(r.Pos(), "call of non-function C.%s", r.Name.Go) + error_(r.Pos(), "call of non-function C.%s", fixGo(r.Name.Go)) break } + functions[r.Name.Go] = true if r.Context == "call2" { + if r.Name.Go == "_CMalloc" { + error_(r.Pos(), "no two-result form for C.malloc") + break + } // Invent new Name for the two-result function. n := f.Name["2"+r.Name.Go] if n == nil { @@ -606,29 +634,42 @@ func (p *Package) rewriteRef(f *File) { } case "expr": if r.Name.Kind == "func" { - error_(r.Pos(), "must call C.%s", r.Name.Go) - } - if r.Name.Kind == "type" { + // Function is being used in an expression, to e.g. pass around a C function pointer. + // Create a new Name for this Ref which causes the variable to be declared in Go land. + fpName := "fp_" + r.Name.Go + name := f.Name[fpName] + if name == nil { + name = &Name{ + Go: fpName, + C: r.Name.C, + Kind: "fpvar", + Type: &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*"), Go: ast.NewIdent("unsafe.Pointer")}, + } + p.mangleName(name) + f.Name[fpName] = name + } + r.Name = name + expr = ast.NewIdent(name.Mangle) + } else if r.Name.Kind == "type" { // Okay - might be new(T) expr = r.Name.Type.Go - } - if r.Name.Kind == "var" { - expr = &ast.StarExpr{X: expr} + } else if r.Name.Kind == "var" { + expr = &ast.StarExpr{Star: (*r.Expr).Pos(), X: expr} } case "type": if r.Name.Kind != "type" { - error_(r.Pos(), "expression C.%s used as type", r.Name.Go) + error_(r.Pos(), "expression C.%s used as type", fixGo(r.Name.Go)) } else if r.Name.Type == nil { // Use of C.enum_x, C.struct_x or C.union_x without C definition. // GCC won't raise an error when using pointers to such unknown types. - error_(r.Pos(), "type C.%s: undefined C type '%s'", r.Name.Go, r.Name.C) + error_(r.Pos(), "type C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C) } else { expr = r.Name.Type.Go } default: if r.Name.Kind == "func" { - error_(r.Pos(), "must call C.%s", r.Name.Go) + error_(r.Pos(), "must call C.%s", fixGo(r.Name.Go)) } } if *godefs || *cdefs { @@ -642,23 +683,42 @@ func (p *Package) rewriteRef(f *File) { } } } + + // Copy position information from old expr into new expr, + // in case expression being replaced is first on line. + // See golang.org/issue/6563. + pos := (*r.Expr).Pos() + switch x := expr.(type) { + case *ast.Ident: + expr = &ast.Ident{NamePos: pos, Name: x.Name} + } + *r.Expr = expr } -} -// gccName returns the name of the compiler to run. Use $CC if set in -// the environment, otherwise just "gcc". + // Remove functions only used as expressions, so their respective + // bridge functions are not generated. + for name, used := range functions { + if !used { + delete(f.Name, name) + } + } +} -func (p *Package) gccName() string { +// gccBaseCmd returns the start of the compiler command line. +// It uses $CC if set, or else $GCC, or else the compiler recorded +// during the initial build as defaultCC. +// defaultCC is defined in zdefaultcc.go, written by cmd/dist. +func (p *Package) gccBaseCmd() []string { // Use $CC if set, since that's what the build uses. - if ret := os.Getenv("CC"); ret != "" { + if ret := strings.Fields(os.Getenv("CC")); len(ret) > 0 { return ret } - // Fall back to $GCC if set, since that's what we used to use. - if ret := os.Getenv("GCC"); ret != "" { + // Try $GCC if set, since that's what we used to use. + if ret := strings.Fields(os.Getenv("GCC")); len(ret) > 0 { return ret } - return "gcc" + return strings.Fields(defaultCC) } // gccMachine returns the gcc -m flag to use, either "-m32", "-m64" or "-marm". @@ -681,17 +741,15 @@ func gccTmp() string { // gccCmd returns the gcc command line to use for compiling // the input. func (p *Package) gccCmd() []string { - c := []string{ - p.gccName(), - "-Wall", // many warnings - "-Werror", // warnings are errors - "-o" + gccTmp(), // write object to tmp - "-gdwarf-2", // generate DWARF v2 debugging symbols - "-fno-eliminate-unused-debug-types", // gets rid of e.g. untyped enum otherwise - "-c", // do not link - "-xc", // input language is C - } - if strings.Contains(p.gccName(), "clang") { + c := append(p.gccBaseCmd(), + "-w", // no warnings + "-Wno-error", // warnings are not errors + "-o"+gccTmp(), // write object to tmp + "-gdwarf-2", // generate DWARF v2 debugging symbols + "-c", // do not link + "-xc", // input language is C + ) + if strings.Contains(c[0], "clang") { c = append(c, "-ferror-limit=0", // Apple clang version 1.7 (tags/Apple/clang-77) (based on LLVM 2.9svn) @@ -701,6 +759,13 @@ func (p *Package) gccCmd() []string { "-Wno-unneeded-internal-declaration", "-Wno-unused-function", "-Qunused-arguments", + // Clang embeds prototypes for some builtin functions, + // like malloc and calloc, but all size_t parameters are + // incorrectly typed unsigned long. We work around that + // by disabling the builtin functions (this is safe as + // it won't affect the actual compilation of the C code). + // See: http://golang.org/issue/6506. + "-fno-builtin", ) } @@ -715,7 +780,13 @@ func (p *Package) gccCmd() []string { func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) { runGcc(stdin, p.gccCmd()) + isDebugData := func(s string) bool { + // Some systems use leading _ to denote non-assembly symbols. + return s == "__cgodebug_data" || s == "___cgodebug_data" + } + if f, err := macho.Open(gccTmp()); err == nil { + defer f.Close() d, err := f.DWARF() if err != nil { fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) @@ -724,8 +795,7 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) if f.Symtab != nil { for i := range f.Symtab.Syms { s := &f.Symtab.Syms[i] - // Mach-O still uses a leading _ to denote non-assembly symbols. - if s.Name == "_"+"__cgodebug_data" { + if isDebugData(s.Name) { // Found it. Now find data section. if i := int(s.Sect) - 1; 0 <= i && i < len(f.Sections) { sect := f.Sections[i] @@ -742,6 +812,7 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) } if f, err := elf.Open(gccTmp()); err == nil { + defer f.Close() d, err := f.DWARF() if err != nil { fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) @@ -751,7 +822,7 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) if err == nil { for i := range symtab { s := &symtab[i] - if s.Name == "__cgodebug_data" { + if isDebugData(s.Name) { // Found it. Now find data section. if i := int(s.Section); 0 <= i && i < len(f.Sections) { sect := f.Sections[i] @@ -768,13 +839,14 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) } if f, err := pe.Open(gccTmp()); err == nil { + defer f.Close() d, err := f.DWARF() if err != nil { fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) } var data []byte for _, s := range f.Symbols { - if s.Name == "_"+"__cgodebug_data" { + if isDebugData(s.Name) { if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) { sect := f.Sections[i] if s.Value < sect.Size { @@ -797,7 +869,7 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) // #defines that gcc encountered while processing the input // and its included files. func (p *Package) gccDefines(stdin []byte) string { - base := []string{p.gccName(), "-E", "-dM", "-xc"} + base := append(p.gccBaseCmd(), "-E", "-dM", "-xc") base = append(base, p.gccMachine()...) stdout, _ := runGcc(stdin, append(append(base, p.GccOptions...), "-")) return stdout @@ -810,14 +882,6 @@ func (p *Package) gccErrors(stdin []byte) string { // TODO(rsc): require failure args := p.gccCmd() - // GCC 4.8.0 has a bug: it sometimes does not apply - // -Wunused-value to values that are macros defined in system - // headers. See issue 5118. Adding -Wsystem-headers avoids - // that problem. This will produce additional errors, but it - // doesn't matter because we will ignore all errors that are - // not marked for the cgo-test file. - args = append(args, "-Wsystem-headers") - if *debugGcc { fmt.Fprintf(os.Stderr, "$ %s <<EOF\n", strings.Join(args, " ")) os.Stderr.Write(stdin) @@ -982,21 +1046,11 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { } t := new(Type) - t.Size = dtype.Size() + t.Size = dtype.Size() // note: wrong for array of pointers, corrected below t.Align = -1 t.C = &TypeRepr{Repr: dtype.Common().Name} c.m[dtype] = t - if t.Size < 0 { - // Unsized types are [0]byte - t.Size = 0 - t.Go = c.Opaque(0) - if t.C.Empty() { - t.C.Set("void") - } - return t - } - switch dt := dtype.(type) { default: fatalf("%s: unexpected type: %s", lineno(pos), dtype) @@ -1021,7 +1075,7 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { sub := c.Type(dt.Type, pos) t.Align = sub.Align gt.Elt = sub.Go - t.C.Set("typeof(%s[%d])", sub.C, dt.Count) + t.C.Set("__typeof__(%s[%d])", sub.C, dt.Count) case *dwarf.BoolType: t.Go = c.bool @@ -1143,6 +1197,9 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { return t case *dwarf.StructType: + if dt.ByteSize < 0 { // opaque struct + break + } // Convert to Go struct, being careful about alignment. // Have to give it a name to simulate C "struct foo" references. tag := dt.StructName @@ -1159,7 +1216,7 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { case "class", "union": t.Go = c.Opaque(t.Size) if t.C.Empty() { - t.C.Set("typeof(unsigned char[%d])", t.Size) + t.C.Set("__typeof__(unsigned char[%d])", t.Size) } t.Align = 1 // TODO: should probably base this on field alignment. typedef[name.Name] = t @@ -1261,6 +1318,25 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { } } + if t.Size <= 0 { + // Clang does not record the size of a pointer in its DWARF entry, + // so if dtype is an array, the call to dtype.Size at the top of the function + // computed the size as the array length * 0 = 0. + // The type switch called Type (this function) recursively on the pointer + // entry, and the code near the top of the function updated the size to + // be correct, so calling dtype.Size again will produce the correct value. + t.Size = dtype.Size() + if t.Size < 0 { + // Unsized types are [0]byte + t.Size = 0 + t.Go = c.Opaque(0) + if t.C.Empty() { + t.C.Set("void") + } + return t + } + } + if t.C.Empty() { fatalf("%s: internal error: did not create C name for %s", lineno(pos), dtype) } diff --git a/src/cmd/cgo/godefs.go b/src/cmd/cgo/godefs.go index 20376170d..ce5ac2736 100644 --- a/src/cmd/cgo/godefs.go +++ b/src/cmd/cgo/godefs.go @@ -204,6 +204,11 @@ func (p *Package) cdefs(f *File, srcfile string) string { // byte Z[4]; // } if strings.HasPrefix(line, "type ") && strings.HasSuffix(line, " struct {") { + if len(lines) > i+1 && lines[i+1] == "}" { + // do not output empty struct + i++ + continue + } s := line[len("type ") : len(line)-len(" struct {")] printf("struct %s {\n", s) for i++; i < len(lines) && lines[i] != "}"; i++ { @@ -256,17 +261,17 @@ func cdecl(name, typ string) string { if strings.HasPrefix(typ, "*[0]") { typ = "*void" } - // X *byte -> *X byte - if strings.HasPrefix(typ, "*") { - name = "*" + name - typ = typ[1:] - } // X [4]byte -> X[4] byte - if strings.HasPrefix(typ, "[") { + for strings.HasPrefix(typ, "[") { i := strings.Index(typ, "]") + 1 name = name + typ[:i] typ = typ[i:] } + // X *byte -> *X byte + for strings.HasPrefix(typ, "*") { + name = "*" + name + typ = typ[1:] + } // X T -> T X // Handle the special case: 'unsafe.Pointer' is 'void *' if typ == "unsafe.Pointer" { diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go index 9bd326e1d..17b0cdd16 100644 --- a/src/cmd/cgo/main.go +++ b/src/cmd/cgo/main.go @@ -80,13 +80,18 @@ type Name struct { Mangle string // name used in generated Go C string // name used in C Define string // #define expansion - Kind string // "const", "type", "var", "func", "not-type" + Kind string // "const", "type", "var", "fpvar", "func", "not-type" Type *Type // the type of xxx FuncType *FuncType AddError bool Const string // constant definition } +// IsVar returns true if Kind is either "var" or "fpvar" +func (n *Name) IsVar() bool { + return n.Kind == "var" || n.Kind == "fpvar" +} + // A ExpFunc is an exported function, callable from C. // Such functions are identified in the Go input file // by doc comments containing the line //export ExpName @@ -336,7 +341,7 @@ func (p *Package) Record(f *File) { if p.Name[k] == nil { p.Name[k] = v } else if !reflect.DeepEqual(p.Name[k], v) { - error_(token.NoPos, "inconsistent definitions for C.%s", k) + error_(token.NoPos, "inconsistent definitions for C.%s", fixGo(k)) } } } diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 7fb818168..83ab95251 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -32,7 +32,7 @@ func (p *Package) writeDefs() { fflg := creat(*objDir + "_cgo_flags") for k, v := range p.CgoFlags { fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, strings.Join(v, " ")) - if k == "LDFLAGS" { + if k == "LDFLAGS" && !*gccgo { for _, arg := range v { fmt.Fprintf(fc, "#pragma cgo_ldflag %q\n", arg) } @@ -47,7 +47,7 @@ func (p *Package) writeDefs() { } else { // If we're not importing runtime/cgo, we *are* runtime/cgo, // which provides crosscall2. We just need a prototype. - fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c);") + fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c);\n") } fmt.Fprintf(fm, "void _cgo_allocate(void *a, int c) { }\n") fmt.Fprintf(fm, "void _cgo_panic(void *a, int c) { }\n") @@ -87,7 +87,7 @@ func (p *Package) writeDefs() { } if *gccgo { - fmt.Fprintf(fc, cPrologGccgo) + fmt.Fprintf(fc, p.cPrologGccgo()) } else { fmt.Fprintf(fc, cProlog) } @@ -97,7 +97,7 @@ func (p *Package) writeDefs() { cVars := make(map[string]bool) for _, key := range nameKeys(p.Name) { n := p.Name[key] - if n.Kind != "var" { + if !n.IsVar() { continue } @@ -105,22 +105,37 @@ func (p *Package) writeDefs() { fmt.Fprintf(fm, "extern char %s[];\n", n.C) fmt.Fprintf(fm, "void *_cgohack_%s = %s;\n\n", n.C, n.C) - fmt.Fprintf(fc, "#pragma cgo_import_static %s\n", n.C) + if !*gccgo { + fmt.Fprintf(fc, "#pragma cgo_import_static %s\n", n.C) + } + fmt.Fprintf(fc, "extern byte *%s;\n", n.C) cVars[n.C] = true } - + var amp string + var node ast.Node + if n.Kind == "var" { + amp = "&" + node = &ast.StarExpr{X: n.Type.Go} + } else if n.Kind == "fpvar" { + node = n.Type.Go + if *gccgo { + amp = "&" + } + } else { + panic(fmt.Errorf("invalid var kind %q", n.Kind)) + } if *gccgo { fmt.Fprintf(fc, `extern void *%s __asm__("%s.%s");`, n.Mangle, gccgoSymbolPrefix, n.Mangle) - fmt.Fprintf(&gccgoInit, "\t%s = &%s;\n", n.Mangle, n.C) + fmt.Fprintf(&gccgoInit, "\t%s = %s%s;\n", n.Mangle, amp, n.C) } else { - fmt.Fprintf(fc, "void *·%s = &%s;\n", n.Mangle, n.C) + fmt.Fprintf(fc, "void *·%s = %s%s;\n", n.Mangle, amp, n.C) } fmt.Fprintf(fc, "\n") fmt.Fprintf(fgo2, "var %s ", n.Mangle) - conf.Fprint(fgo2, fset, &ast.StarExpr{X: n.Type.Go}) + conf.Fprint(fgo2, fset, node) fmt.Fprintf(fgo2, "\n") } fmt.Fprintf(fc, "\n") @@ -282,7 +297,7 @@ func (p *Package) structType(n *Name) (string, int64) { off += pad } if n.AddError { - fmt.Fprint(&buf, "\t\tvoid *e[2]; /* error */\n") + fmt.Fprint(&buf, "\t\tint e[2*sizeof(void *)/sizeof(int)]; /* error */\n") off += 2 * p.PtrSize } if off == 0 { @@ -319,7 +334,7 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { } // Builtins defined in the C prolog. - inProlog := name == "CString" || name == "GoString" || name == "GoStringN" || name == "GoBytes" + inProlog := name == "CString" || name == "GoString" || name == "GoStringN" || name == "GoBytes" || name == "_CMalloc" if *gccgo { // Gccgo style hooks. @@ -368,11 +383,7 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { fmt.Fprint(fgo2, "}\n") // declare the C function. - if inProlog { - fmt.Fprintf(fgo2, "//extern %s\n", n.C) - } else { - fmt.Fprintf(fgo2, "//extern _cgo%s%s\n", cPrefix, n.Mangle) - } + fmt.Fprintf(fgo2, "//extern _cgo%s%s\n", cPrefix, n.Mangle) d.Name = ast.NewIdent(cname) if n.AddError { l := d.Type.Results.List @@ -401,7 +412,17 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { if argSize == 0 { argSize++ } - fmt.Fprintf(fc, "·%s(struct{uint8 x[%d];}p)\n", n.Mangle, argSize) + // TODO(rsc): The struct here should declare pointers only where + // there are pointers in the actual argument frame. + // This is a workaround for golang.org/issue/6397. + fmt.Fprintf(fc, "·%s(struct{", n.Mangle) + if n := argSize / p.PtrSize; n > 0 { + fmt.Fprintf(fc, "void *y[%d];", n) + } + if n := argSize % p.PtrSize; n > 0 { + fmt.Fprintf(fc, "uint8 x[%d];", n) + } + fmt.Fprintf(fc, "}p)\n") fmt.Fprintf(fc, "{\n") fmt.Fprintf(fc, "\truntime·cgocall(_cgo%s%s, &p);\n", cPrefix, n.Mangle) if n.AddError { @@ -464,9 +485,27 @@ func (p *Package) writeOutput(f *File, srcfile string) { fgcc.Close() } +// fixGo convers the internal Name.Go field into the name we should show +// to users in error messages. There's only one for now: on input we rewrite +// C.malloc into C._CMalloc, so change it back here. +func fixGo(name string) string { + if name == "_CMalloc" { + return "malloc" + } + return name +} + +var isBuiltin = map[string]bool{ + "_Cfunc_CString": true, + "_Cfunc_GoString": true, + "_Cfunc_GoStringN": true, + "_Cfunc_GoBytes": true, + "_Cfunc__CMalloc": true, +} + func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { name := n.Mangle - if name == "_Cfunc_CString" || name == "_Cfunc_GoString" || name == "_Cfunc_GoStringN" || name == "_Cfunc_GoBytes" || p.Written[name] { + if isBuiltin[name] || p.Written[name] { // The builtins are already defined in the C prolog, and we don't // want to duplicate function definitions we've already done. return @@ -486,19 +525,24 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { fmt.Fprintf(fgcc, "_cgo%s%s(void *v)\n", cPrefix, n.Mangle) fmt.Fprintf(fgcc, "{\n") if n.AddError { - fmt.Fprintf(fgcc, "\tint e;\n") // assuming 32 bit (see comment above structType) fmt.Fprintf(fgcc, "\terrno = 0;\n") } // We're trying to write a gcc struct that matches 6c/8c/5c's layout. // Use packed attribute to force no padding in this struct in case // gcc has different packing requirements. For example, // on 386 Windows, gcc wants to 8-align int64s, but 8c does not. - fmt.Fprintf(fgcc, "\t%s __attribute__((__packed__)) *a = v;\n", ctype) + // Use __gcc_struct__ to work around http://gcc.gnu.org/PR52991 on x86, + // and http://golang.org/issue/5603. + extraAttr := "" + if !strings.Contains(p.gccBaseCmd()[0], "clang") && (goarch == "amd64" || goarch == "386") { + extraAttr = ", __gcc_struct__" + } + fmt.Fprintf(fgcc, "\t%s __attribute__((__packed__%v)) *a = v;\n", ctype, extraAttr) fmt.Fprintf(fgcc, "\t") if t := n.FuncType.Result; t != nil { fmt.Fprintf(fgcc, "a->r = ") if c := t.C.String(); c[len(c)-1] == '*' { - fmt.Fprintf(fgcc, "(const %s) ", t.C) + fmt.Fprint(fgcc, "(__typeof__(a->r)) ") } } fmt.Fprintf(fgcc, "%s(", n.C) @@ -993,7 +1037,8 @@ func (p *Package) cgoType(e ast.Expr) *Type { return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("%s*", x.C)} case *ast.ArrayType: if t.Len == nil { - return &Type{Size: p.PtrSize + 8, Align: p.PtrSize, C: c("GoSlice")} + // Slice: pointer, len, cap. + return &Type{Size: p.PtrSize * 3, Align: p.PtrSize, C: c("GoSlice")} } case *ast.StructType: // TODO @@ -1030,8 +1075,7 @@ func (p *Package) cgoType(e ast.Expr) *Type { return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("GoUintptr")} } if t.Name == "string" { - // The string data is 1 pointer + 1 int, but this always - // rounds to 2 pointers due to alignment. + // The string data is 1 pointer + 1 (pointer-sized) int. return &Type{Size: 2 * p.PtrSize, Align: p.PtrSize, C: c("GoString")} } if t.Name == "error" { @@ -1084,12 +1128,24 @@ __cgo_size_assert(double, 8) ` const builtinProlog = ` -typedef struct { char *p; int n; } _GoString_; -typedef struct { char *p; int n; int c; } _GoBytes_; +#include <sys/types.h> /* for size_t below */ + +/* Define intgo when compiling with GCC. */ +#ifdef __PTRDIFF_TYPE__ +typedef __PTRDIFF_TYPE__ intgo; +#elif defined(_LP64) +typedef long long intgo; +#else +typedef int intgo; +#endif + +typedef struct { char *p; intgo n; } _GoString_; +typedef struct { char *p; intgo n; intgo c; } _GoBytes_; _GoString_ GoString(char *p); _GoString_ GoStringN(char *p, int l); _GoBytes_ GoBytes(void *p, int n); char *CString(_GoString_); +void *_CMalloc(size_t); ` const cProlog = ` @@ -1127,10 +1183,22 @@ void p[s.len] = 0; FLUSH(&p); } + +void +·_Cfunc__CMalloc(uintptr n, int8 *p) +{ + p = runtime·cmalloc(n); + FLUSH(&p); +} ` +func (p *Package) cPrologGccgo() string { + return strings.Replace(cPrologGccgo, "PREFIX", cPrefix, -1) +} + const cPrologGccgo = ` #include <stdint.h> +#include <stdlib.h> #include <string.h> typedef unsigned char byte; @@ -1150,23 +1218,33 @@ typedef struct __go_open_array { struct __go_string __go_byte_array_to_string(const void* p, intgo len); struct __go_open_array __go_string_to_byte_array (struct __go_string str); -const char *CString(struct __go_string s) { +const char *_cgoPREFIX_Cfunc_CString(struct __go_string s) { return strndup((const char*)s.__data, s.__length); } -struct __go_string GoString(char *p) { +struct __go_string _cgoPREFIX_Cfunc_GoString(char *p) { intgo len = (p != NULL) ? strlen(p) : 0; return __go_byte_array_to_string(p, len); } -struct __go_string GoStringN(char *p, intgo n) { +struct __go_string _cgoPREFIX_Cfunc_GoStringN(char *p, int32_t n) { return __go_byte_array_to_string(p, n); } -Slice GoBytes(char *p, intgo n) { +Slice _cgoPREFIX_Cfunc_GoBytes(char *p, int32_t n) { struct __go_string s = { (const unsigned char *)p, n }; return __go_string_to_byte_array(s); } + +extern void runtime_throw(const char *); +void *_cgoPREFIX_Cfunc__CMalloc(size_t n) { + void *p = malloc(n); + if(p == NULL && n == 0) + p = malloc(1); + if(p == NULL) + runtime_throw("runtime: C malloc failed"); + return p; +} ` func (p *Package) gccExportHeaderProlog() string { @@ -1190,9 +1268,9 @@ typedef double GoFloat64; typedef __complex float GoComplex64; typedef __complex double GoComplex128; -typedef struct { char *p; int n; } GoString; +typedef struct { char *p; GoInt n; } GoString; typedef void *GoMap; typedef void *GoChan; typedef struct { void *t; void *v; } GoInterface; -typedef struct { void *data; int len; int cap; } GoSlice; +typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; ` diff --git a/src/cmd/dist/a.h b/src/cmd/dist/a.h index 73c126476..9de93180f 100644 --- a/src/cmd/dist/a.h +++ b/src/cmd/dist/a.h @@ -74,10 +74,13 @@ extern char *goroot; extern char *goroot_final; extern char *goextlinkenabled; extern char *goversion; +extern char *defaultcc; +extern char *defaultcxx; extern char *workdir; extern char *tooldir; extern char *slash; extern bool rebuildall; +extern bool defaultclang; int find(char*, char**, int); void init(void); @@ -94,16 +97,22 @@ void mkenam(char*, char*); // buildruntime.c void mkzasm(char*, char*); +void mkzsys(char*, char*); void mkzgoarch(char*, char*); void mkzgoos(char*, char*); void mkzruntimedefs(char*, char*); void mkzversion(char*, char*); +void mkzexperiment(char*, char*); + +// buildgo.c +void mkzdefaultcc(char*, char*); // goc2c.c void goc2c(char*, char*); // main.c extern int vflag; +extern int sflag; void usage(void); void xmain(int argc, char **argv); diff --git a/src/cmd/dist/build.c b/src/cmd/dist/build.c index 169e5cadd..e6e5f0cf7 100644 --- a/src/cmd/dist/build.c +++ b/src/cmd/dist/build.c @@ -26,8 +26,10 @@ char *tooldir; char *gochar; char *goversion; char *slash; // / for unix, \ for windows - -bool rebuildall = 0; +char *defaultcc; +char *defaultcxx; +bool rebuildall; +bool defaultclang; static bool shouldbuild(char*, char*); static void copy(char*, char*, int); @@ -47,6 +49,7 @@ static char *okgoarch[] = { // The known operating systems. static char *okgoos[] = { "darwin", + "dragonfly", "linux", "freebsd", "netbsd", @@ -146,6 +149,29 @@ init(void) if(!streq(goextlinkenabled, "0") && !streq(goextlinkenabled, "1")) fatal("unknown $GO_EXTLINK_ENABLED %s", goextlinkenabled); } + + xgetenv(&b, "CC"); + if(b.len == 0) { + // Use clang on OS X, because gcc is deprecated there. + // Xcode for OS X 10.9 Mavericks will ship a fake "gcc" binary that + // actually runs clang. We prepare different command + // lines for the two binaries, so it matters what we call it. + // See golang.org/issue/5822. + if(defaultclang) + bprintf(&b, "clang"); + else + bprintf(&b, "gcc"); + } + defaultcc = btake(&b); + + xgetenv(&b, "CXX"); + if(b.len == 0) { + if(defaultclang) + bprintf(&b, "clang++"); + else + bprintf(&b, "g++"); + } + defaultcxx = btake(&b); xsetenv("GOROOT", goroot); xsetenv("GOARCH", goarch); @@ -318,7 +344,6 @@ static char *oldtool[] = { // Unreleased directories (relative to $GOROOT) that should // not be in release branches. static char *unreleased[] = { - "src/cmd/cov", "src/cmd/prof", "src/pkg/old", }; @@ -408,12 +433,16 @@ static char *proto_gccargs[] = { // native Plan 9 compilers don't like non-standard prototypes // so let gcc catch them. "-Wstrict-prototypes", + "-Wextra", + "-Wunused", + "-Wuninitialized", "-Wno-sign-compare", "-Wno-missing-braces", "-Wno-parentheses", "-Wno-unknown-pragmas", "-Wno-switch", "-Wno-comment", + "-Wno-missing-field-initializers", "-Werror", "-fno-common", "-ggdb", @@ -471,6 +500,7 @@ static struct { {"cmd/gc", { "-cplx.c", "-pgen.c", + "-popt.c", "-y1.tab.c", // makefile dreg "opnames.h", }}, @@ -495,18 +525,24 @@ static struct { {"cmd/5g", { "../gc/cplx.c", "../gc/pgen.c", + "../gc/popt.c", + "../gc/popt.h", "../5l/enam.c", "$GOROOT/pkg/obj/$GOOS_$GOARCH/libgc.a", }}, {"cmd/6g", { "../gc/cplx.c", "../gc/pgen.c", + "../gc/popt.c", + "../gc/popt.h", "../6l/enam.c", "$GOROOT/pkg/obj/$GOOS_$GOARCH/libgc.a", }}, {"cmd/8g", { "../gc/cplx.c", "../gc/pgen.c", + "../gc/popt.c", + "../gc/popt.h", "../8l/enam.c", "$GOROOT/pkg/obj/$GOOS_$GOARCH/libgc.a", }}, @@ -522,13 +558,18 @@ static struct { "../ld/*", "enam.c", }}, + {"cmd/go", { + "zdefaultcc.go", + }}, {"cmd/", { "$GOROOT/pkg/obj/$GOOS_$GOARCH/libmach.a", "$GOROOT/pkg/obj/$GOOS_$GOARCH/libbio.a", "$GOROOT/pkg/obj/$GOOS_$GOARCH/lib9.a", }}, {"pkg/runtime", { + "zaexperiment.h", // must sort above zasm "zasm_$GOOS_$GOARCH.h", + "zsys_$GOOS_$GOARCH.s", "zgoarch_$GOARCH.go", "zgoos_$GOOS.go", "zruntime_defs_$GOOS_$GOARCH.go", @@ -553,10 +594,13 @@ static struct { {"opnames.h", gcopnames}, {"enam.c", mkenam}, {"zasm_", mkzasm}, + {"zdefaultcc.go", mkzdefaultcc}, + {"zsys_", mkzsys}, {"zgoarch_", mkzgoarch}, {"zgoos_", mkzgoos}, {"zruntime_defs_", mkzruntimedefs}, {"zversion.go", mkzversion}, + {"zaexperiment.h", mkzexperiment}, }; // install installs the library, package, or binary associated with dir, @@ -565,7 +609,7 @@ static void install(char *dir) { char *name, *p, *elem, *prefix, *exe; - bool islib, ispkg, isgo, stale, clang; + bool islib, ispkg, isgo, stale; Buf b, b1, path; Vec compile, files, link, go, missing, clean, lib, extra; Time ttarg, t; @@ -602,8 +646,8 @@ install(char *dir) goto out; } - // For release, cmd/prof and cmd/cov are not included. - if((streq(dir, "cmd/cov") || streq(dir, "cmd/prof")) && !isdir(bstr(&path))) { + // For release, cmd/prof is not included. + if((streq(dir, "cmd/prof")) && !isdir(bstr(&path))) { if(vflag > 1) errprintf("skipping %s - does not exist\n", dir); goto out; @@ -611,14 +655,13 @@ install(char *dir) // set up gcc command line on first run. if(gccargs.len == 0) { - xgetenv(&b, "CC"); - if(b.len == 0) - bprintf(&b, "gcc"); - clang = contains(bstr(&b), "clang"); + bprintf(&b, "%s", defaultcc); splitfields(&gccargs, bstr(&b)); for(i=0; i<nelem(proto_gccargs); i++) vadd(&gccargs, proto_gccargs[i]); - if(clang) { + if(contains(gccargs.p[0], "clang")) { + // disable ASCII art in clang errors, if possible + vadd(&gccargs, "-fno-caret-diagnostics"); // clang is too smart about unused command-line arguments vadd(&gccargs, "-Qunused-arguments"); } @@ -677,6 +720,8 @@ install(char *dir) vadd(&link, bpathf(&b, "%s/%s", tooldir, name)); } else { vcopy(&link, gccargs.p, gccargs.len); + if(sflag) + vadd(&link, "-static"); vadd(&link, "-o"); targ = link.len; vadd(&link, bpathf(&b, "%s/%s%s", tooldir, name, exe)); @@ -901,6 +946,8 @@ install(char *dir) vadd(&compile, "-Bp+"); vadd(&compile, bpathf(&b, "-I%s/include/plan9", goroot)); vadd(&compile, bpathf(&b, "-I%s/include/plan9/%s", goroot, gohostarch)); + // Work around Plan 9 C compiler's handling of #include with .. path. + vadd(&compile, bpathf(&b, "-I%s/src/cmd/ld", goroot)); } else { vcopy(&compile, gccargs.p, gccargs.len); vadd(&compile, "-c"); @@ -962,6 +1009,8 @@ install(char *dir) vadd(&compile, bprintf(&b, "GOOS_%s", goos)); vadd(&compile, "-D"); vadd(&compile, bprintf(&b, "GOARCH_%s", goarch)); + vadd(&compile, "-D"); + vadd(&compile, bprintf(&b, "GOOS_GOARCH_%s_%s", goos, goarch)); } bpathf(&b, "%s/%s", workdir, lastelem(files.p[i])); @@ -1198,7 +1247,6 @@ static char *buildorder[] = { "misc/pprof", "cmd/addr2line", - "cmd/cov", "cmd/nm", "cmd/objdump", "cmd/pack", @@ -1236,6 +1284,7 @@ static char *buildorder[] = { "pkg/os", "pkg/reflect", "pkg/fmt", + "pkg/encoding", "pkg/encoding/json", "pkg/flag", "pkg/path/filepath", @@ -1276,7 +1325,6 @@ static char *cleantab[] = { "cmd/8l", "cmd/addr2line", "cmd/cc", - "cmd/cov", "cmd/gc", "cmd/go", "cmd/nm", @@ -1289,6 +1337,7 @@ static char *cleantab[] = { "pkg/bufio", "pkg/bytes", "pkg/container/heap", + "pkg/encoding", "pkg/encoding/base64", "pkg/encoding/json", "pkg/errors", @@ -1339,7 +1388,7 @@ clean(void) vinit(&dir); for(i=0; i<nelem(cleantab); i++) { - if((streq(cleantab[i], "cmd/cov") || streq(cleantab[i], "cmd/prof")) && !isdir(cleantab[i])) + if((streq(cleantab[i], "cmd/prof")) && !isdir(cleantab[i])) continue; bpathf(&path, "%s/src/%s", goroot, cleantab[i]); xreaddir(&dir, bstr(&path)); @@ -1438,6 +1487,7 @@ cmdenv(int argc, char **argv) if(argc > 0) usage(); + xprintf(format, "CC", defaultcc); xprintf(format, "GOROOT", goroot); xprintf(format, "GOBIN", gobin); xprintf(format, "GOARCH", goarch); @@ -1479,6 +1529,9 @@ cmdbootstrap(int argc, char **argv) case 'a': rebuildall = 1; break; + case 's': + sflag++; + break; case 'v': vflag++; break; @@ -1565,6 +1618,9 @@ cmdinstall(int argc, char **argv) int i; ARGBEGIN{ + case 's': + sflag++; + break; case 'v': vflag++; break; @@ -1626,7 +1682,10 @@ cmdbanner(int argc, char **argv) xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot); xprintf("Installed commands in %s\n", gobin); - if(streq(gohostos, "plan9")) { + if(!xsamefile(goroot_final, goroot)) { + // If the files are to be moved, don't check that gobin + // is on PATH; assume they know what they are doing. + } else if(streq(gohostos, "plan9")) { // Check that gobin is bound before /bin. readfile(&b, "#c/pid"); bsubst(&b, " ", ""); diff --git a/src/cmd/dist/buildgo.c b/src/cmd/dist/buildgo.c new file mode 100644 index 000000000..a340252bc --- /dev/null +++ b/src/cmd/dist/buildgo.c @@ -0,0 +1,49 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "a.h" + +/* + * Helpers for building cmd/go and cmd/cgo. + */ + +// mkzdefaultcc writes zdefaultcc.go: +// +// package main +// const defaultCC = <defaultcc> +// const defaultCXX = <defaultcxx> +// +// It is invoked to write cmd/go/zdefaultcc.go +// but we also write cmd/cgo/zdefaultcc.go. +void +mkzdefaultcc(char *dir, char *file) +{ + Buf b, out; + + USED(dir); + + binit(&out); + bprintf(&out, + "// auto generated by go tool dist\n" + "\n" + "package main\n" + "\n" + "const defaultCC = `%s`\n" + "const defaultCXX = `%s`\n", + defaultcc, defaultcxx); + + writefile(&out, file, 0); + + // Convert file name to replace. + binit(&b); + bwritestr(&b, file); + if(slash[0] == '/') + bsubst(&b, "/go/zdefaultcc.go", "/cgo/zdefaultcc.go"); + else + bsubst(&b, "\\go\\zdefaultcc.go", "\\cgo\\zdefaultcc.go"); + writefile(&out, bstr(&b), 0); + + bfree(&b); + bfree(&out); +} diff --git a/src/cmd/dist/buildruntime.c b/src/cmd/dist/buildruntime.c index ee867566f..e6e309d92 100644 --- a/src/cmd/dist/buildruntime.c +++ b/src/cmd/dist/buildruntime.c @@ -38,6 +38,34 @@ mkzversion(char *dir, char *file) bfree(&out); } +// mkzexperiment writes zaexperiment.h (sic): +// +// #define GOEXPERIMENT "experiment string" +// +void +mkzexperiment(char *dir, char *file) +{ + Buf b, out, exp; + + USED(dir); + + binit(&b); + binit(&out); + binit(&exp); + + xgetenv(&exp, "GOEXPERIMENT"); + bwritestr(&out, bprintf(&b, + "// auto generated by go tool dist\n" + "\n" + "#define GOEXPERIMENT \"%s\"\n", bstr(&exp))); + + writefile(&out, file, 0); + + bfree(&b); + bfree(&out); + bfree(&exp); +} + // mkzgoarch writes zgoarch_$GOARCH.go: // // package runtime @@ -162,24 +190,27 @@ static struct { "#define m(r) 8(GS)\n" "#define procid(r) 16(GS)\n" }, - {"amd64", "", - "// The offsets 0 and 8 are known to:\n" - "// ../../cmd/6l/pass.c:/D_GS\n" - "// cgo/gcc_linux_amd64.c:/^threadentry\n" - "// cgo/gcc_darwin_amd64.c:/^threadentry\n" - "//\n" - "#define get_tls(r)\n" - "#define g(r) 0(GS)\n" - "#define m(r) 8(GS)\n" + // The TLS accessors here are defined here to use initial exec model. + // If the linker is not outputting a shared library, it will reduce + // the TLS accessors to the local exec model, effectively removing + // get_tls(). + {"amd64", "linux", + "#define get_tls(r) MOVQ runtime·tlsgm(SB), r\n" + "#define g(r) 0(r)(GS*1)\n" + "#define m(r) 8(r)(GS*1)\n" }, - + {"amd64", "", + "#define get_tls(r)\n" + "#define g(r) 0(GS)\n" + "#define m(r) 8(GS)\n" + }, {"arm", "", - "#define g R10\n" - "#define m R9\n" "#define LR R14\n" }, }; +#define MAXWINCB 2000 /* maximum number of windows callbacks allowed */ + // mkzasm writes zasm_$GOOS_$GOARCH.h, // which contains struct offsets for use by // assembly files. It also writes a copy to the work space @@ -190,12 +221,13 @@ mkzasm(char *dir, char *file) { int i, n; char *aggr, *p; - Buf in, b, out; + Buf in, b, out, exp; Vec argv, lines, fields; binit(&in); binit(&b); binit(&out); + binit(&exp); vinit(&argv); vinit(&lines); vinit(&fields); @@ -234,10 +266,11 @@ ok: // Gobuf 24 sched; // 'Y' 48 stack0; // } + // StackMin = 128; // into output like // #define g_sched 24 // #define g_stack0 48 - // + // #define const_StackMin 128 aggr = nil; splitlines(&lines, bstr(&in)); for(i=0; i<lines.len; i++) { @@ -247,10 +280,14 @@ ok: aggr = "g"; else if(streq(fields.p[1], "M")) aggr = "m"; + else if(streq(fields.p[1], "P")) + aggr = "p"; else if(streq(fields.p[1], "Gobuf")) aggr = "gobuf"; else if(streq(fields.p[1], "WinCall")) aggr = "wincall"; + else if(streq(fields.p[1], "WinCallbackContext")) + aggr = "cbctxt"; else if(streq(fields.p[1], "SEH")) aggr = "seh"; } @@ -263,8 +300,22 @@ ok: p[xstrlen(p)-1] = '\0'; bwritestr(&out, bprintf(&b, "#define %s_%s %s\n", aggr, fields.p[n-1], fields.p[n-2])); } + if(fields.len == 3 && streq(fields.p[1], "=")) { // generated from enumerated constants + p = fields.p[2]; + if(p[xstrlen(p)-1] == ';') + p[xstrlen(p)-1] = '\0'; + bwritestr(&out, bprintf(&b, "#define const_%s %s\n", fields.p[0], p)); + } + } + + // Some #defines that are used for .c files. + if(streq(goos, "windows")) { + bwritestr(&out, bprintf(&b, "#define cb_max %d\n", MAXWINCB)); } + xgetenv(&exp, "GOEXPERIMENT"); + bwritestr(&out, bprintf(&b, "#define GOEXPERIMENT \"%s\"\n", bstr(&exp))); + // Write both to file and to workdir/zasm_GOOS_GOARCH.h. writefile(&out, file, 0); writefile(&out, bprintf(&b, "%s/zasm_GOOS_GOARCH.h", workdir), 0); @@ -272,11 +323,47 @@ ok: bfree(&in); bfree(&b); bfree(&out); + bfree(&exp); vfree(&argv); vfree(&lines); vfree(&fields); } +// mkzsys writes zsys_$GOOS_$GOARCH.h, +// which contains arch or os specific asm code. +// +void +mkzsys(char *dir, char *file) +{ + int i; + Buf out; + + USED(dir); + + binit(&out); + + bwritestr(&out, "// auto generated by go tool dist\n\n"); + if(streq(goos, "windows")) { + bwritef(&out, + "// runtime·callbackasm is called by external code to\n" + "// execute Go implemented callback function. It is not\n" + "// called from the start, instead runtime·compilecallback\n" + "// always returns address into runtime·callbackasm offset\n" + "// appropriately so different callbacks start with different\n" + "// CALL instruction in runtime·callbackasm. This determines\n" + "// which Go callback function is executed later on.\n" + "TEXT runtime·callbackasm(SB),7,$0\n"); + for(i=0; i<MAXWINCB; i++) { + bwritef(&out, "\tCALL\truntime·callbackasm1(SB)\n"); + } + bwritef(&out, "\tRET\n"); + } + + writefile(&out, file, 0); + + bfree(&out); +} + static char *runtimedefs[] = { "proc.c", "iface.c", diff --git a/src/cmd/dist/goc2c.c b/src/cmd/dist/goc2c.c index f58460397..f0fa04335 100644 --- a/src/cmd/dist/goc2c.c +++ b/src/cmd/dist/goc2c.c @@ -694,17 +694,29 @@ copy_body(void) static void process_file(void) { - char *package, *name; + char *package, *name, *p, *n; struct params *params, *rets; int paramwid; package = read_package(); read_preprocessor_lines(); while (read_func_header(&name, ¶ms, ¶mwid, &rets)) { - write_func_header(package, name, params, paramwid, rets); + // name may have a package override already + n = xstrstr(name, "·"); + if(n != nil) { + p = xmalloc(n - name + 1); + xmemmove(p, name, n - name); + p[n - name] = 0; + n += xstrlen("·"); + } else { + p = package; + n = name; + } + write_func_header(p, n, params, paramwid, rets); copy_body(); - write_func_trailer(package, name, rets); + write_func_trailer(p, n, rets); xfree(name); + if(p != package) xfree(p); free_params(params); free_params(rets); } diff --git a/src/cmd/dist/main.c b/src/cmd/dist/main.c index 72a7579d1..fad01802a 100644 --- a/src/cmd/dist/main.c +++ b/src/cmd/dist/main.c @@ -5,6 +5,7 @@ #include "a.h" int vflag; +int sflag; char *argv0; // cmdtab records the available commands. diff --git a/src/cmd/dist/plan9.c b/src/cmd/dist/plan9.c index 8a7c0ab1c..8d492ebc6 100644 --- a/src/cmd/dist/plan9.c +++ b/src/cmd/dist/plan9.c @@ -578,7 +578,7 @@ hassuffix(char *p, char *suffix) return np >= ns && strcmp(p+np-ns, suffix) == 0; } -// hasprefix reports whether p begins wtih prefix. +// hasprefix reports whether p begins with prefix. bool hasprefix(char *p, char *prefix) { @@ -736,7 +736,7 @@ xstrrchr(char *p, int c) return strrchr(p, c); } -// xsamefile returns whether f1 and f2 are the same file (or dir) +// xsamefile reports whether f1 and f2 are the same file (or dir) int xsamefile(char *f1, char *f2) { diff --git a/src/cmd/dist/unix.c b/src/cmd/dist/unix.c index f2ea48974..fa388e058 100644 --- a/src/cmd/dist/unix.c +++ b/src/cmd/dist/unix.c @@ -466,9 +466,12 @@ xworkdir(void) xgetenv(&b, "TMPDIR"); if(b.len == 0) bwritestr(&b, "/var/tmp"); - bwritestr(&b, "/go-cbuild-XXXXXX"); - if(mkdtemp(bstr(&b)) == nil) - fatal("mkdtemp: %s", strerror(errno)); + if(b.p[b.len-1] != '/') + bwrite(&b, "/", 1); + bwritestr(&b, "go-cbuild-XXXXXX"); + p = bstr(&b); + if(mkdtemp(p) == nil) + fatal("mkdtemp(%s): %s", p, strerror(errno)); p = btake(&b); bfree(&b); @@ -548,7 +551,7 @@ hassuffix(char *p, char *suffix) return np >= ns && strcmp(p+np-ns, suffix) == 0; } -// hasprefix reports whether p begins wtih prefix. +// hasprefix reports whether p begins with prefix. bool hasprefix(char *p, char *prefix) { @@ -651,11 +654,14 @@ int main(int argc, char **argv) { Buf b; + int osx; struct utsname u; setvbuf(stdout, nil, _IOLBF, 0); setvbuf(stderr, nil, _IOLBF, 0); + setenv("TERM", "dumb", 1); // disable escape codes in clang errors + binit(&b); slash = "/"; @@ -668,6 +674,8 @@ main(int argc, char **argv) gohostarch = "amd64"; #elif defined(__linux__) gohostos = "linux"; +#elif defined(__DragonFly__) + gohostos = "dragonfly"; #elif defined(__FreeBSD__) gohostos = "freebsd"; #elif defined(__FreeBSD_kernel__) @@ -698,17 +706,23 @@ main(int argc, char **argv) if(strcmp(gohostarch, "arm") == 0) maxnbg = 1; - // The OS X 10.6 linker does not support external - // linking mode; see - // https://code.google.com/p/go/issues/detail?id=5130 . - // The mapping from the uname release field to the OS X - // version number is complicated, but basically 10 or under is - // OS X 10.6 or earlier. + // The OS X 10.6 linker does not support external linking mode. + // See golang.org/issue/5130. + // + // OS X 10.6 does not work with clang either, but OS X 10.9 requires it. + // It seems to work with OS X 10.8, so we default to clang for 10.8 and later. + // See golang.org/issue/5822. + // + // Roughly, OS X 10.N shows up as uname release (N+4), + // so OS X 10.6 is uname version 10 and OS X 10.8 is uname version 12. if(strcmp(gohostos, "darwin") == 0) { if(uname(&u) < 0) fatal("uname: %s", strerror(errno)); - if(u.release[1] == '.' || hasprefix(u.release, "10")) + osx = atoi(u.release) - 4; + if(osx <= 6) goextlinkenabled = "0"; + if(osx >= 8) + defaultclang = 1; } init(); @@ -745,7 +759,7 @@ xstrrchr(char *p, int c) return strrchr(p, c); } -// xsamefile returns whether f1 and f2 are the same file (or dir) +// xsamefile reports whether f1 and f2 are the same file (or dir) int xsamefile(char *f1, char *f2) { diff --git a/src/cmd/dist/windows.c b/src/cmd/dist/windows.c index ba23a7ae8..7d03989b2 100644 --- a/src/cmd/dist/windows.c +++ b/src/cmd/dist/windows.c @@ -465,7 +465,7 @@ xrealwd(Buf *b, char *path) torune(&rnew, path); if(!SetCurrentDirectoryW(rnew)) fatal("chdir %s: %s", path, errstr()); - free(rnew); + xfree(rnew); xgetwd(b); if(!SetCurrentDirectoryW(old)) { breset(b); @@ -929,7 +929,7 @@ xstrrchr(char *p, int c) return nil; } -// xsamefile returns whether f1 and f2 are the same file (or dir) +// xsamefile reports whether f1 and f2 are the same file (or dir) int xsamefile(char *f1, char *f2) { diff --git a/src/cmd/fix/netipv6zone.go b/src/cmd/fix/netipv6zone.go index fe973a211..195c21807 100644 --- a/src/cmd/fix/netipv6zone.go +++ b/src/cmd/fix/netipv6zone.go @@ -51,7 +51,7 @@ func netipv6zone(f *ast.File) bool { Value: e, } case 1: - if e.(*ast.BasicLit).Value == "0" { + if elit, ok := e.(*ast.BasicLit); ok && elit.Value == "0" { cl.Elts = append(cl.Elts[:i], cl.Elts[i+1:]...) } else { cl.Elts[i] = &ast.KeyValueExpr{ diff --git a/src/cmd/fix/netipv6zone_test.go b/src/cmd/fix/netipv6zone_test.go index 0fab00531..142880a12 100644 --- a/src/cmd/fix/netipv6zone_test.go +++ b/src/cmd/fix/netipv6zone_test.go @@ -20,6 +20,8 @@ func f() net.Addr { sub(&net.UDPAddr{ip2, 12345}) c := &net.TCPAddr{IP: ip3, Port: 54321} d := &net.TCPAddr{ip4, 0} + p := 1234 + e := &net.TCPAddr{ip4, p} return &net.TCPAddr{ip5}, nil } `, @@ -32,6 +34,8 @@ func f() net.Addr { sub(&net.UDPAddr{IP: ip2, Port: 12345}) c := &net.TCPAddr{IP: ip3, Port: 54321} d := &net.TCPAddr{IP: ip4} + p := 1234 + e := &net.TCPAddr{IP: ip4, Port: p} return &net.TCPAddr{IP: ip5}, nil } `, diff --git a/src/cmd/fix/testdata/reflect.asn1.go.in b/src/cmd/fix/testdata/reflect.asn1.go.in deleted file mode 100644 index 43128f6b2..000000000 --- a/src/cmd/fix/testdata/reflect.asn1.go.in +++ /dev/null @@ -1,814 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// The asn1 package implements parsing of DER-encoded ASN.1 data structures, -// as defined in ITU-T Rec X.690. -// -// See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,'' -// http://luca.ntop.org/Teaching/Appunti/asn1.html. -package asn1 - -// ASN.1 is a syntax for specifying abstract objects and BER, DER, PER, XER etc -// are different encoding formats for those objects. Here, we'll be dealing -// with DER, the Distinguished Encoding Rules. DER is used in X.509 because -// it's fast to parse and, unlike BER, has a unique encoding for every object. -// When calculating hashes over objects, it's important that the resulting -// bytes be the same at both ends and DER removes this margin of error. -// -// ASN.1 is very complex and this package doesn't attempt to implement -// everything by any means. - -import ( - "fmt" - "os" - "reflect" - "time" -) - -// A StructuralError suggests that the ASN.1 data is valid, but the Go type -// which is receiving it doesn't match. -type StructuralError struct { - Msg string -} - -func (e StructuralError) String() string { return "ASN.1 structure error: " + e.Msg } - -// A SyntaxError suggests that the ASN.1 data is invalid. -type SyntaxError struct { - Msg string -} - -func (e SyntaxError) String() string { return "ASN.1 syntax error: " + e.Msg } - -// We start by dealing with each of the primitive types in turn. - -// BOOLEAN - -func parseBool(bytes []byte) (ret bool, err os.Error) { - if len(bytes) != 1 { - err = SyntaxError{"invalid boolean"} - return - } - - return bytes[0] != 0, nil -} - -// INTEGER - -// parseInt64 treats the given bytes as a big-endian, signed integer and -// returns the result. -func parseInt64(bytes []byte) (ret int64, err os.Error) { - if len(bytes) > 8 { - // We'll overflow an int64 in this case. - err = StructuralError{"integer too large"} - return - } - for bytesRead := 0; bytesRead < len(bytes); bytesRead++ { - ret <<= 8 - ret |= int64(bytes[bytesRead]) - } - - // Shift up and down in order to sign extend the result. - ret <<= 64 - uint8(len(bytes))*8 - ret >>= 64 - uint8(len(bytes))*8 - return -} - -// parseInt treats the given bytes as a big-endian, signed integer and returns -// the result. -func parseInt(bytes []byte) (int, os.Error) { - ret64, err := parseInt64(bytes) - if err != nil { - return 0, err - } - if ret64 != int64(int(ret64)) { - return 0, StructuralError{"integer too large"} - } - return int(ret64), nil -} - -// BIT STRING - -// BitString is the structure to use when you want an ASN.1 BIT STRING type. A -// bit string is padded up to the nearest byte in memory and the number of -// valid bits is recorded. Padding bits will be zero. -type BitString struct { - Bytes []byte // bits packed into bytes. - BitLength int // length in bits. -} - -// At returns the bit at the given index. If the index is out of range it -// returns false. -func (b BitString) At(i int) int { - if i < 0 || i >= b.BitLength { - return 0 - } - x := i / 8 - y := 7 - uint(i%8) - return int(b.Bytes[x]>>y) & 1 -} - -// RightAlign returns a slice where the padding bits are at the beginning. The -// slice may share memory with the BitString. -func (b BitString) RightAlign() []byte { - shift := uint(8 - (b.BitLength % 8)) - if shift == 8 || len(b.Bytes) == 0 { - return b.Bytes - } - - a := make([]byte, len(b.Bytes)) - a[0] = b.Bytes[0] >> shift - for i := 1; i < len(b.Bytes); i++ { - a[i] = b.Bytes[i-1] << (8 - shift) - a[i] |= b.Bytes[i] >> shift - } - - return a -} - -// parseBitString parses an ASN.1 bit string from the given byte array and returns it. -func parseBitString(bytes []byte) (ret BitString, err os.Error) { - if len(bytes) == 0 { - err = SyntaxError{"zero length BIT STRING"} - return - } - paddingBits := int(bytes[0]) - if paddingBits > 7 || - len(bytes) == 1 && paddingBits > 0 || - bytes[len(bytes)-1]&((1<<bytes[0])-1) != 0 { - err = SyntaxError{"invalid padding bits in BIT STRING"} - return - } - ret.BitLength = (len(bytes)-1)*8 - paddingBits - ret.Bytes = bytes[1:] - return -} - -// OBJECT IDENTIFIER - -// An ObjectIdentifier represents an ASN.1 OBJECT IDENTIFIER. -type ObjectIdentifier []int - -// Equal returns true iff oi and other represent the same identifier. -func (oi ObjectIdentifier) Equal(other ObjectIdentifier) bool { - if len(oi) != len(other) { - return false - } - for i := 0; i < len(oi); i++ { - if oi[i] != other[i] { - return false - } - } - - return true -} - -// parseObjectIdentifier parses an OBJECT IDENTIFER from the given bytes and -// returns it. An object identifer is a sequence of variable length integers -// that are assigned in a hierarachy. -func parseObjectIdentifier(bytes []byte) (s []int, err os.Error) { - if len(bytes) == 0 { - err = SyntaxError{"zero length OBJECT IDENTIFIER"} - return - } - - // In the worst case, we get two elements from the first byte (which is - // encoded differently) and then every varint is a single byte long. - s = make([]int, len(bytes)+1) - - // The first byte is 40*value1 + value2: - s[0] = int(bytes[0]) / 40 - s[1] = int(bytes[0]) % 40 - i := 2 - for offset := 1; offset < len(bytes); i++ { - var v int - v, offset, err = parseBase128Int(bytes, offset) - if err != nil { - return - } - s[i] = v - } - s = s[0:i] - return -} - -// ENUMERATED - -// An Enumerated is represented as a plain int. -type Enumerated int - -// FLAG - -// A Flag accepts any data and is set to true if present. -type Flag bool - -// parseBase128Int parses a base-128 encoded int from the given offset in the -// given byte array. It returns the value and the new offset. -func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err os.Error) { - offset = initOffset - for shifted := 0; offset < len(bytes); shifted++ { - if shifted > 4 { - err = StructuralError{"base 128 integer too large"} - return - } - ret <<= 7 - b := bytes[offset] - ret |= int(b & 0x7f) - offset++ - if b&0x80 == 0 { - return - } - } - err = SyntaxError{"truncated base 128 integer"} - return -} - -// UTCTime - -func parseUTCTime(bytes []byte) (ret *time.Time, err os.Error) { - s := string(bytes) - ret, err = time.Parse("0601021504Z0700", s) - if err == nil { - return - } - ret, err = time.Parse("060102150405Z0700", s) - return -} - -// parseGeneralizedTime parses the GeneralizedTime from the given byte array -// and returns the resulting time. -func parseGeneralizedTime(bytes []byte) (ret *time.Time, err os.Error) { - return time.Parse("20060102150405Z0700", string(bytes)) -} - -// PrintableString - -// parsePrintableString parses a ASN.1 PrintableString from the given byte -// array and returns it. -func parsePrintableString(bytes []byte) (ret string, err os.Error) { - for _, b := range bytes { - if !isPrintable(b) { - err = SyntaxError{"PrintableString contains invalid character"} - return - } - } - ret = string(bytes) - return -} - -// isPrintable returns true iff the given b is in the ASN.1 PrintableString set. -func isPrintable(b byte) bool { - return 'a' <= b && b <= 'z' || - 'A' <= b && b <= 'Z' || - '0' <= b && b <= '9' || - '\'' <= b && b <= ')' || - '+' <= b && b <= '/' || - b == ' ' || - b == ':' || - b == '=' || - b == '?' || - // This is techincally not allowed in a PrintableString. - // However, x509 certificates with wildcard strings don't - // always use the correct string type so we permit it. - b == '*' -} - -// IA5String - -// parseIA5String parses a ASN.1 IA5String (ASCII string) from the given -// byte array and returns it. -func parseIA5String(bytes []byte) (ret string, err os.Error) { - for _, b := range bytes { - if b >= 0x80 { - err = SyntaxError{"IA5String contains invalid character"} - return - } - } - ret = string(bytes) - return -} - -// T61String - -// parseT61String parses a ASN.1 T61String (8-bit clean string) from the given -// byte array and returns it. -func parseT61String(bytes []byte) (ret string, err os.Error) { - return string(bytes), nil -} - -// A RawValue represents an undecoded ASN.1 object. -type RawValue struct { - Class, Tag int - IsCompound bool - Bytes []byte - FullBytes []byte // includes the tag and length -} - -// RawContent is used to signal that the undecoded, DER data needs to be -// preserved for a struct. To use it, the first field of the struct must have -// this type. It's an error for any of the other fields to have this type. -type RawContent []byte - -// Tagging - -// parseTagAndLength parses an ASN.1 tag and length pair from the given offset -// into a byte array. It returns the parsed data and the new offset. SET and -// SET OF (tag 17) are mapped to SEQUENCE and SEQUENCE OF (tag 16) since we -// don't distinguish between ordered and unordered objects in this code. -func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset int, err os.Error) { - offset = initOffset - b := bytes[offset] - offset++ - ret.class = int(b >> 6) - ret.isCompound = b&0x20 == 0x20 - ret.tag = int(b & 0x1f) - - // If the bottom five bits are set, then the tag number is actually base 128 - // encoded afterwards - if ret.tag == 0x1f { - ret.tag, offset, err = parseBase128Int(bytes, offset) - if err != nil { - return - } - } - if offset >= len(bytes) { - err = SyntaxError{"truncated tag or length"} - return - } - b = bytes[offset] - offset++ - if b&0x80 == 0 { - // The length is encoded in the bottom 7 bits. - ret.length = int(b & 0x7f) - } else { - // Bottom 7 bits give the number of length bytes to follow. - numBytes := int(b & 0x7f) - // We risk overflowing a signed 32-bit number if we accept more than 3 bytes. - if numBytes > 3 { - err = StructuralError{"length too large"} - return - } - if numBytes == 0 { - err = SyntaxError{"indefinite length found (not DER)"} - return - } - ret.length = 0 - for i := 0; i < numBytes; i++ { - if offset >= len(bytes) { - err = SyntaxError{"truncated tag or length"} - return - } - b = bytes[offset] - offset++ - ret.length <<= 8 - ret.length |= int(b) - } - } - - return -} - -// parseSequenceOf is used for SEQUENCE OF and SET OF values. It tries to parse -// a number of ASN.1 values from the given byte array and returns them as a -// slice of Go values of the given type. -func parseSequenceOf(bytes []byte, sliceType *reflect.SliceType, elemType reflect.Type) (ret *reflect.SliceValue, err os.Error) { - expectedTag, compoundType, ok := getUniversalType(elemType) - if !ok { - err = StructuralError{"unknown Go type for slice"} - return - } - - // First we iterate over the input and count the number of elements, - // checking that the types are correct in each case. - numElements := 0 - for offset := 0; offset < len(bytes); { - var t tagAndLength - t, offset, err = parseTagAndLength(bytes, offset) - if err != nil { - return - } - // We pretend that GENERAL STRINGs are PRINTABLE STRINGs so - // that a sequence of them can be parsed into a []string. - if t.tag == tagGeneralString { - t.tag = tagPrintableString - } - if t.class != classUniversal || t.isCompound != compoundType || t.tag != expectedTag { - err = StructuralError{"sequence tag mismatch"} - return - } - if invalidLength(offset, t.length, len(bytes)) { - err = SyntaxError{"truncated sequence"} - return - } - offset += t.length - numElements++ - } - ret = reflect.MakeSlice(sliceType, numElements, numElements) - params := fieldParameters{} - offset := 0 - for i := 0; i < numElements; i++ { - offset, err = parseField(ret.Elem(i), bytes, offset, params) - if err != nil { - return - } - } - return -} - -var ( - bitStringType = reflect.Typeof(BitString{}) - objectIdentifierType = reflect.Typeof(ObjectIdentifier{}) - enumeratedType = reflect.Typeof(Enumerated(0)) - flagType = reflect.Typeof(Flag(false)) - timeType = reflect.Typeof(&time.Time{}) - rawValueType = reflect.Typeof(RawValue{}) - rawContentsType = reflect.Typeof(RawContent(nil)) -) - -// invalidLength returns true iff offset + length > sliceLength, or if the -// addition would overflow. -func invalidLength(offset, length, sliceLength int) bool { - return offset+length < offset || offset+length > sliceLength -} - -// parseField is the main parsing function. Given a byte array and an offset -// into the array, it will try to parse a suitable ASN.1 value out and store it -// in the given Value. -func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParameters) (offset int, err os.Error) { - offset = initOffset - fieldType := v.Type() - - // If we have run out of data, it may be that there are optional elements at the end. - if offset == len(bytes) { - if !setDefaultValue(v, params) { - err = SyntaxError{"sequence truncated"} - } - return - } - - // Deal with raw values. - if fieldType == rawValueType { - var t tagAndLength - t, offset, err = parseTagAndLength(bytes, offset) - if err != nil { - return - } - if invalidLength(offset, t.length, len(bytes)) { - err = SyntaxError{"data truncated"} - return - } - result := RawValue{t.class, t.tag, t.isCompound, bytes[offset : offset+t.length], bytes[initOffset : offset+t.length]} - offset += t.length - v.(*reflect.StructValue).Set(reflect.NewValue(result).(*reflect.StructValue)) - return - } - - // Deal with the ANY type. - if ifaceType, ok := fieldType.(*reflect.InterfaceType); ok && ifaceType.NumMethod() == 0 { - ifaceValue := v.(*reflect.InterfaceValue) - var t tagAndLength - t, offset, err = parseTagAndLength(bytes, offset) - if err != nil { - return - } - if invalidLength(offset, t.length, len(bytes)) { - err = SyntaxError{"data truncated"} - return - } - var result interface{} - if !t.isCompound && t.class == classUniversal { - innerBytes := bytes[offset : offset+t.length] - switch t.tag { - case tagPrintableString: - result, err = parsePrintableString(innerBytes) - case tagIA5String: - result, err = parseIA5String(innerBytes) - case tagT61String: - result, err = parseT61String(innerBytes) - case tagInteger: - result, err = parseInt64(innerBytes) - case tagBitString: - result, err = parseBitString(innerBytes) - case tagOID: - result, err = parseObjectIdentifier(innerBytes) - case tagUTCTime: - result, err = parseUTCTime(innerBytes) - case tagOctetString: - result = innerBytes - default: - // If we don't know how to handle the type, we just leave Value as nil. - } - } - offset += t.length - if err != nil { - return - } - if result != nil { - ifaceValue.Set(reflect.NewValue(result)) - } - return - } - universalTag, compoundType, ok1 := getUniversalType(fieldType) - if !ok1 { - err = StructuralError{fmt.Sprintf("unknown Go type: %v", fieldType)} - return - } - - t, offset, err := parseTagAndLength(bytes, offset) - if err != nil { - return - } - if params.explicit { - expectedClass := classContextSpecific - if params.application { - expectedClass = classApplication - } - if t.class == expectedClass && t.tag == *params.tag && (t.length == 0 || t.isCompound) { - if t.length > 0 { - t, offset, err = parseTagAndLength(bytes, offset) - if err != nil { - return - } - } else { - if fieldType != flagType { - err = StructuralError{"Zero length explicit tag was not an asn1.Flag"} - return - } - - flagValue := v.(*reflect.BoolValue) - flagValue.Set(true) - return - } - } else { - // The tags didn't match, it might be an optional element. - ok := setDefaultValue(v, params) - if ok { - offset = initOffset - } else { - err = StructuralError{"explicitly tagged member didn't match"} - } - return - } - } - - // Special case for strings: PrintableString and IA5String both map to - // the Go type string. getUniversalType returns the tag for - // PrintableString when it sees a string so, if we see an IA5String on - // the wire, we change the universal type to match. - if universalTag == tagPrintableString && t.tag == tagIA5String { - universalTag = tagIA5String - } - // Likewise for GeneralString - if universalTag == tagPrintableString && t.tag == tagGeneralString { - universalTag = tagGeneralString - } - - // Special case for time: UTCTime and GeneralizedTime both map to the - // Go type time.Time. - if universalTag == tagUTCTime && t.tag == tagGeneralizedTime { - universalTag = tagGeneralizedTime - } - - expectedClass := classUniversal - expectedTag := universalTag - - if !params.explicit && params.tag != nil { - expectedClass = classContextSpecific - expectedTag = *params.tag - } - - if !params.explicit && params.application && params.tag != nil { - expectedClass = classApplication - expectedTag = *params.tag - } - - // We have unwrapped any explicit tagging at this point. - if t.class != expectedClass || t.tag != expectedTag || t.isCompound != compoundType { - // Tags don't match. Again, it could be an optional element. - ok := setDefaultValue(v, params) - if ok { - offset = initOffset - } else { - err = StructuralError{fmt.Sprintf("tags don't match (%d vs %+v) %+v %s @%d", expectedTag, t, params, fieldType.Name(), offset)} - } - return - } - if invalidLength(offset, t.length, len(bytes)) { - err = SyntaxError{"data truncated"} - return - } - innerBytes := bytes[offset : offset+t.length] - offset += t.length - - // We deal with the structures defined in this package first. - switch fieldType { - case objectIdentifierType: - newSlice, err1 := parseObjectIdentifier(innerBytes) - sliceValue := v.(*reflect.SliceValue) - sliceValue.Set(reflect.MakeSlice(sliceValue.Type().(*reflect.SliceType), len(newSlice), len(newSlice))) - if err1 == nil { - reflect.Copy(sliceValue, reflect.NewValue(newSlice).(reflect.ArrayOrSliceValue)) - } - err = err1 - return - case bitStringType: - structValue := v.(*reflect.StructValue) - bs, err1 := parseBitString(innerBytes) - if err1 == nil { - structValue.Set(reflect.NewValue(bs).(*reflect.StructValue)) - } - err = err1 - return - case timeType: - ptrValue := v.(*reflect.PtrValue) - var time *time.Time - var err1 os.Error - if universalTag == tagUTCTime { - time, err1 = parseUTCTime(innerBytes) - } else { - time, err1 = parseGeneralizedTime(innerBytes) - } - if err1 == nil { - ptrValue.Set(reflect.NewValue(time).(*reflect.PtrValue)) - } - err = err1 - return - case enumeratedType: - parsedInt, err1 := parseInt(innerBytes) - enumValue := v.(*reflect.IntValue) - if err1 == nil { - enumValue.Set(int64(parsedInt)) - } - err = err1 - return - case flagType: - flagValue := v.(*reflect.BoolValue) - flagValue.Set(true) - return - } - switch val := v.(type) { - case *reflect.BoolValue: - parsedBool, err1 := parseBool(innerBytes) - if err1 == nil { - val.Set(parsedBool) - } - err = err1 - return - case *reflect.IntValue: - switch val.Type().Kind() { - case reflect.Int: - parsedInt, err1 := parseInt(innerBytes) - if err1 == nil { - val.Set(int64(parsedInt)) - } - err = err1 - return - case reflect.Int64: - parsedInt, err1 := parseInt64(innerBytes) - if err1 == nil { - val.Set(parsedInt) - } - err = err1 - return - } - case *reflect.StructValue: - structType := fieldType.(*reflect.StructType) - - if structType.NumField() > 0 && - structType.Field(0).Type == rawContentsType { - bytes := bytes[initOffset:offset] - val.Field(0).SetValue(reflect.NewValue(RawContent(bytes))) - } - - innerOffset := 0 - for i := 0; i < structType.NumField(); i++ { - field := structType.Field(i) - if i == 0 && field.Type == rawContentsType { - continue - } - innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, parseFieldParameters(field.Tag)) - if err != nil { - return - } - } - // We allow extra bytes at the end of the SEQUENCE because - // adding elements to the end has been used in X.509 as the - // version numbers have increased. - return - case *reflect.SliceValue: - sliceType := fieldType.(*reflect.SliceType) - if sliceType.Elem().Kind() == reflect.Uint8 { - val.Set(reflect.MakeSlice(sliceType, len(innerBytes), len(innerBytes))) - reflect.Copy(val, reflect.NewValue(innerBytes).(reflect.ArrayOrSliceValue)) - return - } - newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem()) - if err1 == nil { - val.Set(newSlice) - } - err = err1 - return - case *reflect.StringValue: - var v string - switch universalTag { - case tagPrintableString: - v, err = parsePrintableString(innerBytes) - case tagIA5String: - v, err = parseIA5String(innerBytes) - case tagT61String: - v, err = parseT61String(innerBytes) - case tagGeneralString: - // GeneralString is specified in ISO-2022/ECMA-35, - // A brief review suggests that it includes structures - // that allow the encoding to change midstring and - // such. We give up and pass it as an 8-bit string. - v, err = parseT61String(innerBytes) - default: - err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)} - } - if err == nil { - val.Set(v) - } - return - } - err = StructuralError{"unknown Go type"} - return -} - -// setDefaultValue is used to install a default value, from a tag string, into -// a Value. It is successful is the field was optional, even if a default value -// wasn't provided or it failed to install it into the Value. -func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) { - if !params.optional { - return - } - ok = true - if params.defaultValue == nil { - return - } - switch val := v.(type) { - case *reflect.IntValue: - val.Set(*params.defaultValue) - } - return -} - -// Unmarshal parses the DER-encoded ASN.1 data structure b -// and uses the reflect package to fill in an arbitrary value pointed at by val. -// Because Unmarshal uses the reflect package, the structs -// being written to must use upper case field names. -// -// An ASN.1 INTEGER can be written to an int or int64. -// If the encoded value does not fit in the Go type, -// Unmarshal returns a parse error. -// -// An ASN.1 BIT STRING can be written to a BitString. -// -// An ASN.1 OCTET STRING can be written to a []byte. -// -// An ASN.1 OBJECT IDENTIFIER can be written to an -// ObjectIdentifier. -// -// An ASN.1 ENUMERATED can be written to an Enumerated. -// -// An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a *time.Time. -// -// An ASN.1 PrintableString or IA5String can be written to a string. -// -// Any of the above ASN.1 values can be written to an interface{}. -// The value stored in the interface has the corresponding Go type. -// For integers, that type is int64. -// -// An ASN.1 SEQUENCE OF x or SET OF x can be written -// to a slice if an x can be written to the slice's element type. -// -// An ASN.1 SEQUENCE or SET can be written to a struct -// if each of the elements in the sequence can be -// written to the corresponding element in the struct. -// -// The following tags on struct fields have special meaning to Unmarshal: -// -// optional marks the field as ASN.1 OPTIONAL -// [explicit] tag:x specifies the ASN.1 tag number; implies ASN.1 CONTEXT SPECIFIC -// default:x sets the default value for optional integer fields -// -// If the type of the first field of a structure is RawContent then the raw -// ASN1 contents of the struct will be stored in it. -// -// Other ASN.1 types are not supported; if it encounters them, -// Unmarshal returns a parse error. -func Unmarshal(b []byte, val interface{}) (rest []byte, err os.Error) { - return UnmarshalWithParams(b, val, "") -} - -// UnmarshalWithParams allows field parameters to be specified for the -// top-level element. The form of the params is the same as the field tags. -func UnmarshalWithParams(b []byte, val interface{}, params string) (rest []byte, err os.Error) { - v := reflect.NewValue(val).(*reflect.PtrValue).Elem() - offset, err := parseField(v, b, 0, parseFieldParameters(params)) - if err != nil { - return nil, err - } - return b[offset:], nil -} diff --git a/src/cmd/fix/testdata/reflect.asn1.go.out b/src/cmd/fix/testdata/reflect.asn1.go.out deleted file mode 100644 index ba6224e6d..000000000 --- a/src/cmd/fix/testdata/reflect.asn1.go.out +++ /dev/null @@ -1,814 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// The asn1 package implements parsing of DER-encoded ASN.1 data structures, -// as defined in ITU-T Rec X.690. -// -// See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,'' -// http://luca.ntop.org/Teaching/Appunti/asn1.html. -package asn1 - -// ASN.1 is a syntax for specifying abstract objects and BER, DER, PER, XER etc -// are different encoding formats for those objects. Here, we'll be dealing -// with DER, the Distinguished Encoding Rules. DER is used in X.509 because -// it's fast to parse and, unlike BER, has a unique encoding for every object. -// When calculating hashes over objects, it's important that the resulting -// bytes be the same at both ends and DER removes this margin of error. -// -// ASN.1 is very complex and this package doesn't attempt to implement -// everything by any means. - -import ( - "fmt" - "os" - "reflect" - "time" -) - -// A StructuralError suggests that the ASN.1 data is valid, but the Go type -// which is receiving it doesn't match. -type StructuralError struct { - Msg string -} - -func (e StructuralError) String() string { return "ASN.1 structure error: " + e.Msg } - -// A SyntaxError suggests that the ASN.1 data is invalid. -type SyntaxError struct { - Msg string -} - -func (e SyntaxError) String() string { return "ASN.1 syntax error: " + e.Msg } - -// We start by dealing with each of the primitive types in turn. - -// BOOLEAN - -func parseBool(bytes []byte) (ret bool, err os.Error) { - if len(bytes) != 1 { - err = SyntaxError{"invalid boolean"} - return - } - - return bytes[0] != 0, nil -} - -// INTEGER - -// parseInt64 treats the given bytes as a big-endian, signed integer and -// returns the result. -func parseInt64(bytes []byte) (ret int64, err os.Error) { - if len(bytes) > 8 { - // We'll overflow an int64 in this case. - err = StructuralError{"integer too large"} - return - } - for bytesRead := 0; bytesRead < len(bytes); bytesRead++ { - ret <<= 8 - ret |= int64(bytes[bytesRead]) - } - - // Shift up and down in order to sign extend the result. - ret <<= 64 - uint8(len(bytes))*8 - ret >>= 64 - uint8(len(bytes))*8 - return -} - -// parseInt treats the given bytes as a big-endian, signed integer and returns -// the result. -func parseInt(bytes []byte) (int, os.Error) { - ret64, err := parseInt64(bytes) - if err != nil { - return 0, err - } - if ret64 != int64(int(ret64)) { - return 0, StructuralError{"integer too large"} - } - return int(ret64), nil -} - -// BIT STRING - -// BitString is the structure to use when you want an ASN.1 BIT STRING type. A -// bit string is padded up to the nearest byte in memory and the number of -// valid bits is recorded. Padding bits will be zero. -type BitString struct { - Bytes []byte // bits packed into bytes. - BitLength int // length in bits. -} - -// At returns the bit at the given index. If the index is out of range it -// returns false. -func (b BitString) At(i int) int { - if i < 0 || i >= b.BitLength { - return 0 - } - x := i / 8 - y := 7 - uint(i%8) - return int(b.Bytes[x]>>y) & 1 -} - -// RightAlign returns a slice where the padding bits are at the beginning. The -// slice may share memory with the BitString. -func (b BitString) RightAlign() []byte { - shift := uint(8 - (b.BitLength % 8)) - if shift == 8 || len(b.Bytes) == 0 { - return b.Bytes - } - - a := make([]byte, len(b.Bytes)) - a[0] = b.Bytes[0] >> shift - for i := 1; i < len(b.Bytes); i++ { - a[i] = b.Bytes[i-1] << (8 - shift) - a[i] |= b.Bytes[i] >> shift - } - - return a -} - -// parseBitString parses an ASN.1 bit string from the given byte array and returns it. -func parseBitString(bytes []byte) (ret BitString, err os.Error) { - if len(bytes) == 0 { - err = SyntaxError{"zero length BIT STRING"} - return - } - paddingBits := int(bytes[0]) - if paddingBits > 7 || - len(bytes) == 1 && paddingBits > 0 || - bytes[len(bytes)-1]&((1<<bytes[0])-1) != 0 { - err = SyntaxError{"invalid padding bits in BIT STRING"} - return - } - ret.BitLength = (len(bytes)-1)*8 - paddingBits - ret.Bytes = bytes[1:] - return -} - -// OBJECT IDENTIFIER - -// An ObjectIdentifier represents an ASN.1 OBJECT IDENTIFIER. -type ObjectIdentifier []int - -// Equal returns true iff oi and other represent the same identifier. -func (oi ObjectIdentifier) Equal(other ObjectIdentifier) bool { - if len(oi) != len(other) { - return false - } - for i := 0; i < len(oi); i++ { - if oi[i] != other[i] { - return false - } - } - - return true -} - -// parseObjectIdentifier parses an OBJECT IDENTIFER from the given bytes and -// returns it. An object identifer is a sequence of variable length integers -// that are assigned in a hierarachy. -func parseObjectIdentifier(bytes []byte) (s []int, err os.Error) { - if len(bytes) == 0 { - err = SyntaxError{"zero length OBJECT IDENTIFIER"} - return - } - - // In the worst case, we get two elements from the first byte (which is - // encoded differently) and then every varint is a single byte long. - s = make([]int, len(bytes)+1) - - // The first byte is 40*value1 + value2: - s[0] = int(bytes[0]) / 40 - s[1] = int(bytes[0]) % 40 - i := 2 - for offset := 1; offset < len(bytes); i++ { - var v int - v, offset, err = parseBase128Int(bytes, offset) - if err != nil { - return - } - s[i] = v - } - s = s[0:i] - return -} - -// ENUMERATED - -// An Enumerated is represented as a plain int. -type Enumerated int - -// FLAG - -// A Flag accepts any data and is set to true if present. -type Flag bool - -// parseBase128Int parses a base-128 encoded int from the given offset in the -// given byte array. It returns the value and the new offset. -func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err os.Error) { - offset = initOffset - for shifted := 0; offset < len(bytes); shifted++ { - if shifted > 4 { - err = StructuralError{"base 128 integer too large"} - return - } - ret <<= 7 - b := bytes[offset] - ret |= int(b & 0x7f) - offset++ - if b&0x80 == 0 { - return - } - } - err = SyntaxError{"truncated base 128 integer"} - return -} - -// UTCTime - -func parseUTCTime(bytes []byte) (ret *time.Time, err os.Error) { - s := string(bytes) - ret, err = time.Parse("0601021504Z0700", s) - if err == nil { - return - } - ret, err = time.Parse("060102150405Z0700", s) - return -} - -// parseGeneralizedTime parses the GeneralizedTime from the given byte array -// and returns the resulting time. -func parseGeneralizedTime(bytes []byte) (ret *time.Time, err os.Error) { - return time.Parse("20060102150405Z0700", string(bytes)) -} - -// PrintableString - -// parsePrintableString parses a ASN.1 PrintableString from the given byte -// array and returns it. -func parsePrintableString(bytes []byte) (ret string, err os.Error) { - for _, b := range bytes { - if !isPrintable(b) { - err = SyntaxError{"PrintableString contains invalid character"} - return - } - } - ret = string(bytes) - return -} - -// isPrintable returns true iff the given b is in the ASN.1 PrintableString set. -func isPrintable(b byte) bool { - return 'a' <= b && b <= 'z' || - 'A' <= b && b <= 'Z' || - '0' <= b && b <= '9' || - '\'' <= b && b <= ')' || - '+' <= b && b <= '/' || - b == ' ' || - b == ':' || - b == '=' || - b == '?' || - // This is techincally not allowed in a PrintableString. - // However, x509 certificates with wildcard strings don't - // always use the correct string type so we permit it. - b == '*' -} - -// IA5String - -// parseIA5String parses a ASN.1 IA5String (ASCII string) from the given -// byte array and returns it. -func parseIA5String(bytes []byte) (ret string, err os.Error) { - for _, b := range bytes { - if b >= 0x80 { - err = SyntaxError{"IA5String contains invalid character"} - return - } - } - ret = string(bytes) - return -} - -// T61String - -// parseT61String parses a ASN.1 T61String (8-bit clean string) from the given -// byte array and returns it. -func parseT61String(bytes []byte) (ret string, err os.Error) { - return string(bytes), nil -} - -// A RawValue represents an undecoded ASN.1 object. -type RawValue struct { - Class, Tag int - IsCompound bool - Bytes []byte - FullBytes []byte // includes the tag and length -} - -// RawContent is used to signal that the undecoded, DER data needs to be -// preserved for a struct. To use it, the first field of the struct must have -// this type. It's an error for any of the other fields to have this type. -type RawContent []byte - -// Tagging - -// parseTagAndLength parses an ASN.1 tag and length pair from the given offset -// into a byte array. It returns the parsed data and the new offset. SET and -// SET OF (tag 17) are mapped to SEQUENCE and SEQUENCE OF (tag 16) since we -// don't distinguish between ordered and unordered objects in this code. -func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset int, err os.Error) { - offset = initOffset - b := bytes[offset] - offset++ - ret.class = int(b >> 6) - ret.isCompound = b&0x20 == 0x20 - ret.tag = int(b & 0x1f) - - // If the bottom five bits are set, then the tag number is actually base 128 - // encoded afterwards - if ret.tag == 0x1f { - ret.tag, offset, err = parseBase128Int(bytes, offset) - if err != nil { - return - } - } - if offset >= len(bytes) { - err = SyntaxError{"truncated tag or length"} - return - } - b = bytes[offset] - offset++ - if b&0x80 == 0 { - // The length is encoded in the bottom 7 bits. - ret.length = int(b & 0x7f) - } else { - // Bottom 7 bits give the number of length bytes to follow. - numBytes := int(b & 0x7f) - // We risk overflowing a signed 32-bit number if we accept more than 3 bytes. - if numBytes > 3 { - err = StructuralError{"length too large"} - return - } - if numBytes == 0 { - err = SyntaxError{"indefinite length found (not DER)"} - return - } - ret.length = 0 - for i := 0; i < numBytes; i++ { - if offset >= len(bytes) { - err = SyntaxError{"truncated tag or length"} - return - } - b = bytes[offset] - offset++ - ret.length <<= 8 - ret.length |= int(b) - } - } - - return -} - -// parseSequenceOf is used for SEQUENCE OF and SET OF values. It tries to parse -// a number of ASN.1 values from the given byte array and returns them as a -// slice of Go values of the given type. -func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type) (ret reflect.Value, err os.Error) { - expectedTag, compoundType, ok := getUniversalType(elemType) - if !ok { - err = StructuralError{"unknown Go type for slice"} - return - } - - // First we iterate over the input and count the number of elements, - // checking that the types are correct in each case. - numElements := 0 - for offset := 0; offset < len(bytes); { - var t tagAndLength - t, offset, err = parseTagAndLength(bytes, offset) - if err != nil { - return - } - // We pretend that GENERAL STRINGs are PRINTABLE STRINGs so - // that a sequence of them can be parsed into a []string. - if t.tag == tagGeneralString { - t.tag = tagPrintableString - } - if t.class != classUniversal || t.isCompound != compoundType || t.tag != expectedTag { - err = StructuralError{"sequence tag mismatch"} - return - } - if invalidLength(offset, t.length, len(bytes)) { - err = SyntaxError{"truncated sequence"} - return - } - offset += t.length - numElements++ - } - ret = reflect.MakeSlice(sliceType, numElements, numElements) - params := fieldParameters{} - offset := 0 - for i := 0; i < numElements; i++ { - offset, err = parseField(ret.Index(i), bytes, offset, params) - if err != nil { - return - } - } - return -} - -var ( - bitStringType = reflect.TypeOf(BitString{}) - objectIdentifierType = reflect.TypeOf(ObjectIdentifier{}) - enumeratedType = reflect.TypeOf(Enumerated(0)) - flagType = reflect.TypeOf(Flag(false)) - timeType = reflect.TypeOf(&time.Time{}) - rawValueType = reflect.TypeOf(RawValue{}) - rawContentsType = reflect.TypeOf(RawContent(nil)) -) - -// invalidLength returns true iff offset + length > sliceLength, or if the -// addition would overflow. -func invalidLength(offset, length, sliceLength int) bool { - return offset+length < offset || offset+length > sliceLength -} - -// parseField is the main parsing function. Given a byte array and an offset -// into the array, it will try to parse a suitable ASN.1 value out and store it -// in the given Value. -func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParameters) (offset int, err os.Error) { - offset = initOffset - fieldType := v.Type() - - // If we have run out of data, it may be that there are optional elements at the end. - if offset == len(bytes) { - if !setDefaultValue(v, params) { - err = SyntaxError{"sequence truncated"} - } - return - } - - // Deal with raw values. - if fieldType == rawValueType { - var t tagAndLength - t, offset, err = parseTagAndLength(bytes, offset) - if err != nil { - return - } - if invalidLength(offset, t.length, len(bytes)) { - err = SyntaxError{"data truncated"} - return - } - result := RawValue{t.class, t.tag, t.isCompound, bytes[offset : offset+t.length], bytes[initOffset : offset+t.length]} - offset += t.length - v.Set(reflect.ValueOf(result)) - return - } - - // Deal with the ANY type. - if ifaceType := fieldType; ifaceType.Kind() == reflect.Interface && ifaceType.NumMethod() == 0 { - ifaceValue := v - var t tagAndLength - t, offset, err = parseTagAndLength(bytes, offset) - if err != nil { - return - } - if invalidLength(offset, t.length, len(bytes)) { - err = SyntaxError{"data truncated"} - return - } - var result interface{} - if !t.isCompound && t.class == classUniversal { - innerBytes := bytes[offset : offset+t.length] - switch t.tag { - case tagPrintableString: - result, err = parsePrintableString(innerBytes) - case tagIA5String: - result, err = parseIA5String(innerBytes) - case tagT61String: - result, err = parseT61String(innerBytes) - case tagInteger: - result, err = parseInt64(innerBytes) - case tagBitString: - result, err = parseBitString(innerBytes) - case tagOID: - result, err = parseObjectIdentifier(innerBytes) - case tagUTCTime: - result, err = parseUTCTime(innerBytes) - case tagOctetString: - result = innerBytes - default: - // If we don't know how to handle the type, we just leave Value as nil. - } - } - offset += t.length - if err != nil { - return - } - if result != nil { - ifaceValue.Set(reflect.ValueOf(result)) - } - return - } - universalTag, compoundType, ok1 := getUniversalType(fieldType) - if !ok1 { - err = StructuralError{fmt.Sprintf("unknown Go type: %v", fieldType)} - return - } - - t, offset, err := parseTagAndLength(bytes, offset) - if err != nil { - return - } - if params.explicit { - expectedClass := classContextSpecific - if params.application { - expectedClass = classApplication - } - if t.class == expectedClass && t.tag == *params.tag && (t.length == 0 || t.isCompound) { - if t.length > 0 { - t, offset, err = parseTagAndLength(bytes, offset) - if err != nil { - return - } - } else { - if fieldType != flagType { - err = StructuralError{"Zero length explicit tag was not an asn1.Flag"} - return - } - - flagValue := v - flagValue.SetBool(true) - return - } - } else { - // The tags didn't match, it might be an optional element. - ok := setDefaultValue(v, params) - if ok { - offset = initOffset - } else { - err = StructuralError{"explicitly tagged member didn't match"} - } - return - } - } - - // Special case for strings: PrintableString and IA5String both map to - // the Go type string. getUniversalType returns the tag for - // PrintableString when it sees a string so, if we see an IA5String on - // the wire, we change the universal type to match. - if universalTag == tagPrintableString && t.tag == tagIA5String { - universalTag = tagIA5String - } - // Likewise for GeneralString - if universalTag == tagPrintableString && t.tag == tagGeneralString { - universalTag = tagGeneralString - } - - // Special case for time: UTCTime and GeneralizedTime both map to the - // Go type time.Time. - if universalTag == tagUTCTime && t.tag == tagGeneralizedTime { - universalTag = tagGeneralizedTime - } - - expectedClass := classUniversal - expectedTag := universalTag - - if !params.explicit && params.tag != nil { - expectedClass = classContextSpecific - expectedTag = *params.tag - } - - if !params.explicit && params.application && params.tag != nil { - expectedClass = classApplication - expectedTag = *params.tag - } - - // We have unwrapped any explicit tagging at this point. - if t.class != expectedClass || t.tag != expectedTag || t.isCompound != compoundType { - // Tags don't match. Again, it could be an optional element. - ok := setDefaultValue(v, params) - if ok { - offset = initOffset - } else { - err = StructuralError{fmt.Sprintf("tags don't match (%d vs %+v) %+v %s @%d", expectedTag, t, params, fieldType.Name(), offset)} - } - return - } - if invalidLength(offset, t.length, len(bytes)) { - err = SyntaxError{"data truncated"} - return - } - innerBytes := bytes[offset : offset+t.length] - offset += t.length - - // We deal with the structures defined in this package first. - switch fieldType { - case objectIdentifierType: - newSlice, err1 := parseObjectIdentifier(innerBytes) - sliceValue := v - sliceValue.Set(reflect.MakeSlice(sliceValue.Type(), len(newSlice), len(newSlice))) - if err1 == nil { - reflect.Copy(sliceValue, reflect.ValueOf(newSlice)) - } - err = err1 - return - case bitStringType: - structValue := v - bs, err1 := parseBitString(innerBytes) - if err1 == nil { - structValue.Set(reflect.ValueOf(bs)) - } - err = err1 - return - case timeType: - ptrValue := v - var time *time.Time - var err1 os.Error - if universalTag == tagUTCTime { - time, err1 = parseUTCTime(innerBytes) - } else { - time, err1 = parseGeneralizedTime(innerBytes) - } - if err1 == nil { - ptrValue.Set(reflect.ValueOf(time)) - } - err = err1 - return - case enumeratedType: - parsedInt, err1 := parseInt(innerBytes) - enumValue := v - if err1 == nil { - enumValue.SetInt(int64(parsedInt)) - } - err = err1 - return - case flagType: - flagValue := v - flagValue.SetBool(true) - return - } - switch val := v; val.Kind() { - case reflect.Bool: - parsedBool, err1 := parseBool(innerBytes) - if err1 == nil { - val.SetBool(parsedBool) - } - err = err1 - return - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - switch val.Type().Kind() { - case reflect.Int: - parsedInt, err1 := parseInt(innerBytes) - if err1 == nil { - val.SetInt(int64(parsedInt)) - } - err = err1 - return - case reflect.Int64: - parsedInt, err1 := parseInt64(innerBytes) - if err1 == nil { - val.SetInt(parsedInt) - } - err = err1 - return - } - case reflect.Struct: - structType := fieldType - - if structType.NumField() > 0 && - structType.Field(0).Type == rawContentsType { - bytes := bytes[initOffset:offset] - val.Field(0).Set(reflect.ValueOf(RawContent(bytes))) - } - - innerOffset := 0 - for i := 0; i < structType.NumField(); i++ { - field := structType.Field(i) - if i == 0 && field.Type == rawContentsType { - continue - } - innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, parseFieldParameters(field.Tag)) - if err != nil { - return - } - } - // We allow extra bytes at the end of the SEQUENCE because - // adding elements to the end has been used in X.509 as the - // version numbers have increased. - return - case reflect.Slice: - sliceType := fieldType - if sliceType.Elem().Kind() == reflect.Uint8 { - val.Set(reflect.MakeSlice(sliceType, len(innerBytes), len(innerBytes))) - reflect.Copy(val, reflect.ValueOf(innerBytes)) - return - } - newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem()) - if err1 == nil { - val.Set(newSlice) - } - err = err1 - return - case reflect.String: - var v string - switch universalTag { - case tagPrintableString: - v, err = parsePrintableString(innerBytes) - case tagIA5String: - v, err = parseIA5String(innerBytes) - case tagT61String: - v, err = parseT61String(innerBytes) - case tagGeneralString: - // GeneralString is specified in ISO-2022/ECMA-35, - // A brief review suggests that it includes structures - // that allow the encoding to change midstring and - // such. We give up and pass it as an 8-bit string. - v, err = parseT61String(innerBytes) - default: - err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)} - } - if err == nil { - val.SetString(v) - } - return - } - err = StructuralError{"unknown Go type"} - return -} - -// setDefaultValue is used to install a default value, from a tag string, into -// a Value. It is successful is the field was optional, even if a default value -// wasn't provided or it failed to install it into the Value. -func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) { - if !params.optional { - return - } - ok = true - if params.defaultValue == nil { - return - } - switch val := v; val.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - val.SetInt(*params.defaultValue) - } - return -} - -// Unmarshal parses the DER-encoded ASN.1 data structure b -// and uses the reflect package to fill in an arbitrary value pointed at by val. -// Because Unmarshal uses the reflect package, the structs -// being written to must use upper case field names. -// -// An ASN.1 INTEGER can be written to an int or int64. -// If the encoded value does not fit in the Go type, -// Unmarshal returns a parse error. -// -// An ASN.1 BIT STRING can be written to a BitString. -// -// An ASN.1 OCTET STRING can be written to a []byte. -// -// An ASN.1 OBJECT IDENTIFIER can be written to an -// ObjectIdentifier. -// -// An ASN.1 ENUMERATED can be written to an Enumerated. -// -// An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a *time.Time. -// -// An ASN.1 PrintableString or IA5String can be written to a string. -// -// Any of the above ASN.1 values can be written to an interface{}. -// The value stored in the interface has the corresponding Go type. -// For integers, that type is int64. -// -// An ASN.1 SEQUENCE OF x or SET OF x can be written -// to a slice if an x can be written to the slice's element type. -// -// An ASN.1 SEQUENCE or SET can be written to a struct -// if each of the elements in the sequence can be -// written to the corresponding element in the struct. -// -// The following tags on struct fields have special meaning to Unmarshal: -// -// optional marks the field as ASN.1 OPTIONAL -// [explicit] tag:x specifies the ASN.1 tag number; implies ASN.1 CONTEXT SPECIFIC -// default:x sets the default value for optional integer fields -// -// If the type of the first field of a structure is RawContent then the raw -// ASN1 contents of the struct will be stored in it. -// -// Other ASN.1 types are not supported; if it encounters them, -// Unmarshal returns a parse error. -func Unmarshal(b []byte, val interface{}) (rest []byte, err os.Error) { - return UnmarshalWithParams(b, val, "") -} - -// UnmarshalWithParams allows field parameters to be specified for the -// top-level element. The form of the params is the same as the field tags. -func UnmarshalWithParams(b []byte, val interface{}, params string) (rest []byte, err os.Error) { - v := reflect.ValueOf(val).Elem() - offset, err := parseField(v, b, 0, parseFieldParameters(params)) - if err != nil { - return nil, err - } - return b[offset:], nil -} diff --git a/src/cmd/fix/testdata/reflect.datafmt.go.in b/src/cmd/fix/testdata/reflect.datafmt.go.in deleted file mode 100644 index 91f885f9a..000000000 --- a/src/cmd/fix/testdata/reflect.datafmt.go.in +++ /dev/null @@ -1,710 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* The datafmt package implements syntax-directed, type-driven formatting - of arbitrary data structures. Formatting a data structure consists of - two phases: first, a parser reads a format specification and builds a - "compiled" format. Then, the format can be applied repeatedly to - arbitrary values. Applying a format to a value evaluates to a []byte - containing the formatted value bytes, or nil. - - A format specification is a set of package declarations and format rules: - - Format = [ Entry { ";" Entry } [ ";" ] ] . - Entry = PackageDecl | FormatRule . - - (The syntax of a format specification is presented in the same EBNF - notation as used in the Go language specification. The syntax of white - space, comments, identifiers, and string literals is the same as in Go.) - - A package declaration binds a package name (such as 'ast') to a - package import path (such as '"go/ast"'). Each package used (in - a type name, see below) must be declared once before use. - - PackageDecl = PackageName ImportPath . - PackageName = identifier . - ImportPath = string . - - A format rule binds a rule name to a format expression. A rule name - may be a type name or one of the special names 'default' or '/'. - A type name may be the name of a predeclared type (for example, 'int', - 'float32', etc.), the package-qualified name of a user-defined type - (for example, 'ast.MapType'), or an identifier indicating the structure - of unnamed composite types ('array', 'chan', 'func', 'interface', 'map', - or 'ptr'). Each rule must have a unique name; rules can be declared in - any order. - - FormatRule = RuleName "=" Expression . - RuleName = TypeName | "default" | "/" . - TypeName = [ PackageName "." ] identifier . - - To format a value, the value's type name is used to select the format rule - (there is an override mechanism, see below). The format expression of the - selected rule specifies how the value is formatted. Each format expression, - when applied to a value, evaluates to a byte sequence or nil. - - In its most general form, a format expression is a list of alternatives, - each of which is a sequence of operands: - - Expression = [ Sequence ] { "|" [ Sequence ] } . - Sequence = Operand { Operand } . - - The formatted result produced by an expression is the result of the first - alternative sequence that evaluates to a non-nil result; if there is no - such alternative, the expression evaluates to nil. The result produced by - an operand sequence is the concatenation of the results of its operands. - If any operand in the sequence evaluates to nil, the entire sequence - evaluates to nil. - - There are five kinds of operands: - - Operand = Literal | Field | Group | Option | Repetition . - - Literals evaluate to themselves, with two substitutions. First, - %-formats expand in the manner of fmt.Printf, with the current value - passed as the parameter. Second, the current indentation (see below) - is inserted after every newline or form feed character. - - Literal = string . - - This table shows string literals applied to the value 42 and the - corresponding formatted result: - - "foo" foo - "%x" 2a - "x = %d" x = 42 - "%#x = %d" 0x2a = 42 - - A field operand is a field name optionally followed by an alternate - rule name. The field name may be an identifier or one of the special - names @ or *. - - Field = FieldName [ ":" RuleName ] . - FieldName = identifier | "@" | "*" . - - If the field name is an identifier, the current value must be a struct, - and there must be a field with that name in the struct. The same lookup - rules apply as in the Go language (for instance, the name of an anonymous - field is the unqualified type name). The field name denotes the field - value in the struct. If the field is not found, formatting is aborted - and an error message is returned. (TODO consider changing the semantics - such that if a field is not found, it evaluates to nil). - - The special name '@' denotes the current value. - - The meaning of the special name '*' depends on the type of the current - value: - - array, slice types array, slice element (inside {} only, see below) - interfaces value stored in interface - pointers value pointed to by pointer - - (Implementation restriction: channel, function and map types are not - supported due to missing reflection support). - - Fields are evaluated as follows: If the field value is nil, or an array - or slice element does not exist, the result is nil (see below for details - on array/slice elements). If the value is not nil the field value is - formatted (recursively) using the rule corresponding to its type name, - or the alternate rule name, if given. - - The following example shows a complete format specification for a - struct 'myPackage.Point'. Assume the package - - package myPackage // in directory myDir/myPackage - type Point struct { - name string; - x, y int; - } - - Applying the format specification - - myPackage "myDir/myPackage"; - int = "%d"; - hexInt = "0x%x"; - string = "---%s---"; - myPackage.Point = name "{" x ", " y:hexInt "}"; - - to the value myPackage.Point{"foo", 3, 15} results in - - ---foo---{3, 0xf} - - Finally, an operand may be a grouped, optional, or repeated expression. - A grouped expression ("group") groups a more complex expression (body) - so that it can be used in place of a single operand: - - Group = "(" [ Indentation ">>" ] Body ")" . - Indentation = Expression . - Body = Expression . - - A group body may be prefixed by an indentation expression followed by '>>'. - The indentation expression is applied to the current value like any other - expression and the result, if not nil, is appended to the current indentation - during the evaluation of the body (see also formatting state, below). - - An optional expression ("option") is enclosed in '[]' brackets. - - Option = "[" Body "]" . - - An option evaluates to its body, except that if the body evaluates to nil, - the option expression evaluates to an empty []byte. Thus an option's purpose - is to protect the expression containing the option from a nil operand. - - A repeated expression ("repetition") is enclosed in '{}' braces. - - Repetition = "{" Body [ "/" Separator ] "}" . - Separator = Expression . - - A repeated expression is evaluated as follows: The body is evaluated - repeatedly and its results are concatenated until the body evaluates - to nil. The result of the repetition is the (possibly empty) concatenation, - but it is never nil. An implicit index is supplied for the evaluation of - the body: that index is used to address elements of arrays or slices. If - the corresponding elements do not exist, the field denoting the element - evaluates to nil (which in turn may terminate the repetition). - - The body of a repetition may be followed by a '/' and a "separator" - expression. If the separator is present, it is invoked between repetitions - of the body. - - The following example shows a complete format specification for formatting - a slice of unnamed type. Applying the specification - - int = "%b"; - array = { * / ", " }; // array is the type name for an unnamed slice - - to the value '[]int{2, 3, 5, 7}' results in - - 10, 11, 101, 111 - - Default rule: If a format rule named 'default' is present, it is used for - formatting a value if no other rule was found. A common default rule is - - default = "%v" - - to provide default formatting for basic types without having to specify - a specific rule for each basic type. - - Global separator rule: If a format rule named '/' is present, it is - invoked with the current value between literals. If the separator - expression evaluates to nil, it is ignored. - - For instance, a global separator rule may be used to punctuate a sequence - of values with commas. The rules: - - default = "%v"; - / = ", "; - - will format an argument list by printing each one in its default format, - separated by a comma and a space. -*/ -package datafmt - -import ( - "bytes" - "fmt" - "go/token" - "io" - "os" - "reflect" - "runtime" -) - -// ---------------------------------------------------------------------------- -// Format representation - -// Custom formatters implement the Formatter function type. -// A formatter is invoked with the current formatting state, the -// value to format, and the rule name under which the formatter -// was installed (the same formatter function may be installed -// under different names). The formatter may access the current state -// to guide formatting and use State.Write to append to the state's -// output. -// -// A formatter must return a boolean value indicating if it evaluated -// to a non-nil value (true), or a nil value (false). -// -type Formatter func(state *State, value interface{}, ruleName string) bool - -// A FormatterMap is a set of custom formatters. -// It maps a rule name to a formatter function. -// -type FormatterMap map[string]Formatter - -// A parsed format expression is built from the following nodes. -// -type ( - expr interface{} - - alternatives []expr // x | y | z - - sequence []expr // x y z - - literal [][]byte // a list of string segments, possibly starting with '%' - - field struct { - fieldName string // including "@", "*" - ruleName string // "" if no rule name specified - } - - group struct { - indent, body expr // (indent >> body) - } - - option struct { - body expr // [body] - } - - repetition struct { - body, separator expr // {body / separator} - } - - custom struct { - ruleName string - fun Formatter - } -) - -// A Format is the result of parsing a format specification. -// The format may be applied repeatedly to format values. -// -type Format map[string]expr - -// ---------------------------------------------------------------------------- -// Formatting - -// An application-specific environment may be provided to Format.Apply; -// the environment is available inside custom formatters via State.Env(). -// Environments must implement copying; the Copy method must return an -// complete copy of the receiver. This is necessary so that the formatter -// can save and restore an environment (in case of an absent expression). -// -// If the Environment doesn't change during formatting (this is under -// control of the custom formatters), the Copy function can simply return -// the receiver, and thus can be very light-weight. -// -type Environment interface { - Copy() Environment -} - -// State represents the current formatting state. -// It is provided as argument to custom formatters. -// -type State struct { - fmt Format // format in use - env Environment // user-supplied environment - errors chan os.Error // not chan *Error (errors <- nil would be wrong!) - hasOutput bool // true after the first literal has been written - indent bytes.Buffer // current indentation - output bytes.Buffer // format output - linePos token.Position // position of line beginning (Column == 0) - default_ expr // possibly nil - separator expr // possibly nil -} - -func newState(fmt Format, env Environment, errors chan os.Error) *State { - s := new(State) - s.fmt = fmt - s.env = env - s.errors = errors - s.linePos = token.Position{Line: 1} - - // if we have a default rule, cache it's expression for fast access - if x, found := fmt["default"]; found { - s.default_ = x - } - - // if we have a global separator rule, cache it's expression for fast access - if x, found := fmt["/"]; found { - s.separator = x - } - - return s -} - -// Env returns the environment passed to Format.Apply. -func (s *State) Env() interface{} { return s.env } - -// LinePos returns the position of the current line beginning -// in the state's output buffer. Line numbers start at 1. -// -func (s *State) LinePos() token.Position { return s.linePos } - -// Pos returns the position of the next byte to be written to the -// output buffer. Line numbers start at 1. -// -func (s *State) Pos() token.Position { - offs := s.output.Len() - return token.Position{Line: s.linePos.Line, Column: offs - s.linePos.Offset, Offset: offs} -} - -// Write writes data to the output buffer, inserting the indentation -// string after each newline or form feed character. It cannot return an error. -// -func (s *State) Write(data []byte) (int, os.Error) { - n := 0 - i0 := 0 - for i, ch := range data { - if ch == '\n' || ch == '\f' { - // write text segment and indentation - n1, _ := s.output.Write(data[i0 : i+1]) - n2, _ := s.output.Write(s.indent.Bytes()) - n += n1 + n2 - i0 = i + 1 - s.linePos.Offset = s.output.Len() - s.linePos.Line++ - } - } - n3, _ := s.output.Write(data[i0:]) - return n + n3, nil -} - -type checkpoint struct { - env Environment - hasOutput bool - outputLen int - linePos token.Position -} - -func (s *State) save() checkpoint { - saved := checkpoint{nil, s.hasOutput, s.output.Len(), s.linePos} - if s.env != nil { - saved.env = s.env.Copy() - } - return saved -} - -func (s *State) restore(m checkpoint) { - s.env = m.env - s.output.Truncate(m.outputLen) -} - -func (s *State) error(msg string) { - s.errors <- os.NewError(msg) - runtime.Goexit() -} - -// TODO At the moment, unnamed types are simply mapped to the default -// names below. For instance, all unnamed arrays are mapped to -// 'array' which is not really sufficient. Eventually one may want -// to be able to specify rules for say an unnamed slice of T. -// - -func typename(typ reflect.Type) string { - switch typ.(type) { - case *reflect.ArrayType: - return "array" - case *reflect.SliceType: - return "array" - case *reflect.ChanType: - return "chan" - case *reflect.FuncType: - return "func" - case *reflect.InterfaceType: - return "interface" - case *reflect.MapType: - return "map" - case *reflect.PtrType: - return "ptr" - } - return typ.String() -} - -func (s *State) getFormat(name string) expr { - if fexpr, found := s.fmt[name]; found { - return fexpr - } - - if s.default_ != nil { - return s.default_ - } - - s.error(fmt.Sprintf("no format rule for type: '%s'", name)) - return nil -} - -// eval applies a format expression fexpr to a value. If the expression -// evaluates internally to a non-nil []byte, that slice is appended to -// the state's output buffer and eval returns true. Otherwise, eval -// returns false and the state remains unchanged. -// -func (s *State) eval(fexpr expr, value reflect.Value, index int) bool { - // an empty format expression always evaluates - // to a non-nil (but empty) []byte - if fexpr == nil { - return true - } - - switch t := fexpr.(type) { - case alternatives: - // append the result of the first alternative that evaluates to - // a non-nil []byte to the state's output - mark := s.save() - for _, x := range t { - if s.eval(x, value, index) { - return true - } - s.restore(mark) - } - return false - - case sequence: - // append the result of all operands to the state's output - // unless a nil result is encountered - mark := s.save() - for _, x := range t { - if !s.eval(x, value, index) { - s.restore(mark) - return false - } - } - return true - - case literal: - // write separator, if any - if s.hasOutput { - // not the first literal - if s.separator != nil { - sep := s.separator // save current separator - s.separator = nil // and disable it (avoid recursion) - mark := s.save() - if !s.eval(sep, value, index) { - s.restore(mark) - } - s.separator = sep // enable it again - } - } - s.hasOutput = true - // write literal segments - for _, lit := range t { - if len(lit) > 1 && lit[0] == '%' { - // segment contains a %-format at the beginning - if lit[1] == '%' { - // "%%" is printed as a single "%" - s.Write(lit[1:]) - } else { - // use s instead of s.output to get indentation right - fmt.Fprintf(s, string(lit), value.Interface()) - } - } else { - // segment contains no %-formats - s.Write(lit) - } - } - return true // a literal never evaluates to nil - - case *field: - // determine field value - switch t.fieldName { - case "@": - // field value is current value - - case "*": - // indirection: operation is type-specific - switch v := value.(type) { - case *reflect.ArrayValue: - if v.Len() <= index { - return false - } - value = v.Elem(index) - - case *reflect.SliceValue: - if v.IsNil() || v.Len() <= index { - return false - } - value = v.Elem(index) - - case *reflect.MapValue: - s.error("reflection support for maps incomplete") - - case *reflect.PtrValue: - if v.IsNil() { - return false - } - value = v.Elem() - - case *reflect.InterfaceValue: - if v.IsNil() { - return false - } - value = v.Elem() - - case *reflect.ChanValue: - s.error("reflection support for chans incomplete") - - case *reflect.FuncValue: - s.error("reflection support for funcs incomplete") - - default: - s.error(fmt.Sprintf("error: * does not apply to `%s`", value.Type())) - } - - default: - // value is value of named field - var field reflect.Value - if sval, ok := value.(*reflect.StructValue); ok { - field = sval.FieldByName(t.fieldName) - if field == nil { - // TODO consider just returning false in this case - s.error(fmt.Sprintf("error: no field `%s` in `%s`", t.fieldName, value.Type())) - } - } - value = field - } - - // determine rule - ruleName := t.ruleName - if ruleName == "" { - // no alternate rule name, value type determines rule - ruleName = typename(value.Type()) - } - fexpr = s.getFormat(ruleName) - - mark := s.save() - if !s.eval(fexpr, value, index) { - s.restore(mark) - return false - } - return true - - case *group: - // remember current indentation - indentLen := s.indent.Len() - - // update current indentation - mark := s.save() - s.eval(t.indent, value, index) - // if the indentation evaluates to nil, the state's output buffer - // didn't change - either way it's ok to append the difference to - // the current identation - s.indent.Write(s.output.Bytes()[mark.outputLen:s.output.Len()]) - s.restore(mark) - - // format group body - mark = s.save() - b := true - if !s.eval(t.body, value, index) { - s.restore(mark) - b = false - } - - // reset indentation - s.indent.Truncate(indentLen) - return b - - case *option: - // evaluate the body and append the result to the state's output - // buffer unless the result is nil - mark := s.save() - if !s.eval(t.body, value, 0) { // TODO is 0 index correct? - s.restore(mark) - } - return true // an option never evaluates to nil - - case *repetition: - // evaluate the body and append the result to the state's output - // buffer until a result is nil - for i := 0; ; i++ { - mark := s.save() - // write separator, if any - if i > 0 && t.separator != nil { - // nil result from separator is ignored - mark := s.save() - if !s.eval(t.separator, value, i) { - s.restore(mark) - } - } - if !s.eval(t.body, value, i) { - s.restore(mark) - break - } - } - return true // a repetition never evaluates to nil - - case *custom: - // invoke the custom formatter to obtain the result - mark := s.save() - if !t.fun(s, value.Interface(), t.ruleName) { - s.restore(mark) - return false - } - return true - } - - panic("unreachable") - return false -} - -// Eval formats each argument according to the format -// f and returns the resulting []byte and os.Error. If -// an error occurred, the []byte contains the partially -// formatted result. An environment env may be passed -// in which is available in custom formatters through -// the state parameter. -// -func (f Format) Eval(env Environment, args ...interface{}) ([]byte, os.Error) { - if f == nil { - return nil, os.NewError("format is nil") - } - - errors := make(chan os.Error) - s := newState(f, env, errors) - - go func() { - for _, v := range args { - fld := reflect.NewValue(v) - if fld == nil { - errors <- os.NewError("nil argument") - return - } - mark := s.save() - if !s.eval(s.getFormat(typename(fld.Type())), fld, 0) { // TODO is 0 index correct? - s.restore(mark) - } - } - errors <- nil // no errors - }() - - err := <-errors - return s.output.Bytes(), err -} - -// ---------------------------------------------------------------------------- -// Convenience functions - -// Fprint formats each argument according to the format f -// and writes to w. The result is the total number of bytes -// written and an os.Error, if any. -// -func (f Format) Fprint(w io.Writer, env Environment, args ...interface{}) (int, os.Error) { - data, err := f.Eval(env, args...) - if err != nil { - // TODO should we print partial result in case of error? - return 0, err - } - return w.Write(data) -} - -// Print formats each argument according to the format f -// and writes to standard output. The result is the total -// number of bytes written and an os.Error, if any. -// -func (f Format) Print(args ...interface{}) (int, os.Error) { - return f.Fprint(os.Stdout, nil, args...) -} - -// Sprint formats each argument according to the format f -// and returns the resulting string. If an error occurs -// during formatting, the result string contains the -// partially formatted result followed by an error message. -// -func (f Format) Sprint(args ...interface{}) string { - var buf bytes.Buffer - _, err := f.Fprint(&buf, nil, args...) - if err != nil { - var i interface{} = args - fmt.Fprintf(&buf, "--- Sprint(%s) failed: %v", fmt.Sprint(i), err) - } - return buf.String() -} diff --git a/src/cmd/fix/testdata/reflect.datafmt.go.out b/src/cmd/fix/testdata/reflect.datafmt.go.out deleted file mode 100644 index fd447588b..000000000 --- a/src/cmd/fix/testdata/reflect.datafmt.go.out +++ /dev/null @@ -1,710 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* The datafmt package implements syntax-directed, type-driven formatting - of arbitrary data structures. Formatting a data structure consists of - two phases: first, a parser reads a format specification and builds a - "compiled" format. Then, the format can be applied repeatedly to - arbitrary values. Applying a format to a value evaluates to a []byte - containing the formatted value bytes, or nil. - - A format specification is a set of package declarations and format rules: - - Format = [ Entry { ";" Entry } [ ";" ] ] . - Entry = PackageDecl | FormatRule . - - (The syntax of a format specification is presented in the same EBNF - notation as used in the Go language specification. The syntax of white - space, comments, identifiers, and string literals is the same as in Go.) - - A package declaration binds a package name (such as 'ast') to a - package import path (such as '"go/ast"'). Each package used (in - a type name, see below) must be declared once before use. - - PackageDecl = PackageName ImportPath . - PackageName = identifier . - ImportPath = string . - - A format rule binds a rule name to a format expression. A rule name - may be a type name or one of the special names 'default' or '/'. - A type name may be the name of a predeclared type (for example, 'int', - 'float32', etc.), the package-qualified name of a user-defined type - (for example, 'ast.MapType'), or an identifier indicating the structure - of unnamed composite types ('array', 'chan', 'func', 'interface', 'map', - or 'ptr'). Each rule must have a unique name; rules can be declared in - any order. - - FormatRule = RuleName "=" Expression . - RuleName = TypeName | "default" | "/" . - TypeName = [ PackageName "." ] identifier . - - To format a value, the value's type name is used to select the format rule - (there is an override mechanism, see below). The format expression of the - selected rule specifies how the value is formatted. Each format expression, - when applied to a value, evaluates to a byte sequence or nil. - - In its most general form, a format expression is a list of alternatives, - each of which is a sequence of operands: - - Expression = [ Sequence ] { "|" [ Sequence ] } . - Sequence = Operand { Operand } . - - The formatted result produced by an expression is the result of the first - alternative sequence that evaluates to a non-nil result; if there is no - such alternative, the expression evaluates to nil. The result produced by - an operand sequence is the concatenation of the results of its operands. - If any operand in the sequence evaluates to nil, the entire sequence - evaluates to nil. - - There are five kinds of operands: - - Operand = Literal | Field | Group | Option | Repetition . - - Literals evaluate to themselves, with two substitutions. First, - %-formats expand in the manner of fmt.Printf, with the current value - passed as the parameter. Second, the current indentation (see below) - is inserted after every newline or form feed character. - - Literal = string . - - This table shows string literals applied to the value 42 and the - corresponding formatted result: - - "foo" foo - "%x" 2a - "x = %d" x = 42 - "%#x = %d" 0x2a = 42 - - A field operand is a field name optionally followed by an alternate - rule name. The field name may be an identifier or one of the special - names @ or *. - - Field = FieldName [ ":" RuleName ] . - FieldName = identifier | "@" | "*" . - - If the field name is an identifier, the current value must be a struct, - and there must be a field with that name in the struct. The same lookup - rules apply as in the Go language (for instance, the name of an anonymous - field is the unqualified type name). The field name denotes the field - value in the struct. If the field is not found, formatting is aborted - and an error message is returned. (TODO consider changing the semantics - such that if a field is not found, it evaluates to nil). - - The special name '@' denotes the current value. - - The meaning of the special name '*' depends on the type of the current - value: - - array, slice types array, slice element (inside {} only, see below) - interfaces value stored in interface - pointers value pointed to by pointer - - (Implementation restriction: channel, function and map types are not - supported due to missing reflection support). - - Fields are evaluated as follows: If the field value is nil, or an array - or slice element does not exist, the result is nil (see below for details - on array/slice elements). If the value is not nil the field value is - formatted (recursively) using the rule corresponding to its type name, - or the alternate rule name, if given. - - The following example shows a complete format specification for a - struct 'myPackage.Point'. Assume the package - - package myPackage // in directory myDir/myPackage - type Point struct { - name string; - x, y int; - } - - Applying the format specification - - myPackage "myDir/myPackage"; - int = "%d"; - hexInt = "0x%x"; - string = "---%s---"; - myPackage.Point = name "{" x ", " y:hexInt "}"; - - to the value myPackage.Point{"foo", 3, 15} results in - - ---foo---{3, 0xf} - - Finally, an operand may be a grouped, optional, or repeated expression. - A grouped expression ("group") groups a more complex expression (body) - so that it can be used in place of a single operand: - - Group = "(" [ Indentation ">>" ] Body ")" . - Indentation = Expression . - Body = Expression . - - A group body may be prefixed by an indentation expression followed by '>>'. - The indentation expression is applied to the current value like any other - expression and the result, if not nil, is appended to the current indentation - during the evaluation of the body (see also formatting state, below). - - An optional expression ("option") is enclosed in '[]' brackets. - - Option = "[" Body "]" . - - An option evaluates to its body, except that if the body evaluates to nil, - the option expression evaluates to an empty []byte. Thus an option's purpose - is to protect the expression containing the option from a nil operand. - - A repeated expression ("repetition") is enclosed in '{}' braces. - - Repetition = "{" Body [ "/" Separator ] "}" . - Separator = Expression . - - A repeated expression is evaluated as follows: The body is evaluated - repeatedly and its results are concatenated until the body evaluates - to nil. The result of the repetition is the (possibly empty) concatenation, - but it is never nil. An implicit index is supplied for the evaluation of - the body: that index is used to address elements of arrays or slices. If - the corresponding elements do not exist, the field denoting the element - evaluates to nil (which in turn may terminate the repetition). - - The body of a repetition may be followed by a '/' and a "separator" - expression. If the separator is present, it is invoked between repetitions - of the body. - - The following example shows a complete format specification for formatting - a slice of unnamed type. Applying the specification - - int = "%b"; - array = { * / ", " }; // array is the type name for an unnamed slice - - to the value '[]int{2, 3, 5, 7}' results in - - 10, 11, 101, 111 - - Default rule: If a format rule named 'default' is present, it is used for - formatting a value if no other rule was found. A common default rule is - - default = "%v" - - to provide default formatting for basic types without having to specify - a specific rule for each basic type. - - Global separator rule: If a format rule named '/' is present, it is - invoked with the current value between literals. If the separator - expression evaluates to nil, it is ignored. - - For instance, a global separator rule may be used to punctuate a sequence - of values with commas. The rules: - - default = "%v"; - / = ", "; - - will format an argument list by printing each one in its default format, - separated by a comma and a space. -*/ -package datafmt - -import ( - "bytes" - "fmt" - "go/token" - "io" - "os" - "reflect" - "runtime" -) - -// ---------------------------------------------------------------------------- -// Format representation - -// Custom formatters implement the Formatter function type. -// A formatter is invoked with the current formatting state, the -// value to format, and the rule name under which the formatter -// was installed (the same formatter function may be installed -// under different names). The formatter may access the current state -// to guide formatting and use State.Write to append to the state's -// output. -// -// A formatter must return a boolean value indicating if it evaluated -// to a non-nil value (true), or a nil value (false). -// -type Formatter func(state *State, value interface{}, ruleName string) bool - -// A FormatterMap is a set of custom formatters. -// It maps a rule name to a formatter function. -// -type FormatterMap map[string]Formatter - -// A parsed format expression is built from the following nodes. -// -type ( - expr interface{} - - alternatives []expr // x | y | z - - sequence []expr // x y z - - literal [][]byte // a list of string segments, possibly starting with '%' - - field struct { - fieldName string // including "@", "*" - ruleName string // "" if no rule name specified - } - - group struct { - indent, body expr // (indent >> body) - } - - option struct { - body expr // [body] - } - - repetition struct { - body, separator expr // {body / separator} - } - - custom struct { - ruleName string - fun Formatter - } -) - -// A Format is the result of parsing a format specification. -// The format may be applied repeatedly to format values. -// -type Format map[string]expr - -// ---------------------------------------------------------------------------- -// Formatting - -// An application-specific environment may be provided to Format.Apply; -// the environment is available inside custom formatters via State.Env(). -// Environments must implement copying; the Copy method must return an -// complete copy of the receiver. This is necessary so that the formatter -// can save and restore an environment (in case of an absent expression). -// -// If the Environment doesn't change during formatting (this is under -// control of the custom formatters), the Copy function can simply return -// the receiver, and thus can be very light-weight. -// -type Environment interface { - Copy() Environment -} - -// State represents the current formatting state. -// It is provided as argument to custom formatters. -// -type State struct { - fmt Format // format in use - env Environment // user-supplied environment - errors chan os.Error // not chan *Error (errors <- nil would be wrong!) - hasOutput bool // true after the first literal has been written - indent bytes.Buffer // current indentation - output bytes.Buffer // format output - linePos token.Position // position of line beginning (Column == 0) - default_ expr // possibly nil - separator expr // possibly nil -} - -func newState(fmt Format, env Environment, errors chan os.Error) *State { - s := new(State) - s.fmt = fmt - s.env = env - s.errors = errors - s.linePos = token.Position{Line: 1} - - // if we have a default rule, cache it's expression for fast access - if x, found := fmt["default"]; found { - s.default_ = x - } - - // if we have a global separator rule, cache it's expression for fast access - if x, found := fmt["/"]; found { - s.separator = x - } - - return s -} - -// Env returns the environment passed to Format.Apply. -func (s *State) Env() interface{} { return s.env } - -// LinePos returns the position of the current line beginning -// in the state's output buffer. Line numbers start at 1. -// -func (s *State) LinePos() token.Position { return s.linePos } - -// Pos returns the position of the next byte to be written to the -// output buffer. Line numbers start at 1. -// -func (s *State) Pos() token.Position { - offs := s.output.Len() - return token.Position{Line: s.linePos.Line, Column: offs - s.linePos.Offset, Offset: offs} -} - -// Write writes data to the output buffer, inserting the indentation -// string after each newline or form feed character. It cannot return an error. -// -func (s *State) Write(data []byte) (int, os.Error) { - n := 0 - i0 := 0 - for i, ch := range data { - if ch == '\n' || ch == '\f' { - // write text segment and indentation - n1, _ := s.output.Write(data[i0 : i+1]) - n2, _ := s.output.Write(s.indent.Bytes()) - n += n1 + n2 - i0 = i + 1 - s.linePos.Offset = s.output.Len() - s.linePos.Line++ - } - } - n3, _ := s.output.Write(data[i0:]) - return n + n3, nil -} - -type checkpoint struct { - env Environment - hasOutput bool - outputLen int - linePos token.Position -} - -func (s *State) save() checkpoint { - saved := checkpoint{nil, s.hasOutput, s.output.Len(), s.linePos} - if s.env != nil { - saved.env = s.env.Copy() - } - return saved -} - -func (s *State) restore(m checkpoint) { - s.env = m.env - s.output.Truncate(m.outputLen) -} - -func (s *State) error(msg string) { - s.errors <- os.NewError(msg) - runtime.Goexit() -} - -// TODO At the moment, unnamed types are simply mapped to the default -// names below. For instance, all unnamed arrays are mapped to -// 'array' which is not really sufficient. Eventually one may want -// to be able to specify rules for say an unnamed slice of T. -// - -func typename(typ reflect.Type) string { - switch typ.Kind() { - case reflect.Array: - return "array" - case reflect.Slice: - return "array" - case reflect.Chan: - return "chan" - case reflect.Func: - return "func" - case reflect.Interface: - return "interface" - case reflect.Map: - return "map" - case reflect.Ptr: - return "ptr" - } - return typ.String() -} - -func (s *State) getFormat(name string) expr { - if fexpr, found := s.fmt[name]; found { - return fexpr - } - - if s.default_ != nil { - return s.default_ - } - - s.error(fmt.Sprintf("no format rule for type: '%s'", name)) - return nil -} - -// eval applies a format expression fexpr to a value. If the expression -// evaluates internally to a non-nil []byte, that slice is appended to -// the state's output buffer and eval returns true. Otherwise, eval -// returns false and the state remains unchanged. -// -func (s *State) eval(fexpr expr, value reflect.Value, index int) bool { - // an empty format expression always evaluates - // to a non-nil (but empty) []byte - if fexpr == nil { - return true - } - - switch t := fexpr.(type) { - case alternatives: - // append the result of the first alternative that evaluates to - // a non-nil []byte to the state's output - mark := s.save() - for _, x := range t { - if s.eval(x, value, index) { - return true - } - s.restore(mark) - } - return false - - case sequence: - // append the result of all operands to the state's output - // unless a nil result is encountered - mark := s.save() - for _, x := range t { - if !s.eval(x, value, index) { - s.restore(mark) - return false - } - } - return true - - case literal: - // write separator, if any - if s.hasOutput { - // not the first literal - if s.separator != nil { - sep := s.separator // save current separator - s.separator = nil // and disable it (avoid recursion) - mark := s.save() - if !s.eval(sep, value, index) { - s.restore(mark) - } - s.separator = sep // enable it again - } - } - s.hasOutput = true - // write literal segments - for _, lit := range t { - if len(lit) > 1 && lit[0] == '%' { - // segment contains a %-format at the beginning - if lit[1] == '%' { - // "%%" is printed as a single "%" - s.Write(lit[1:]) - } else { - // use s instead of s.output to get indentation right - fmt.Fprintf(s, string(lit), value.Interface()) - } - } else { - // segment contains no %-formats - s.Write(lit) - } - } - return true // a literal never evaluates to nil - - case *field: - // determine field value - switch t.fieldName { - case "@": - // field value is current value - - case "*": - // indirection: operation is type-specific - switch v := value; v.Kind() { - case reflect.Array: - if v.Len() <= index { - return false - } - value = v.Index(index) - - case reflect.Slice: - if v.IsNil() || v.Len() <= index { - return false - } - value = v.Index(index) - - case reflect.Map: - s.error("reflection support for maps incomplete") - - case reflect.Ptr: - if v.IsNil() { - return false - } - value = v.Elem() - - case reflect.Interface: - if v.IsNil() { - return false - } - value = v.Elem() - - case reflect.Chan: - s.error("reflection support for chans incomplete") - - case reflect.Func: - s.error("reflection support for funcs incomplete") - - default: - s.error(fmt.Sprintf("error: * does not apply to `%s`", value.Type())) - } - - default: - // value is value of named field - var field reflect.Value - if sval := value; sval.Kind() == reflect.Struct { - field = sval.FieldByName(t.fieldName) - if !field.IsValid() { - // TODO consider just returning false in this case - s.error(fmt.Sprintf("error: no field `%s` in `%s`", t.fieldName, value.Type())) - } - } - value = field - } - - // determine rule - ruleName := t.ruleName - if ruleName == "" { - // no alternate rule name, value type determines rule - ruleName = typename(value.Type()) - } - fexpr = s.getFormat(ruleName) - - mark := s.save() - if !s.eval(fexpr, value, index) { - s.restore(mark) - return false - } - return true - - case *group: - // remember current indentation - indentLen := s.indent.Len() - - // update current indentation - mark := s.save() - s.eval(t.indent, value, index) - // if the indentation evaluates to nil, the state's output buffer - // didn't change - either way it's ok to append the difference to - // the current identation - s.indent.Write(s.output.Bytes()[mark.outputLen:s.output.Len()]) - s.restore(mark) - - // format group body - mark = s.save() - b := true - if !s.eval(t.body, value, index) { - s.restore(mark) - b = false - } - - // reset indentation - s.indent.Truncate(indentLen) - return b - - case *option: - // evaluate the body and append the result to the state's output - // buffer unless the result is nil - mark := s.save() - if !s.eval(t.body, value, 0) { // TODO is 0 index correct? - s.restore(mark) - } - return true // an option never evaluates to nil - - case *repetition: - // evaluate the body and append the result to the state's output - // buffer until a result is nil - for i := 0; ; i++ { - mark := s.save() - // write separator, if any - if i > 0 && t.separator != nil { - // nil result from separator is ignored - mark := s.save() - if !s.eval(t.separator, value, i) { - s.restore(mark) - } - } - if !s.eval(t.body, value, i) { - s.restore(mark) - break - } - } - return true // a repetition never evaluates to nil - - case *custom: - // invoke the custom formatter to obtain the result - mark := s.save() - if !t.fun(s, value.Interface(), t.ruleName) { - s.restore(mark) - return false - } - return true - } - - panic("unreachable") - return false -} - -// Eval formats each argument according to the format -// f and returns the resulting []byte and os.Error. If -// an error occurred, the []byte contains the partially -// formatted result. An environment env may be passed -// in which is available in custom formatters through -// the state parameter. -// -func (f Format) Eval(env Environment, args ...interface{}) ([]byte, os.Error) { - if f == nil { - return nil, os.NewError("format is nil") - } - - errors := make(chan os.Error) - s := newState(f, env, errors) - - go func() { - for _, v := range args { - fld := reflect.ValueOf(v) - if !fld.IsValid() { - errors <- os.NewError("nil argument") - return - } - mark := s.save() - if !s.eval(s.getFormat(typename(fld.Type())), fld, 0) { // TODO is 0 index correct? - s.restore(mark) - } - } - errors <- nil // no errors - }() - - err := <-errors - return s.output.Bytes(), err -} - -// ---------------------------------------------------------------------------- -// Convenience functions - -// Fprint formats each argument according to the format f -// and writes to w. The result is the total number of bytes -// written and an os.Error, if any. -// -func (f Format) Fprint(w io.Writer, env Environment, args ...interface{}) (int, os.Error) { - data, err := f.Eval(env, args...) - if err != nil { - // TODO should we print partial result in case of error? - return 0, err - } - return w.Write(data) -} - -// Print formats each argument according to the format f -// and writes to standard output. The result is the total -// number of bytes written and an os.Error, if any. -// -func (f Format) Print(args ...interface{}) (int, os.Error) { - return f.Fprint(os.Stdout, nil, args...) -} - -// Sprint formats each argument according to the format f -// and returns the resulting string. If an error occurs -// during formatting, the result string contains the -// partially formatted result followed by an error message. -// -func (f Format) Sprint(args ...interface{}) string { - var buf bytes.Buffer - _, err := f.Fprint(&buf, nil, args...) - if err != nil { - var i interface{} = args - fmt.Fprintf(&buf, "--- Sprint(%s) failed: %v", fmt.Sprint(i), err) - } - return buf.String() -} diff --git a/src/cmd/fix/testdata/reflect.decode.go.in b/src/cmd/fix/testdata/reflect.decode.go.in deleted file mode 100644 index f831abee3..000000000 --- a/src/cmd/fix/testdata/reflect.decode.go.in +++ /dev/null @@ -1,905 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Represents JSON data structure using native Go types: booleans, floats, -// strings, arrays, and maps. - -package json - -import ( - "container/vector" - "encoding/base64" - "os" - "reflect" - "runtime" - "strconv" - "strings" - "unicode" - "utf16" - "utf8" -) - -// Unmarshal parses the JSON-encoded data and stores the result -// in the value pointed to by v. -// -// Unmarshal traverses the value v recursively. -// If an encountered value implements the Unmarshaler interface, -// Unmarshal calls its UnmarshalJSON method with a well-formed -// JSON encoding. -// -// Otherwise, Unmarshal uses the inverse of the encodings that -// Marshal uses, allocating maps, slices, and pointers as necessary, -// with the following additional rules: -// -// To unmarshal a JSON value into a nil interface value, the -// type stored in the interface value is one of: -// -// bool, for JSON booleans -// float64, for JSON numbers -// string, for JSON strings -// []interface{}, for JSON arrays -// map[string]interface{}, for JSON objects -// nil for JSON null -// -// If a JSON value is not appropriate for a given target type, -// or if a JSON number overflows the target type, Unmarshal -// skips that field and completes the unmarshalling as best it can. -// If no more serious errors are encountered, Unmarshal returns -// an UnmarshalTypeError describing the earliest such error. -// -func Unmarshal(data []byte, v interface{}) os.Error { - d := new(decodeState).init(data) - - // Quick check for well-formedness. - // Avoids filling out half a data structure - // before discovering a JSON syntax error. - err := checkValid(data, &d.scan) - if err != nil { - return err - } - - return d.unmarshal(v) -} - -// Unmarshaler is the interface implemented by objects -// that can unmarshal a JSON description of themselves. -// The input can be assumed to be a valid JSON object -// encoding. UnmarshalJSON must copy the JSON data -// if it wishes to retain the data after returning. -type Unmarshaler interface { - UnmarshalJSON([]byte) os.Error -} - -// An UnmarshalTypeError describes a JSON value that was -// not appropriate for a value of a specific Go type. -type UnmarshalTypeError struct { - Value string // description of JSON value - "bool", "array", "number -5" - Type reflect.Type // type of Go value it could not be assigned to -} - -func (e *UnmarshalTypeError) String() string { - return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String() -} - -// An UnmarshalFieldError describes a JSON object key that -// led to an unexported (and therefore unwritable) struct field. -type UnmarshalFieldError struct { - Key string - Type *reflect.StructType - Field reflect.StructField -} - -func (e *UnmarshalFieldError) String() string { - return "json: cannot unmarshal object key " + strconv.Quote(e.Key) + " into unexported field " + e.Field.Name + " of type " + e.Type.String() -} - -// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. -// (The argument to Unmarshal must be a non-nil pointer.) -type InvalidUnmarshalError struct { - Type reflect.Type -} - -func (e *InvalidUnmarshalError) String() string { - if e.Type == nil { - return "json: Unmarshal(nil)" - } - - if _, ok := e.Type.(*reflect.PtrType); !ok { - return "json: Unmarshal(non-pointer " + e.Type.String() + ")" - } - return "json: Unmarshal(nil " + e.Type.String() + ")" -} - -func (d *decodeState) unmarshal(v interface{}) (err os.Error) { - defer func() { - if r := recover(); r != nil { - if _, ok := r.(runtime.Error); ok { - panic(r) - } - err = r.(os.Error) - } - }() - - rv := reflect.NewValue(v) - pv, ok := rv.(*reflect.PtrValue) - if !ok || pv.IsNil() { - return &InvalidUnmarshalError{reflect.Typeof(v)} - } - - d.scan.reset() - // We decode rv not pv.Elem because the Unmarshaler interface - // test must be applied at the top level of the value. - d.value(rv) - return d.savedError -} - -// decodeState represents the state while decoding a JSON value. -type decodeState struct { - data []byte - off int // read offset in data - scan scanner - nextscan scanner // for calls to nextValue - savedError os.Error -} - -// errPhase is used for errors that should not happen unless -// there is a bug in the JSON decoder or something is editing -// the data slice while the decoder executes. -var errPhase = os.NewError("JSON decoder out of sync - data changing underfoot?") - -func (d *decodeState) init(data []byte) *decodeState { - d.data = data - d.off = 0 - d.savedError = nil - return d -} - -// error aborts the decoding by panicking with err. -func (d *decodeState) error(err os.Error) { - panic(err) -} - -// saveError saves the first err it is called with, -// for reporting at the end of the unmarshal. -func (d *decodeState) saveError(err os.Error) { - if d.savedError == nil { - d.savedError = err - } -} - -// next cuts off and returns the next full JSON value in d.data[d.off:]. -// The next value is known to be an object or array, not a literal. -func (d *decodeState) next() []byte { - c := d.data[d.off] - item, rest, err := nextValue(d.data[d.off:], &d.nextscan) - if err != nil { - d.error(err) - } - d.off = len(d.data) - len(rest) - - // Our scanner has seen the opening brace/bracket - // and thinks we're still in the middle of the object. - // invent a closing brace/bracket to get it out. - if c == '{' { - d.scan.step(&d.scan, '}') - } else { - d.scan.step(&d.scan, ']') - } - - return item -} - -// scanWhile processes bytes in d.data[d.off:] until it -// receives a scan code not equal to op. -// It updates d.off and returns the new scan code. -func (d *decodeState) scanWhile(op int) int { - var newOp int - for { - if d.off >= len(d.data) { - newOp = d.scan.eof() - d.off = len(d.data) + 1 // mark processed EOF with len+1 - } else { - c := int(d.data[d.off]) - d.off++ - newOp = d.scan.step(&d.scan, c) - } - if newOp != op { - break - } - } - return newOp -} - -// value decodes a JSON value from d.data[d.off:] into the value. -// it updates d.off to point past the decoded value. -func (d *decodeState) value(v reflect.Value) { - if v == nil { - _, rest, err := nextValue(d.data[d.off:], &d.nextscan) - if err != nil { - d.error(err) - } - d.off = len(d.data) - len(rest) - - // d.scan thinks we're still at the beginning of the item. - // Feed in an empty string - the shortest, simplest value - - // so that it knows we got to the end of the value. - if d.scan.step == stateRedo { - panic("redo") - } - d.scan.step(&d.scan, '"') - d.scan.step(&d.scan, '"') - return - } - - switch op := d.scanWhile(scanSkipSpace); op { - default: - d.error(errPhase) - - case scanBeginArray: - d.array(v) - - case scanBeginObject: - d.object(v) - - case scanBeginLiteral: - d.literal(v) - } -} - -// indirect walks down v allocating pointers as needed, -// until it gets to a non-pointer. -// if it encounters an Unmarshaler, indirect stops and returns that. -// if wantptr is true, indirect stops at the last pointer. -func (d *decodeState) indirect(v reflect.Value, wantptr bool) (Unmarshaler, reflect.Value) { - for { - var isUnmarshaler bool - if v.Type().NumMethod() > 0 { - // Remember that this is an unmarshaler, - // but wait to return it until after allocating - // the pointer (if necessary). - _, isUnmarshaler = v.Interface().(Unmarshaler) - } - - if iv, ok := v.(*reflect.InterfaceValue); ok && !iv.IsNil() { - v = iv.Elem() - continue - } - pv, ok := v.(*reflect.PtrValue) - if !ok { - break - } - _, isptrptr := pv.Elem().(*reflect.PtrValue) - if !isptrptr && wantptr && !isUnmarshaler { - return nil, pv - } - if pv.IsNil() { - pv.PointTo(reflect.MakeZero(pv.Type().(*reflect.PtrType).Elem())) - } - if isUnmarshaler { - // Using v.Interface().(Unmarshaler) - // here means that we have to use a pointer - // as the struct field. We cannot use a value inside - // a pointer to a struct, because in that case - // v.Interface() is the value (x.f) not the pointer (&x.f). - // This is an unfortunate consequence of reflect. - // An alternative would be to look up the - // UnmarshalJSON method and return a FuncValue. - return v.Interface().(Unmarshaler), nil - } - v = pv.Elem() - } - return nil, v -} - -// array consumes an array from d.data[d.off-1:], decoding into the value v. -// the first byte of the array ('[') has been read already. -func (d *decodeState) array(v reflect.Value) { - // Check for unmarshaler. - unmarshaler, pv := d.indirect(v, false) - if unmarshaler != nil { - d.off-- - err := unmarshaler.UnmarshalJSON(d.next()) - if err != nil { - d.error(err) - } - return - } - v = pv - - // Decoding into nil interface? Switch to non-reflect code. - iv, ok := v.(*reflect.InterfaceValue) - if ok { - iv.Set(reflect.NewValue(d.arrayInterface())) - return - } - - // Check type of target. - av, ok := v.(reflect.ArrayOrSliceValue) - if !ok { - d.saveError(&UnmarshalTypeError{"array", v.Type()}) - d.off-- - d.next() - return - } - - sv, _ := v.(*reflect.SliceValue) - - i := 0 - for { - // Look ahead for ] - can only happen on first iteration. - op := d.scanWhile(scanSkipSpace) - if op == scanEndArray { - break - } - - // Back up so d.value can have the byte we just read. - d.off-- - d.scan.undo(op) - - // Get element of array, growing if necessary. - if i >= av.Cap() && sv != nil { - newcap := sv.Cap() + sv.Cap()/2 - if newcap < 4 { - newcap = 4 - } - newv := reflect.MakeSlice(sv.Type().(*reflect.SliceType), sv.Len(), newcap) - reflect.Copy(newv, sv) - sv.Set(newv) - } - if i >= av.Len() && sv != nil { - // Must be slice; gave up on array during i >= av.Cap(). - sv.SetLen(i + 1) - } - - // Decode into element. - if i < av.Len() { - d.value(av.Elem(i)) - } else { - // Ran out of fixed array: skip. - d.value(nil) - } - i++ - - // Next token must be , or ]. - op = d.scanWhile(scanSkipSpace) - if op == scanEndArray { - break - } - if op != scanArrayValue { - d.error(errPhase) - } - } - if i < av.Len() { - if sv == nil { - // Array. Zero the rest. - z := reflect.MakeZero(av.Type().(*reflect.ArrayType).Elem()) - for ; i < av.Len(); i++ { - av.Elem(i).SetValue(z) - } - } else { - sv.SetLen(i) - } - } -} - -// matchName returns true if key should be written to a field named name. -func matchName(key, name string) bool { - return strings.ToLower(key) == strings.ToLower(name) -} - -// object consumes an object from d.data[d.off-1:], decoding into the value v. -// the first byte of the object ('{') has been read already. -func (d *decodeState) object(v reflect.Value) { - // Check for unmarshaler. - unmarshaler, pv := d.indirect(v, false) - if unmarshaler != nil { - d.off-- - err := unmarshaler.UnmarshalJSON(d.next()) - if err != nil { - d.error(err) - } - return - } - v = pv - - // Decoding into nil interface? Switch to non-reflect code. - iv, ok := v.(*reflect.InterfaceValue) - if ok { - iv.Set(reflect.NewValue(d.objectInterface())) - return - } - - // Check type of target: struct or map[string]T - var ( - mv *reflect.MapValue - sv *reflect.StructValue - ) - switch v := v.(type) { - case *reflect.MapValue: - // map must have string type - t := v.Type().(*reflect.MapType) - if t.Key() != reflect.Typeof("") { - d.saveError(&UnmarshalTypeError{"object", v.Type()}) - break - } - mv = v - if mv.IsNil() { - mv.SetValue(reflect.MakeMap(t)) - } - case *reflect.StructValue: - sv = v - default: - d.saveError(&UnmarshalTypeError{"object", v.Type()}) - } - - if mv == nil && sv == nil { - d.off-- - d.next() // skip over { } in input - return - } - - for { - // Read opening " of string key or closing }. - op := d.scanWhile(scanSkipSpace) - if op == scanEndObject { - // closing } - can only happen on first iteration. - break - } - if op != scanBeginLiteral { - d.error(errPhase) - } - - // Read string key. - start := d.off - 1 - op = d.scanWhile(scanContinue) - item := d.data[start : d.off-1] - key, ok := unquote(item) - if !ok { - d.error(errPhase) - } - - // Figure out field corresponding to key. - var subv reflect.Value - if mv != nil { - subv = reflect.MakeZero(mv.Type().(*reflect.MapType).Elem()) - } else { - var f reflect.StructField - var ok bool - st := sv.Type().(*reflect.StructType) - // First try for field with that tag. - if isValidTag(key) { - for i := 0; i < sv.NumField(); i++ { - f = st.Field(i) - if f.Tag == key { - ok = true - break - } - } - } - if !ok { - // Second, exact match. - f, ok = st.FieldByName(key) - } - if !ok { - // Third, case-insensitive match. - f, ok = st.FieldByNameFunc(func(s string) bool { return matchName(key, s) }) - } - - // Extract value; name must be exported. - if ok { - if f.PkgPath != "" { - d.saveError(&UnmarshalFieldError{key, st, f}) - } else { - subv = sv.FieldByIndex(f.Index) - } - } - } - - // Read : before value. - if op == scanSkipSpace { - op = d.scanWhile(scanSkipSpace) - } - if op != scanObjectKey { - d.error(errPhase) - } - - // Read value. - d.value(subv) - - // Write value back to map; - // if using struct, subv points into struct already. - if mv != nil { - mv.SetElem(reflect.NewValue(key), subv) - } - - // Next token must be , or }. - op = d.scanWhile(scanSkipSpace) - if op == scanEndObject { - break - } - if op != scanObjectValue { - d.error(errPhase) - } - } -} - -// literal consumes a literal from d.data[d.off-1:], decoding into the value v. -// The first byte of the literal has been read already -// (that's how the caller knows it's a literal). -func (d *decodeState) literal(v reflect.Value) { - // All bytes inside literal return scanContinue op code. - start := d.off - 1 - op := d.scanWhile(scanContinue) - - // Scan read one byte too far; back up. - d.off-- - d.scan.undo(op) - item := d.data[start:d.off] - - // Check for unmarshaler. - wantptr := item[0] == 'n' // null - unmarshaler, pv := d.indirect(v, wantptr) - if unmarshaler != nil { - err := unmarshaler.UnmarshalJSON(item) - if err != nil { - d.error(err) - } - return - } - v = pv - - switch c := item[0]; c { - case 'n': // null - switch v.(type) { - default: - d.saveError(&UnmarshalTypeError{"null", v.Type()}) - case *reflect.InterfaceValue, *reflect.PtrValue, *reflect.MapValue: - v.SetValue(nil) - } - - case 't', 'f': // true, false - value := c == 't' - switch v := v.(type) { - default: - d.saveError(&UnmarshalTypeError{"bool", v.Type()}) - case *reflect.BoolValue: - v.Set(value) - case *reflect.InterfaceValue: - v.Set(reflect.NewValue(value)) - } - - case '"': // string - s, ok := unquoteBytes(item) - if !ok { - d.error(errPhase) - } - switch v := v.(type) { - default: - d.saveError(&UnmarshalTypeError{"string", v.Type()}) - case *reflect.SliceValue: - if v.Type() != byteSliceType { - d.saveError(&UnmarshalTypeError{"string", v.Type()}) - break - } - b := make([]byte, base64.StdEncoding.DecodedLen(len(s))) - n, err := base64.StdEncoding.Decode(b, s) - if err != nil { - d.saveError(err) - break - } - v.Set(reflect.NewValue(b[0:n]).(*reflect.SliceValue)) - case *reflect.StringValue: - v.Set(string(s)) - case *reflect.InterfaceValue: - v.Set(reflect.NewValue(string(s))) - } - - default: // number - if c != '-' && (c < '0' || c > '9') { - d.error(errPhase) - } - s := string(item) - switch v := v.(type) { - default: - d.error(&UnmarshalTypeError{"number", v.Type()}) - case *reflect.InterfaceValue: - n, err := strconv.Atof64(s) - if err != nil { - d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) - break - } - v.Set(reflect.NewValue(n)) - - case *reflect.IntValue: - n, err := strconv.Atoi64(s) - if err != nil || v.Overflow(n) { - d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) - break - } - v.Set(n) - - case *reflect.UintValue: - n, err := strconv.Atoui64(s) - if err != nil || v.Overflow(n) { - d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) - break - } - v.Set(n) - - case *reflect.FloatValue: - n, err := strconv.AtofN(s, v.Type().Bits()) - if err != nil || v.Overflow(n) { - d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) - break - } - v.Set(n) - } - } -} - -// The xxxInterface routines build up a value to be stored -// in an empty interface. They are not strictly necessary, -// but they avoid the weight of reflection in this common case. - -// valueInterface is like value but returns interface{} -func (d *decodeState) valueInterface() interface{} { - switch d.scanWhile(scanSkipSpace) { - default: - d.error(errPhase) - case scanBeginArray: - return d.arrayInterface() - case scanBeginObject: - return d.objectInterface() - case scanBeginLiteral: - return d.literalInterface() - } - panic("unreachable") -} - -// arrayInterface is like array but returns []interface{}. -func (d *decodeState) arrayInterface() []interface{} { - var v vector.Vector - for { - // Look ahead for ] - can only happen on first iteration. - op := d.scanWhile(scanSkipSpace) - if op == scanEndArray { - break - } - - // Back up so d.value can have the byte we just read. - d.off-- - d.scan.undo(op) - - v.Push(d.valueInterface()) - - // Next token must be , or ]. - op = d.scanWhile(scanSkipSpace) - if op == scanEndArray { - break - } - if op != scanArrayValue { - d.error(errPhase) - } - } - return v -} - -// objectInterface is like object but returns map[string]interface{}. -func (d *decodeState) objectInterface() map[string]interface{} { - m := make(map[string]interface{}) - for { - // Read opening " of string key or closing }. - op := d.scanWhile(scanSkipSpace) - if op == scanEndObject { - // closing } - can only happen on first iteration. - break - } - if op != scanBeginLiteral { - d.error(errPhase) - } - - // Read string key. - start := d.off - 1 - op = d.scanWhile(scanContinue) - item := d.data[start : d.off-1] - key, ok := unquote(item) - if !ok { - d.error(errPhase) - } - - // Read : before value. - if op == scanSkipSpace { - op = d.scanWhile(scanSkipSpace) - } - if op != scanObjectKey { - d.error(errPhase) - } - - // Read value. - m[key] = d.valueInterface() - - // Next token must be , or }. - op = d.scanWhile(scanSkipSpace) - if op == scanEndObject { - break - } - if op != scanObjectValue { - d.error(errPhase) - } - } - return m -} - -// literalInterface is like literal but returns an interface value. -func (d *decodeState) literalInterface() interface{} { - // All bytes inside literal return scanContinue op code. - start := d.off - 1 - op := d.scanWhile(scanContinue) - - // Scan read one byte too far; back up. - d.off-- - d.scan.undo(op) - item := d.data[start:d.off] - - switch c := item[0]; c { - case 'n': // null - return nil - - case 't', 'f': // true, false - return c == 't' - - case '"': // string - s, ok := unquote(item) - if !ok { - d.error(errPhase) - } - return s - - default: // number - if c != '-' && (c < '0' || c > '9') { - d.error(errPhase) - } - n, err := strconv.Atof64(string(item)) - if err != nil { - d.saveError(&UnmarshalTypeError{"number " + string(item), reflect.Typeof(0.0)}) - } - return n - } - panic("unreachable") -} - -// getu4 decodes \uXXXX from the beginning of s, returning the hex value, -// or it returns -1. -func getu4(s []byte) int { - if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { - return -1 - } - rune, err := strconv.Btoui64(string(s[2:6]), 16) - if err != nil { - return -1 - } - return int(rune) -} - -// unquote converts a quoted JSON string literal s into an actual string t. -// The rules are different than for Go, so cannot use strconv.Unquote. -func unquote(s []byte) (t string, ok bool) { - s, ok = unquoteBytes(s) - t = string(s) - return -} - -func unquoteBytes(s []byte) (t []byte, ok bool) { - if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { - return - } - s = s[1 : len(s)-1] - - // Check for unusual characters. If there are none, - // then no unquoting is needed, so return a slice of the - // original bytes. - r := 0 - for r < len(s) { - c := s[r] - if c == '\\' || c == '"' || c < ' ' { - break - } - if c < utf8.RuneSelf { - r++ - continue - } - rune, size := utf8.DecodeRune(s[r:]) - if rune == utf8.RuneError && size == 1 { - break - } - r += size - } - if r == len(s) { - return s, true - } - - b := make([]byte, len(s)+2*utf8.UTFMax) - w := copy(b, s[0:r]) - for r < len(s) { - // Out of room? Can only happen if s is full of - // malformed UTF-8 and we're replacing each - // byte with RuneError. - if w >= len(b)-2*utf8.UTFMax { - nb := make([]byte, (len(b)+utf8.UTFMax)*2) - copy(nb, b[0:w]) - b = nb - } - switch c := s[r]; { - case c == '\\': - r++ - if r >= len(s) { - return - } - switch s[r] { - default: - return - case '"', '\\', '/', '\'': - b[w] = s[r] - r++ - w++ - case 'b': - b[w] = '\b' - r++ - w++ - case 'f': - b[w] = '\f' - r++ - w++ - case 'n': - b[w] = '\n' - r++ - w++ - case 'r': - b[w] = '\r' - r++ - w++ - case 't': - b[w] = '\t' - r++ - w++ - case 'u': - r-- - rune := getu4(s[r:]) - if rune < 0 { - return - } - r += 6 - if utf16.IsSurrogate(rune) { - rune1 := getu4(s[r:]) - if dec := utf16.DecodeRune(rune, rune1); dec != unicode.ReplacementChar { - // A valid pair; consume. - r += 6 - w += utf8.EncodeRune(b[w:], dec) - break - } - // Invalid surrogate; fall back to replacement rune. - rune = unicode.ReplacementChar - } - w += utf8.EncodeRune(b[w:], rune) - } - - // Quote, control characters are invalid. - case c == '"', c < ' ': - return - - // ASCII - case c < utf8.RuneSelf: - b[w] = c - r++ - w++ - - // Coerce to well-formed UTF-8. - default: - rune, size := utf8.DecodeRune(s[r:]) - r += size - w += utf8.EncodeRune(b[w:], rune) - } - } - return b[0:w], true -} diff --git a/src/cmd/fix/testdata/reflect.decode.go.out b/src/cmd/fix/testdata/reflect.decode.go.out deleted file mode 100644 index fb7910ee3..000000000 --- a/src/cmd/fix/testdata/reflect.decode.go.out +++ /dev/null @@ -1,908 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Represents JSON data structure using native Go types: booleans, floats, -// strings, arrays, and maps. - -package json - -import ( - "container/vector" - "encoding/base64" - "os" - "reflect" - "runtime" - "strconv" - "strings" - "unicode" - "utf16" - "utf8" -) - -// Unmarshal parses the JSON-encoded data and stores the result -// in the value pointed to by v. -// -// Unmarshal traverses the value v recursively. -// If an encountered value implements the Unmarshaler interface, -// Unmarshal calls its UnmarshalJSON method with a well-formed -// JSON encoding. -// -// Otherwise, Unmarshal uses the inverse of the encodings that -// Marshal uses, allocating maps, slices, and pointers as necessary, -// with the following additional rules: -// -// To unmarshal a JSON value into a nil interface value, the -// type stored in the interface value is one of: -// -// bool, for JSON booleans -// float64, for JSON numbers -// string, for JSON strings -// []interface{}, for JSON arrays -// map[string]interface{}, for JSON objects -// nil for JSON null -// -// If a JSON value is not appropriate for a given target type, -// or if a JSON number overflows the target type, Unmarshal -// skips that field and completes the unmarshalling as best it can. -// If no more serious errors are encountered, Unmarshal returns -// an UnmarshalTypeError describing the earliest such error. -// -func Unmarshal(data []byte, v interface{}) os.Error { - d := new(decodeState).init(data) - - // Quick check for well-formedness. - // Avoids filling out half a data structure - // before discovering a JSON syntax error. - err := checkValid(data, &d.scan) - if err != nil { - return err - } - - return d.unmarshal(v) -} - -// Unmarshaler is the interface implemented by objects -// that can unmarshal a JSON description of themselves. -// The input can be assumed to be a valid JSON object -// encoding. UnmarshalJSON must copy the JSON data -// if it wishes to retain the data after returning. -type Unmarshaler interface { - UnmarshalJSON([]byte) os.Error -} - -// An UnmarshalTypeError describes a JSON value that was -// not appropriate for a value of a specific Go type. -type UnmarshalTypeError struct { - Value string // description of JSON value - "bool", "array", "number -5" - Type reflect.Type // type of Go value it could not be assigned to -} - -func (e *UnmarshalTypeError) String() string { - return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String() -} - -// An UnmarshalFieldError describes a JSON object key that -// led to an unexported (and therefore unwritable) struct field. -type UnmarshalFieldError struct { - Key string - Type reflect.Type - Field reflect.StructField -} - -func (e *UnmarshalFieldError) String() string { - return "json: cannot unmarshal object key " + strconv.Quote(e.Key) + " into unexported field " + e.Field.Name + " of type " + e.Type.String() -} - -// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. -// (The argument to Unmarshal must be a non-nil pointer.) -type InvalidUnmarshalError struct { - Type reflect.Type -} - -func (e *InvalidUnmarshalError) String() string { - if e.Type == nil { - return "json: Unmarshal(nil)" - } - - if e.Type.Kind() != reflect.Ptr { - return "json: Unmarshal(non-pointer " + e.Type.String() + ")" - } - return "json: Unmarshal(nil " + e.Type.String() + ")" -} - -func (d *decodeState) unmarshal(v interface{}) (err os.Error) { - defer func() { - if r := recover(); r != nil { - if _, ok := r.(runtime.Error); ok { - panic(r) - } - err = r.(os.Error) - } - }() - - rv := reflect.ValueOf(v) - pv := rv - if pv.Kind() != reflect.Ptr || - pv.IsNil() { - return &InvalidUnmarshalError{reflect.TypeOf(v)} - } - - d.scan.reset() - // We decode rv not pv.Elem because the Unmarshaler interface - // test must be applied at the top level of the value. - d.value(rv) - return d.savedError -} - -// decodeState represents the state while decoding a JSON value. -type decodeState struct { - data []byte - off int // read offset in data - scan scanner - nextscan scanner // for calls to nextValue - savedError os.Error -} - -// errPhase is used for errors that should not happen unless -// there is a bug in the JSON decoder or something is editing -// the data slice while the decoder executes. -var errPhase = os.NewError("JSON decoder out of sync - data changing underfoot?") - -func (d *decodeState) init(data []byte) *decodeState { - d.data = data - d.off = 0 - d.savedError = nil - return d -} - -// error aborts the decoding by panicking with err. -func (d *decodeState) error(err os.Error) { - panic(err) -} - -// saveError saves the first err it is called with, -// for reporting at the end of the unmarshal. -func (d *decodeState) saveError(err os.Error) { - if d.savedError == nil { - d.savedError = err - } -} - -// next cuts off and returns the next full JSON value in d.data[d.off:]. -// The next value is known to be an object or array, not a literal. -func (d *decodeState) next() []byte { - c := d.data[d.off] - item, rest, err := nextValue(d.data[d.off:], &d.nextscan) - if err != nil { - d.error(err) - } - d.off = len(d.data) - len(rest) - - // Our scanner has seen the opening brace/bracket - // and thinks we're still in the middle of the object. - // invent a closing brace/bracket to get it out. - if c == '{' { - d.scan.step(&d.scan, '}') - } else { - d.scan.step(&d.scan, ']') - } - - return item -} - -// scanWhile processes bytes in d.data[d.off:] until it -// receives a scan code not equal to op. -// It updates d.off and returns the new scan code. -func (d *decodeState) scanWhile(op int) int { - var newOp int - for { - if d.off >= len(d.data) { - newOp = d.scan.eof() - d.off = len(d.data) + 1 // mark processed EOF with len+1 - } else { - c := int(d.data[d.off]) - d.off++ - newOp = d.scan.step(&d.scan, c) - } - if newOp != op { - break - } - } - return newOp -} - -// value decodes a JSON value from d.data[d.off:] into the value. -// it updates d.off to point past the decoded value. -func (d *decodeState) value(v reflect.Value) { - if !v.IsValid() { - _, rest, err := nextValue(d.data[d.off:], &d.nextscan) - if err != nil { - d.error(err) - } - d.off = len(d.data) - len(rest) - - // d.scan thinks we're still at the beginning of the item. - // Feed in an empty string - the shortest, simplest value - - // so that it knows we got to the end of the value. - if d.scan.step == stateRedo { - panic("redo") - } - d.scan.step(&d.scan, '"') - d.scan.step(&d.scan, '"') - return - } - - switch op := d.scanWhile(scanSkipSpace); op { - default: - d.error(errPhase) - - case scanBeginArray: - d.array(v) - - case scanBeginObject: - d.object(v) - - case scanBeginLiteral: - d.literal(v) - } -} - -// indirect walks down v allocating pointers as needed, -// until it gets to a non-pointer. -// if it encounters an Unmarshaler, indirect stops and returns that. -// if wantptr is true, indirect stops at the last pointer. -func (d *decodeState) indirect(v reflect.Value, wantptr bool) (Unmarshaler, reflect.Value) { - for { - var isUnmarshaler bool - if v.Type().NumMethod() > 0 { - // Remember that this is an unmarshaler, - // but wait to return it until after allocating - // the pointer (if necessary). - _, isUnmarshaler = v.Interface().(Unmarshaler) - } - - if iv := v; iv.Kind() == reflect.Interface && !iv.IsNil() { - v = iv.Elem() - continue - } - pv := v - if pv.Kind() != reflect.Ptr { - break - } - - if pv.Elem().Kind() != reflect.Ptr && - wantptr && !isUnmarshaler { - return nil, pv - } - if pv.IsNil() { - pv.Set(reflect.Zero(pv.Type().Elem()).Addr()) - } - if isUnmarshaler { - // Using v.Interface().(Unmarshaler) - // here means that we have to use a pointer - // as the struct field. We cannot use a value inside - // a pointer to a struct, because in that case - // v.Interface() is the value (x.f) not the pointer (&x.f). - // This is an unfortunate consequence of reflect. - // An alternative would be to look up the - // UnmarshalJSON method and return a FuncValue. - return v.Interface().(Unmarshaler), reflect.Value{} - } - v = pv.Elem() - } - return nil, v -} - -// array consumes an array from d.data[d.off-1:], decoding into the value v. -// the first byte of the array ('[') has been read already. -func (d *decodeState) array(v reflect.Value) { - // Check for unmarshaler. - unmarshaler, pv := d.indirect(v, false) - if unmarshaler != nil { - d.off-- - err := unmarshaler.UnmarshalJSON(d.next()) - if err != nil { - d.error(err) - } - return - } - v = pv - - // Decoding into nil interface? Switch to non-reflect code. - iv := v - ok := iv.Kind() == reflect.Interface - if ok { - iv.Set(reflect.ValueOf(d.arrayInterface())) - return - } - - // Check type of target. - av := v - if av.Kind() != reflect.Array && av.Kind() != reflect.Slice { - d.saveError(&UnmarshalTypeError{"array", v.Type()}) - d.off-- - d.next() - return - } - - sv := v - - i := 0 - for { - // Look ahead for ] - can only happen on first iteration. - op := d.scanWhile(scanSkipSpace) - if op == scanEndArray { - break - } - - // Back up so d.value can have the byte we just read. - d.off-- - d.scan.undo(op) - - // Get element of array, growing if necessary. - if i >= av.Cap() && sv.IsValid() { - newcap := sv.Cap() + sv.Cap()/2 - if newcap < 4 { - newcap = 4 - } - newv := reflect.MakeSlice(sv.Type(), sv.Len(), newcap) - reflect.Copy(newv, sv) - sv.Set(newv) - } - if i >= av.Len() && sv.IsValid() { - // Must be slice; gave up on array during i >= av.Cap(). - sv.SetLen(i + 1) - } - - // Decode into element. - if i < av.Len() { - d.value(av.Index(i)) - } else { - // Ran out of fixed array: skip. - d.value(reflect.Value{}) - } - i++ - - // Next token must be , or ]. - op = d.scanWhile(scanSkipSpace) - if op == scanEndArray { - break - } - if op != scanArrayValue { - d.error(errPhase) - } - } - if i < av.Len() { - if !sv.IsValid() { - // Array. Zero the rest. - z := reflect.Zero(av.Type().Elem()) - for ; i < av.Len(); i++ { - av.Index(i).Set(z) - } - } else { - sv.SetLen(i) - } - } -} - -// matchName returns true if key should be written to a field named name. -func matchName(key, name string) bool { - return strings.ToLower(key) == strings.ToLower(name) -} - -// object consumes an object from d.data[d.off-1:], decoding into the value v. -// the first byte of the object ('{') has been read already. -func (d *decodeState) object(v reflect.Value) { - // Check for unmarshaler. - unmarshaler, pv := d.indirect(v, false) - if unmarshaler != nil { - d.off-- - err := unmarshaler.UnmarshalJSON(d.next()) - if err != nil { - d.error(err) - } - return - } - v = pv - - // Decoding into nil interface? Switch to non-reflect code. - iv := v - if iv.Kind() == reflect.Interface { - iv.Set(reflect.ValueOf(d.objectInterface())) - return - } - - // Check type of target: struct or map[string]T - var ( - mv reflect.Value - sv reflect.Value - ) - switch v.Kind() { - case reflect.Map: - // map must have string type - t := v.Type() - if t.Key() != reflect.TypeOf("") { - d.saveError(&UnmarshalTypeError{"object", v.Type()}) - break - } - mv = v - if mv.IsNil() { - mv.Set(reflect.MakeMap(t)) - } - case reflect.Struct: - sv = v - default: - d.saveError(&UnmarshalTypeError{"object", v.Type()}) - } - - if !mv.IsValid() && !sv.IsValid() { - d.off-- - d.next() // skip over { } in input - return - } - - for { - // Read opening " of string key or closing }. - op := d.scanWhile(scanSkipSpace) - if op == scanEndObject { - // closing } - can only happen on first iteration. - break - } - if op != scanBeginLiteral { - d.error(errPhase) - } - - // Read string key. - start := d.off - 1 - op = d.scanWhile(scanContinue) - item := d.data[start : d.off-1] - key, ok := unquote(item) - if !ok { - d.error(errPhase) - } - - // Figure out field corresponding to key. - var subv reflect.Value - if mv.IsValid() { - subv = reflect.Zero(mv.Type().Elem()) - } else { - var f reflect.StructField - var ok bool - st := sv.Type() - // First try for field with that tag. - if isValidTag(key) { - for i := 0; i < sv.NumField(); i++ { - f = st.Field(i) - if f.Tag == key { - ok = true - break - } - } - } - if !ok { - // Second, exact match. - f, ok = st.FieldByName(key) - } - if !ok { - // Third, case-insensitive match. - f, ok = st.FieldByNameFunc(func(s string) bool { return matchName(key, s) }) - } - - // Extract value; name must be exported. - if ok { - if f.PkgPath != "" { - d.saveError(&UnmarshalFieldError{key, st, f}) - } else { - subv = sv.FieldByIndex(f.Index) - } - } - } - - // Read : before value. - if op == scanSkipSpace { - op = d.scanWhile(scanSkipSpace) - } - if op != scanObjectKey { - d.error(errPhase) - } - - // Read value. - d.value(subv) - - // Write value back to map; - // if using struct, subv points into struct already. - if mv.IsValid() { - mv.SetMapIndex(reflect.ValueOf(key), subv) - } - - // Next token must be , or }. - op = d.scanWhile(scanSkipSpace) - if op == scanEndObject { - break - } - if op != scanObjectValue { - d.error(errPhase) - } - } -} - -// literal consumes a literal from d.data[d.off-1:], decoding into the value v. -// The first byte of the literal has been read already -// (that's how the caller knows it's a literal). -func (d *decodeState) literal(v reflect.Value) { - // All bytes inside literal return scanContinue op code. - start := d.off - 1 - op := d.scanWhile(scanContinue) - - // Scan read one byte too far; back up. - d.off-- - d.scan.undo(op) - item := d.data[start:d.off] - - // Check for unmarshaler. - wantptr := item[0] == 'n' // null - unmarshaler, pv := d.indirect(v, wantptr) - if unmarshaler != nil { - err := unmarshaler.UnmarshalJSON(item) - if err != nil { - d.error(err) - } - return - } - v = pv - - switch c := item[0]; c { - case 'n': // null - switch v.Kind() { - default: - d.saveError(&UnmarshalTypeError{"null", v.Type()}) - case reflect.Interface, reflect.Ptr, reflect.Map: - v.Set(reflect.Zero(v.Type())) - } - - case 't', 'f': // true, false - value := c == 't' - switch v.Kind() { - default: - d.saveError(&UnmarshalTypeError{"bool", v.Type()}) - case reflect.Bool: - v.SetBool(value) - case reflect.Interface: - v.Set(reflect.ValueOf(value)) - } - - case '"': // string - s, ok := unquoteBytes(item) - if !ok { - d.error(errPhase) - } - switch v.Kind() { - default: - d.saveError(&UnmarshalTypeError{"string", v.Type()}) - case reflect.Slice: - if v.Type() != byteSliceType { - d.saveError(&UnmarshalTypeError{"string", v.Type()}) - break - } - b := make([]byte, base64.StdEncoding.DecodedLen(len(s))) - n, err := base64.StdEncoding.Decode(b, s) - if err != nil { - d.saveError(err) - break - } - v.Set(reflect.ValueOf(b[0:n])) - case reflect.String: - v.SetString(string(s)) - case reflect.Interface: - v.Set(reflect.ValueOf(string(s))) - } - - default: // number - if c != '-' && (c < '0' || c > '9') { - d.error(errPhase) - } - s := string(item) - switch v.Kind() { - default: - d.error(&UnmarshalTypeError{"number", v.Type()}) - case reflect.Interface: - n, err := strconv.Atof64(s) - if err != nil { - d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) - break - } - v.Set(reflect.ValueOf(n)) - - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - n, err := strconv.Atoi64(s) - if err != nil || v.OverflowInt(n) { - d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) - break - } - v.SetInt(n) - - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - n, err := strconv.Atoui64(s) - if err != nil || v.OverflowUint(n) { - d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) - break - } - v.SetUint(n) - - case reflect.Float32, reflect.Float64: - n, err := strconv.AtofN(s, v.Type().Bits()) - if err != nil || v.OverflowFloat(n) { - d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) - break - } - v.SetFloat(n) - } - } -} - -// The xxxInterface routines build up a value to be stored -// in an empty interface. They are not strictly necessary, -// but they avoid the weight of reflection in this common case. - -// valueInterface is like value but returns interface{} -func (d *decodeState) valueInterface() interface{} { - switch d.scanWhile(scanSkipSpace) { - default: - d.error(errPhase) - case scanBeginArray: - return d.arrayInterface() - case scanBeginObject: - return d.objectInterface() - case scanBeginLiteral: - return d.literalInterface() - } - panic("unreachable") -} - -// arrayInterface is like array but returns []interface{}. -func (d *decodeState) arrayInterface() []interface{} { - var v vector.Vector - for { - // Look ahead for ] - can only happen on first iteration. - op := d.scanWhile(scanSkipSpace) - if op == scanEndArray { - break - } - - // Back up so d.value can have the byte we just read. - d.off-- - d.scan.undo(op) - - v.Push(d.valueInterface()) - - // Next token must be , or ]. - op = d.scanWhile(scanSkipSpace) - if op == scanEndArray { - break - } - if op != scanArrayValue { - d.error(errPhase) - } - } - return v -} - -// objectInterface is like object but returns map[string]interface{}. -func (d *decodeState) objectInterface() map[string]interface{} { - m := make(map[string]interface{}) - for { - // Read opening " of string key or closing }. - op := d.scanWhile(scanSkipSpace) - if op == scanEndObject { - // closing } - can only happen on first iteration. - break - } - if op != scanBeginLiteral { - d.error(errPhase) - } - - // Read string key. - start := d.off - 1 - op = d.scanWhile(scanContinue) - item := d.data[start : d.off-1] - key, ok := unquote(item) - if !ok { - d.error(errPhase) - } - - // Read : before value. - if op == scanSkipSpace { - op = d.scanWhile(scanSkipSpace) - } - if op != scanObjectKey { - d.error(errPhase) - } - - // Read value. - m[key] = d.valueInterface() - - // Next token must be , or }. - op = d.scanWhile(scanSkipSpace) - if op == scanEndObject { - break - } - if op != scanObjectValue { - d.error(errPhase) - } - } - return m -} - -// literalInterface is like literal but returns an interface value. -func (d *decodeState) literalInterface() interface{} { - // All bytes inside literal return scanContinue op code. - start := d.off - 1 - op := d.scanWhile(scanContinue) - - // Scan read one byte too far; back up. - d.off-- - d.scan.undo(op) - item := d.data[start:d.off] - - switch c := item[0]; c { - case 'n': // null - return nil - - case 't', 'f': // true, false - return c == 't' - - case '"': // string - s, ok := unquote(item) - if !ok { - d.error(errPhase) - } - return s - - default: // number - if c != '-' && (c < '0' || c > '9') { - d.error(errPhase) - } - n, err := strconv.Atof64(string(item)) - if err != nil { - d.saveError(&UnmarshalTypeError{"number " + string(item), reflect.TypeOf(0.0)}) - } - return n - } - panic("unreachable") -} - -// getu4 decodes \uXXXX from the beginning of s, returning the hex value, -// or it returns -1. -func getu4(s []byte) int { - if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { - return -1 - } - rune, err := strconv.Btoui64(string(s[2:6]), 16) - if err != nil { - return -1 - } - return int(rune) -} - -// unquote converts a quoted JSON string literal s into an actual string t. -// The rules are different than for Go, so cannot use strconv.Unquote. -func unquote(s []byte) (t string, ok bool) { - s, ok = unquoteBytes(s) - t = string(s) - return -} - -func unquoteBytes(s []byte) (t []byte, ok bool) { - if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { - return - } - s = s[1 : len(s)-1] - - // Check for unusual characters. If there are none, - // then no unquoting is needed, so return a slice of the - // original bytes. - r := 0 - for r < len(s) { - c := s[r] - if c == '\\' || c == '"' || c < ' ' { - break - } - if c < utf8.RuneSelf { - r++ - continue - } - rune, size := utf8.DecodeRune(s[r:]) - if rune == utf8.RuneError && size == 1 { - break - } - r += size - } - if r == len(s) { - return s, true - } - - b := make([]byte, len(s)+2*utf8.UTFMax) - w := copy(b, s[0:r]) - for r < len(s) { - // Out of room? Can only happen if s is full of - // malformed UTF-8 and we're replacing each - // byte with RuneError. - if w >= len(b)-2*utf8.UTFMax { - nb := make([]byte, (len(b)+utf8.UTFMax)*2) - copy(nb, b[0:w]) - b = nb - } - switch c := s[r]; { - case c == '\\': - r++ - if r >= len(s) { - return - } - switch s[r] { - default: - return - case '"', '\\', '/', '\'': - b[w] = s[r] - r++ - w++ - case 'b': - b[w] = '\b' - r++ - w++ - case 'f': - b[w] = '\f' - r++ - w++ - case 'n': - b[w] = '\n' - r++ - w++ - case 'r': - b[w] = '\r' - r++ - w++ - case 't': - b[w] = '\t' - r++ - w++ - case 'u': - r-- - rune := getu4(s[r:]) - if rune < 0 { - return - } - r += 6 - if utf16.IsSurrogate(rune) { - rune1 := getu4(s[r:]) - if dec := utf16.DecodeRune(rune, rune1); dec != unicode.ReplacementChar { - // A valid pair; consume. - r += 6 - w += utf8.EncodeRune(b[w:], dec) - break - } - // Invalid surrogate; fall back to replacement rune. - rune = unicode.ReplacementChar - } - w += utf8.EncodeRune(b[w:], rune) - } - - // Quote, control characters are invalid. - case c == '"', c < ' ': - return - - // ASCII - case c < utf8.RuneSelf: - b[w] = c - r++ - w++ - - // Coerce to well-formed UTF-8. - default: - rune, size := utf8.DecodeRune(s[r:]) - r += size - w += utf8.EncodeRune(b[w:], rune) - } - } - return b[0:w], true -} diff --git a/src/cmd/fix/testdata/reflect.decoder.go.in b/src/cmd/fix/testdata/reflect.decoder.go.in deleted file mode 100644 index 0ce9b06fd..000000000 --- a/src/cmd/fix/testdata/reflect.decoder.go.in +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package gob - -import ( - "bufio" - "bytes" - "io" - "os" - "reflect" - "sync" -) - -// A Decoder manages the receipt of type and data information read from the -// remote side of a connection. -type Decoder struct { - mutex sync.Mutex // each item must be received atomically - r io.Reader // source of the data - buf bytes.Buffer // buffer for more efficient i/o from r - wireType map[typeId]*wireType // map from remote ID to local description - decoderCache map[reflect.Type]map[typeId]**decEngine // cache of compiled engines - ignorerCache map[typeId]**decEngine // ditto for ignored objects - freeList *decoderState // list of free decoderStates; avoids reallocation - countBuf []byte // used for decoding integers while parsing messages - tmp []byte // temporary storage for i/o; saves reallocating - err os.Error -} - -// NewDecoder returns a new decoder that reads from the io.Reader. -func NewDecoder(r io.Reader) *Decoder { - dec := new(Decoder) - dec.r = bufio.NewReader(r) - dec.wireType = make(map[typeId]*wireType) - dec.decoderCache = make(map[reflect.Type]map[typeId]**decEngine) - dec.ignorerCache = make(map[typeId]**decEngine) - dec.countBuf = make([]byte, 9) // counts may be uint64s (unlikely!), require 9 bytes - - return dec -} - -// recvType loads the definition of a type. -func (dec *Decoder) recvType(id typeId) { - // Have we already seen this type? That's an error - if id < firstUserId || dec.wireType[id] != nil { - dec.err = os.NewError("gob: duplicate type received") - return - } - - // Type: - wire := new(wireType) - dec.decodeValue(tWireType, reflect.NewValue(wire)) - if dec.err != nil { - return - } - // Remember we've seen this type. - dec.wireType[id] = wire -} - -// recvMessage reads the next count-delimited item from the input. It is the converse -// of Encoder.writeMessage. It returns false on EOF or other error reading the message. -func (dec *Decoder) recvMessage() bool { - // Read a count. - nbytes, _, err := decodeUintReader(dec.r, dec.countBuf) - if err != nil { - dec.err = err - return false - } - dec.readMessage(int(nbytes)) - return dec.err == nil -} - -// readMessage reads the next nbytes bytes from the input. -func (dec *Decoder) readMessage(nbytes int) { - // Allocate the buffer. - if cap(dec.tmp) < nbytes { - dec.tmp = make([]byte, nbytes+100) // room to grow - } - dec.tmp = dec.tmp[:nbytes] - - // Read the data - _, dec.err = io.ReadFull(dec.r, dec.tmp) - if dec.err != nil { - if dec.err == os.EOF { - dec.err = io.ErrUnexpectedEOF - } - return - } - dec.buf.Write(dec.tmp) -} - -// toInt turns an encoded uint64 into an int, according to the marshaling rules. -func toInt(x uint64) int64 { - i := int64(x >> 1) - if x&1 != 0 { - i = ^i - } - return i -} - -func (dec *Decoder) nextInt() int64 { - n, _, err := decodeUintReader(&dec.buf, dec.countBuf) - if err != nil { - dec.err = err - } - return toInt(n) -} - -func (dec *Decoder) nextUint() uint64 { - n, _, err := decodeUintReader(&dec.buf, dec.countBuf) - if err != nil { - dec.err = err - } - return n -} - -// decodeTypeSequence parses: -// TypeSequence -// (TypeDefinition DelimitedTypeDefinition*)? -// and returns the type id of the next value. It returns -1 at -// EOF. Upon return, the remainder of dec.buf is the value to be -// decoded. If this is an interface value, it can be ignored by -// simply resetting that buffer. -func (dec *Decoder) decodeTypeSequence(isInterface bool) typeId { - for dec.err == nil { - if dec.buf.Len() == 0 { - if !dec.recvMessage() { - break - } - } - // Receive a type id. - id := typeId(dec.nextInt()) - if id >= 0 { - // Value follows. - return id - } - // Type definition for (-id) follows. - dec.recvType(-id) - // When decoding an interface, after a type there may be a - // DelimitedValue still in the buffer. Skip its count. - // (Alternatively, the buffer is empty and the byte count - // will be absorbed by recvMessage.) - if dec.buf.Len() > 0 { - if !isInterface { - dec.err = os.NewError("extra data in buffer") - break - } - dec.nextUint() - } - } - return -1 -} - -// Decode reads the next value from the connection and stores -// it in the data represented by the empty interface value. -// If e is nil, the value will be discarded. Otherwise, -// the value underlying e must either be the correct type for the next -// data item received, and must be a pointer. -func (dec *Decoder) Decode(e interface{}) os.Error { - if e == nil { - return dec.DecodeValue(nil) - } - value := reflect.NewValue(e) - // If e represents a value as opposed to a pointer, the answer won't - // get back to the caller. Make sure it's a pointer. - if value.Type().Kind() != reflect.Ptr { - dec.err = os.NewError("gob: attempt to decode into a non-pointer") - return dec.err - } - return dec.DecodeValue(value) -} - -// DecodeValue reads the next value from the connection and stores -// it in the data represented by the reflection value. -// The value must be the correct type for the next -// data item received, or it may be nil, which means the -// value will be discarded. -func (dec *Decoder) DecodeValue(value reflect.Value) os.Error { - // Make sure we're single-threaded through here. - dec.mutex.Lock() - defer dec.mutex.Unlock() - - dec.buf.Reset() // In case data lingers from previous invocation. - dec.err = nil - id := dec.decodeTypeSequence(false) - if dec.err == nil { - dec.decodeValue(id, value) - } - return dec.err -} - -// If debug.go is compiled into the program , debugFunc prints a human-readable -// representation of the gob data read from r by calling that file's Debug function. -// Otherwise it is nil. -var debugFunc func(io.Reader) diff --git a/src/cmd/fix/testdata/reflect.decoder.go.out b/src/cmd/fix/testdata/reflect.decoder.go.out deleted file mode 100644 index ece88ecbe..000000000 --- a/src/cmd/fix/testdata/reflect.decoder.go.out +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package gob - -import ( - "bufio" - "bytes" - "io" - "os" - "reflect" - "sync" -) - -// A Decoder manages the receipt of type and data information read from the -// remote side of a connection. -type Decoder struct { - mutex sync.Mutex // each item must be received atomically - r io.Reader // source of the data - buf bytes.Buffer // buffer for more efficient i/o from r - wireType map[typeId]*wireType // map from remote ID to local description - decoderCache map[reflect.Type]map[typeId]**decEngine // cache of compiled engines - ignorerCache map[typeId]**decEngine // ditto for ignored objects - freeList *decoderState // list of free decoderStates; avoids reallocation - countBuf []byte // used for decoding integers while parsing messages - tmp []byte // temporary storage for i/o; saves reallocating - err os.Error -} - -// NewDecoder returns a new decoder that reads from the io.Reader. -func NewDecoder(r io.Reader) *Decoder { - dec := new(Decoder) - dec.r = bufio.NewReader(r) - dec.wireType = make(map[typeId]*wireType) - dec.decoderCache = make(map[reflect.Type]map[typeId]**decEngine) - dec.ignorerCache = make(map[typeId]**decEngine) - dec.countBuf = make([]byte, 9) // counts may be uint64s (unlikely!), require 9 bytes - - return dec -} - -// recvType loads the definition of a type. -func (dec *Decoder) recvType(id typeId) { - // Have we already seen this type? That's an error - if id < firstUserId || dec.wireType[id] != nil { - dec.err = os.NewError("gob: duplicate type received") - return - } - - // Type: - wire := new(wireType) - dec.decodeValue(tWireType, reflect.ValueOf(wire)) - if dec.err != nil { - return - } - // Remember we've seen this type. - dec.wireType[id] = wire -} - -// recvMessage reads the next count-delimited item from the input. It is the converse -// of Encoder.writeMessage. It returns false on EOF or other error reading the message. -func (dec *Decoder) recvMessage() bool { - // Read a count. - nbytes, _, err := decodeUintReader(dec.r, dec.countBuf) - if err != nil { - dec.err = err - return false - } - dec.readMessage(int(nbytes)) - return dec.err == nil -} - -// readMessage reads the next nbytes bytes from the input. -func (dec *Decoder) readMessage(nbytes int) { - // Allocate the buffer. - if cap(dec.tmp) < nbytes { - dec.tmp = make([]byte, nbytes+100) // room to grow - } - dec.tmp = dec.tmp[:nbytes] - - // Read the data - _, dec.err = io.ReadFull(dec.r, dec.tmp) - if dec.err != nil { - if dec.err == os.EOF { - dec.err = io.ErrUnexpectedEOF - } - return - } - dec.buf.Write(dec.tmp) -} - -// toInt turns an encoded uint64 into an int, according to the marshaling rules. -func toInt(x uint64) int64 { - i := int64(x >> 1) - if x&1 != 0 { - i = ^i - } - return i -} - -func (dec *Decoder) nextInt() int64 { - n, _, err := decodeUintReader(&dec.buf, dec.countBuf) - if err != nil { - dec.err = err - } - return toInt(n) -} - -func (dec *Decoder) nextUint() uint64 { - n, _, err := decodeUintReader(&dec.buf, dec.countBuf) - if err != nil { - dec.err = err - } - return n -} - -// decodeTypeSequence parses: -// TypeSequence -// (TypeDefinition DelimitedTypeDefinition*)? -// and returns the type id of the next value. It returns -1 at -// EOF. Upon return, the remainder of dec.buf is the value to be -// decoded. If this is an interface value, it can be ignored by -// simply resetting that buffer. -func (dec *Decoder) decodeTypeSequence(isInterface bool) typeId { - for dec.err == nil { - if dec.buf.Len() == 0 { - if !dec.recvMessage() { - break - } - } - // Receive a type id. - id := typeId(dec.nextInt()) - if id >= 0 { - // Value follows. - return id - } - // Type definition for (-id) follows. - dec.recvType(-id) - // When decoding an interface, after a type there may be a - // DelimitedValue still in the buffer. Skip its count. - // (Alternatively, the buffer is empty and the byte count - // will be absorbed by recvMessage.) - if dec.buf.Len() > 0 { - if !isInterface { - dec.err = os.NewError("extra data in buffer") - break - } - dec.nextUint() - } - } - return -1 -} - -// Decode reads the next value from the connection and stores -// it in the data represented by the empty interface value. -// If e is nil, the value will be discarded. Otherwise, -// the value underlying e must either be the correct type for the next -// data item received, and must be a pointer. -func (dec *Decoder) Decode(e interface{}) os.Error { - if e == nil { - return dec.DecodeValue(reflect.Value{}) - } - value := reflect.ValueOf(e) - // If e represents a value as opposed to a pointer, the answer won't - // get back to the caller. Make sure it's a pointer. - if value.Type().Kind() != reflect.Ptr { - dec.err = os.NewError("gob: attempt to decode into a non-pointer") - return dec.err - } - return dec.DecodeValue(value) -} - -// DecodeValue reads the next value from the connection and stores -// it in the data represented by the reflection value. -// The value must be the correct type for the next -// data item received, or it may be nil, which means the -// value will be discarded. -func (dec *Decoder) DecodeValue(value reflect.Value) os.Error { - // Make sure we're single-threaded through here. - dec.mutex.Lock() - defer dec.mutex.Unlock() - - dec.buf.Reset() // In case data lingers from previous invocation. - dec.err = nil - id := dec.decodeTypeSequence(false) - if dec.err == nil { - dec.decodeValue(id, value) - } - return dec.err -} - -// If debug.go is compiled into the program , debugFunc prints a human-readable -// representation of the gob data read from r by calling that file's Debug function. -// Otherwise it is nil. -var debugFunc func(io.Reader) diff --git a/src/cmd/fix/testdata/reflect.dnsmsg.go.in b/src/cmd/fix/testdata/reflect.dnsmsg.go.in deleted file mode 100644 index 3d9c312f2..000000000 --- a/src/cmd/fix/testdata/reflect.dnsmsg.go.in +++ /dev/null @@ -1,777 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// DNS packet assembly. See RFC 1035. -// -// This is intended to support name resolution during net.Dial. -// It doesn't have to be blazing fast. -// -// Rather than write the usual handful of routines to pack and -// unpack every message that can appear on the wire, we use -// reflection to write a generic pack/unpack for structs and then -// use it. Thus, if in the future we need to define new message -// structs, no new pack/unpack/printing code needs to be written. -// -// The first half of this file defines the DNS message formats. -// The second half implements the conversion to and from wire format. -// A few of the structure elements have string tags to aid the -// generic pack/unpack routines. -// -// TODO(rsc): There are enough names defined in this file that they're all -// prefixed with dns. Perhaps put this in its own package later. - -package net - -import ( - "fmt" - "os" - "reflect" -) - -// Packet formats - -// Wire constants. -const ( - // valid dnsRR_Header.Rrtype and dnsQuestion.qtype - dnsTypeA = 1 - dnsTypeNS = 2 - dnsTypeMD = 3 - dnsTypeMF = 4 - dnsTypeCNAME = 5 - dnsTypeSOA = 6 - dnsTypeMB = 7 - dnsTypeMG = 8 - dnsTypeMR = 9 - dnsTypeNULL = 10 - dnsTypeWKS = 11 - dnsTypePTR = 12 - dnsTypeHINFO = 13 - dnsTypeMINFO = 14 - dnsTypeMX = 15 - dnsTypeTXT = 16 - dnsTypeAAAA = 28 - dnsTypeSRV = 33 - - // valid dnsQuestion.qtype only - dnsTypeAXFR = 252 - dnsTypeMAILB = 253 - dnsTypeMAILA = 254 - dnsTypeALL = 255 - - // valid dnsQuestion.qclass - dnsClassINET = 1 - dnsClassCSNET = 2 - dnsClassCHAOS = 3 - dnsClassHESIOD = 4 - dnsClassANY = 255 - - // dnsMsg.rcode - dnsRcodeSuccess = 0 - dnsRcodeFormatError = 1 - dnsRcodeServerFailure = 2 - dnsRcodeNameError = 3 - dnsRcodeNotImplemented = 4 - dnsRcodeRefused = 5 -) - -// The wire format for the DNS packet header. -type dnsHeader struct { - Id uint16 - Bits uint16 - Qdcount, Ancount, Nscount, Arcount uint16 -} - -const ( - // dnsHeader.Bits - _QR = 1 << 15 // query/response (response=1) - _AA = 1 << 10 // authoritative - _TC = 1 << 9 // truncated - _RD = 1 << 8 // recursion desired - _RA = 1 << 7 // recursion available -) - -// DNS queries. -type dnsQuestion struct { - Name string "domain-name" // "domain-name" specifies encoding; see packers below - Qtype uint16 - Qclass uint16 -} - -// DNS responses (resource records). -// There are many types of messages, -// but they all share the same header. -type dnsRR_Header struct { - Name string "domain-name" - Rrtype uint16 - Class uint16 - Ttl uint32 - Rdlength uint16 // length of data after header -} - -func (h *dnsRR_Header) Header() *dnsRR_Header { - return h -} - -type dnsRR interface { - Header() *dnsRR_Header -} - -// Specific DNS RR formats for each query type. - -type dnsRR_CNAME struct { - Hdr dnsRR_Header - Cname string "domain-name" -} - -func (rr *dnsRR_CNAME) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_HINFO struct { - Hdr dnsRR_Header - Cpu string - Os string -} - -func (rr *dnsRR_HINFO) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_MB struct { - Hdr dnsRR_Header - Mb string "domain-name" -} - -func (rr *dnsRR_MB) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_MG struct { - Hdr dnsRR_Header - Mg string "domain-name" -} - -func (rr *dnsRR_MG) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_MINFO struct { - Hdr dnsRR_Header - Rmail string "domain-name" - Email string "domain-name" -} - -func (rr *dnsRR_MINFO) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_MR struct { - Hdr dnsRR_Header - Mr string "domain-name" -} - -func (rr *dnsRR_MR) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_MX struct { - Hdr dnsRR_Header - Pref uint16 - Mx string "domain-name" -} - -func (rr *dnsRR_MX) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_NS struct { - Hdr dnsRR_Header - Ns string "domain-name" -} - -func (rr *dnsRR_NS) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_PTR struct { - Hdr dnsRR_Header - Ptr string "domain-name" -} - -func (rr *dnsRR_PTR) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_SOA struct { - Hdr dnsRR_Header - Ns string "domain-name" - Mbox string "domain-name" - Serial uint32 - Refresh uint32 - Retry uint32 - Expire uint32 - Minttl uint32 -} - -func (rr *dnsRR_SOA) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_TXT struct { - Hdr dnsRR_Header - Txt string // not domain name -} - -func (rr *dnsRR_TXT) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_SRV struct { - Hdr dnsRR_Header - Priority uint16 - Weight uint16 - Port uint16 - Target string "domain-name" -} - -func (rr *dnsRR_SRV) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_A struct { - Hdr dnsRR_Header - A uint32 "ipv4" -} - -func (rr *dnsRR_A) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_AAAA struct { - Hdr dnsRR_Header - AAAA [16]byte "ipv6" -} - -func (rr *dnsRR_AAAA) Header() *dnsRR_Header { - return &rr.Hdr -} - -// Packing and unpacking. -// -// All the packers and unpackers take a (msg []byte, off int) -// and return (off1 int, ok bool). If they return ok==false, they -// also return off1==len(msg), so that the next unpacker will -// also fail. This lets us avoid checks of ok until the end of a -// packing sequence. - -// Map of constructors for each RR wire type. -var rr_mk = map[int]func() dnsRR{ - dnsTypeCNAME: func() dnsRR { return new(dnsRR_CNAME) }, - dnsTypeHINFO: func() dnsRR { return new(dnsRR_HINFO) }, - dnsTypeMB: func() dnsRR { return new(dnsRR_MB) }, - dnsTypeMG: func() dnsRR { return new(dnsRR_MG) }, - dnsTypeMINFO: func() dnsRR { return new(dnsRR_MINFO) }, - dnsTypeMR: func() dnsRR { return new(dnsRR_MR) }, - dnsTypeMX: func() dnsRR { return new(dnsRR_MX) }, - dnsTypeNS: func() dnsRR { return new(dnsRR_NS) }, - dnsTypePTR: func() dnsRR { return new(dnsRR_PTR) }, - dnsTypeSOA: func() dnsRR { return new(dnsRR_SOA) }, - dnsTypeTXT: func() dnsRR { return new(dnsRR_TXT) }, - dnsTypeSRV: func() dnsRR { return new(dnsRR_SRV) }, - dnsTypeA: func() dnsRR { return new(dnsRR_A) }, - dnsTypeAAAA: func() dnsRR { return new(dnsRR_AAAA) }, -} - -// Pack a domain name s into msg[off:]. -// Domain names are a sequence of counted strings -// split at the dots. They end with a zero-length string. -func packDomainName(s string, msg []byte, off int) (off1 int, ok bool) { - // Add trailing dot to canonicalize name. - if n := len(s); n == 0 || s[n-1] != '.' { - s += "." - } - - // Each dot ends a segment of the name. - // We trade each dot byte for a length byte. - // There is also a trailing zero. - // Check that we have all the space we need. - tot := len(s) + 1 - if off+tot > len(msg) { - return len(msg), false - } - - // Emit sequence of counted strings, chopping at dots. - begin := 0 - for i := 0; i < len(s); i++ { - if s[i] == '.' { - if i-begin >= 1<<6 { // top two bits of length must be clear - return len(msg), false - } - msg[off] = byte(i - begin) - off++ - for j := begin; j < i; j++ { - msg[off] = s[j] - off++ - } - begin = i + 1 - } - } - msg[off] = 0 - off++ - return off, true -} - -// Unpack a domain name. -// In addition to the simple sequences of counted strings above, -// domain names are allowed to refer to strings elsewhere in the -// packet, to avoid repeating common suffixes when returning -// many entries in a single domain. The pointers are marked -// by a length byte with the top two bits set. Ignoring those -// two bits, that byte and the next give a 14 bit offset from msg[0] -// where we should pick up the trail. -// Note that if we jump elsewhere in the packet, -// we return off1 == the offset after the first pointer we found, -// which is where the next record will start. -// In theory, the pointers are only allowed to jump backward. -// We let them jump anywhere and stop jumping after a while. -func unpackDomainName(msg []byte, off int) (s string, off1 int, ok bool) { - s = "" - ptr := 0 // number of pointers followed -Loop: - for { - if off >= len(msg) { - return "", len(msg), false - } - c := int(msg[off]) - off++ - switch c & 0xC0 { - case 0x00: - if c == 0x00 { - // end of name - break Loop - } - // literal string - if off+c > len(msg) { - return "", len(msg), false - } - s += string(msg[off:off+c]) + "." - off += c - case 0xC0: - // pointer to somewhere else in msg. - // remember location after first ptr, - // since that's how many bytes we consumed. - // also, don't follow too many pointers -- - // maybe there's a loop. - if off >= len(msg) { - return "", len(msg), false - } - c1 := msg[off] - off++ - if ptr == 0 { - off1 = off - } - if ptr++; ptr > 10 { - return "", len(msg), false - } - off = (c^0xC0)<<8 | int(c1) - default: - // 0x80 and 0x40 are reserved - return "", len(msg), false - } - } - if ptr == 0 { - off1 = off - } - return s, off1, true -} - -// TODO(rsc): Move into generic library? -// Pack a reflect.StructValue into msg. Struct members can only be uint16, uint32, string, -// [n]byte, and other (often anonymous) structs. -func packStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, ok bool) { - for i := 0; i < val.NumField(); i++ { - f := val.Type().(*reflect.StructType).Field(i) - switch fv := val.Field(i).(type) { - default: - BadType: - fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) - return len(msg), false - case *reflect.StructValue: - off, ok = packStructValue(fv, msg, off) - case *reflect.UintValue: - i := fv.Get() - switch fv.Type().Kind() { - default: - goto BadType - case reflect.Uint16: - if off+2 > len(msg) { - return len(msg), false - } - msg[off] = byte(i >> 8) - msg[off+1] = byte(i) - off += 2 - case reflect.Uint32: - if off+4 > len(msg) { - return len(msg), false - } - msg[off] = byte(i >> 24) - msg[off+1] = byte(i >> 16) - msg[off+2] = byte(i >> 8) - msg[off+3] = byte(i) - off += 4 - } - case *reflect.ArrayValue: - if fv.Type().(*reflect.ArrayType).Elem().Kind() != reflect.Uint8 { - goto BadType - } - n := fv.Len() - if off+n > len(msg) { - return len(msg), false - } - reflect.Copy(reflect.NewValue(msg[off:off+n]).(*reflect.SliceValue), fv) - off += n - case *reflect.StringValue: - // There are multiple string encodings. - // The tag distinguishes ordinary strings from domain names. - s := fv.Get() - switch f.Tag { - default: - fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag) - return len(msg), false - case "domain-name": - off, ok = packDomainName(s, msg, off) - if !ok { - return len(msg), false - } - case "": - // Counted string: 1 byte length. - if len(s) > 255 || off+1+len(s) > len(msg) { - return len(msg), false - } - msg[off] = byte(len(s)) - off++ - off += copy(msg[off:], s) - } - } - } - return off, true -} - -func structValue(any interface{}) *reflect.StructValue { - return reflect.NewValue(any).(*reflect.PtrValue).Elem().(*reflect.StructValue) -} - -func packStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) { - off, ok = packStructValue(structValue(any), msg, off) - return off, ok -} - -// TODO(rsc): Move into generic library? -// Unpack a reflect.StructValue from msg. -// Same restrictions as packStructValue. -func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, ok bool) { - for i := 0; i < val.NumField(); i++ { - f := val.Type().(*reflect.StructType).Field(i) - switch fv := val.Field(i).(type) { - default: - BadType: - fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) - return len(msg), false - case *reflect.StructValue: - off, ok = unpackStructValue(fv, msg, off) - case *reflect.UintValue: - switch fv.Type().Kind() { - default: - goto BadType - case reflect.Uint16: - if off+2 > len(msg) { - return len(msg), false - } - i := uint16(msg[off])<<8 | uint16(msg[off+1]) - fv.Set(uint64(i)) - off += 2 - case reflect.Uint32: - if off+4 > len(msg) { - return len(msg), false - } - i := uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | uint32(msg[off+2])<<8 | uint32(msg[off+3]) - fv.Set(uint64(i)) - off += 4 - } - case *reflect.ArrayValue: - if fv.Type().(*reflect.ArrayType).Elem().Kind() != reflect.Uint8 { - goto BadType - } - n := fv.Len() - if off+n > len(msg) { - return len(msg), false - } - reflect.Copy(fv, reflect.NewValue(msg[off:off+n]).(*reflect.SliceValue)) - off += n - case *reflect.StringValue: - var s string - switch f.Tag { - default: - fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag) - return len(msg), false - case "domain-name": - s, off, ok = unpackDomainName(msg, off) - if !ok { - return len(msg), false - } - case "": - if off >= len(msg) || off+1+int(msg[off]) > len(msg) { - return len(msg), false - } - n := int(msg[off]) - off++ - b := make([]byte, n) - for i := 0; i < n; i++ { - b[i] = msg[off+i] - } - off += n - s = string(b) - } - fv.Set(s) - } - } - return off, true -} - -func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) { - off, ok = unpackStructValue(structValue(any), msg, off) - return off, ok -} - -// Generic struct printer. -// Doesn't care about the string tag "domain-name", -// but does look for an "ipv4" tag on uint32 variables -// and the "ipv6" tag on array variables, -// printing them as IP addresses. -func printStructValue(val *reflect.StructValue) string { - s := "{" - for i := 0; i < val.NumField(); i++ { - if i > 0 { - s += ", " - } - f := val.Type().(*reflect.StructType).Field(i) - if !f.Anonymous { - s += f.Name + "=" - } - fval := val.Field(i) - if fv, ok := fval.(*reflect.StructValue); ok { - s += printStructValue(fv) - } else if fv, ok := fval.(*reflect.UintValue); ok && f.Tag == "ipv4" { - i := fv.Get() - s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String() - } else if fv, ok := fval.(*reflect.ArrayValue); ok && f.Tag == "ipv6" { - i := fv.Interface().([]byte) - s += IP(i).String() - } else { - s += fmt.Sprint(fval.Interface()) - } - } - s += "}" - return s -} - -func printStruct(any interface{}) string { return printStructValue(structValue(any)) } - -// Resource record packer. -func packRR(rr dnsRR, msg []byte, off int) (off2 int, ok bool) { - var off1 int - // pack twice, once to find end of header - // and again to find end of packet. - // a bit inefficient but this doesn't need to be fast. - // off1 is end of header - // off2 is end of rr - off1, ok = packStruct(rr.Header(), msg, off) - off2, ok = packStruct(rr, msg, off) - if !ok { - return len(msg), false - } - // pack a third time; redo header with correct data length - rr.Header().Rdlength = uint16(off2 - off1) - packStruct(rr.Header(), msg, off) - return off2, true -} - -// Resource record unpacker. -func unpackRR(msg []byte, off int) (rr dnsRR, off1 int, ok bool) { - // unpack just the header, to find the rr type and length - var h dnsRR_Header - off0 := off - if off, ok = unpackStruct(&h, msg, off); !ok { - return nil, len(msg), false - } - end := off + int(h.Rdlength) - - // make an rr of that type and re-unpack. - // again inefficient but doesn't need to be fast. - mk, known := rr_mk[int(h.Rrtype)] - if !known { - return &h, end, true - } - rr = mk() - off, ok = unpackStruct(rr, msg, off0) - if off != end { - return &h, end, true - } - return rr, off, ok -} - -// Usable representation of a DNS packet. - -// A manually-unpacked version of (id, bits). -// This is in its own struct for easy printing. -type dnsMsgHdr struct { - id uint16 - response bool - opcode int - authoritative bool - truncated bool - recursion_desired bool - recursion_available bool - rcode int -} - -type dnsMsg struct { - dnsMsgHdr - question []dnsQuestion - answer []dnsRR - ns []dnsRR - extra []dnsRR -} - -func (dns *dnsMsg) Pack() (msg []byte, ok bool) { - var dh dnsHeader - - // Convert convenient dnsMsg into wire-like dnsHeader. - dh.Id = dns.id - dh.Bits = uint16(dns.opcode)<<11 | uint16(dns.rcode) - if dns.recursion_available { - dh.Bits |= _RA - } - if dns.recursion_desired { - dh.Bits |= _RD - } - if dns.truncated { - dh.Bits |= _TC - } - if dns.authoritative { - dh.Bits |= _AA - } - if dns.response { - dh.Bits |= _QR - } - - // Prepare variable sized arrays. - question := dns.question - answer := dns.answer - ns := dns.ns - extra := dns.extra - - dh.Qdcount = uint16(len(question)) - dh.Ancount = uint16(len(answer)) - dh.Nscount = uint16(len(ns)) - dh.Arcount = uint16(len(extra)) - - // Could work harder to calculate message size, - // but this is far more than we need and not - // big enough to hurt the allocator. - msg = make([]byte, 2000) - - // Pack it in: header and then the pieces. - off := 0 - off, ok = packStruct(&dh, msg, off) - for i := 0; i < len(question); i++ { - off, ok = packStruct(&question[i], msg, off) - } - for i := 0; i < len(answer); i++ { - off, ok = packRR(answer[i], msg, off) - } - for i := 0; i < len(ns); i++ { - off, ok = packRR(ns[i], msg, off) - } - for i := 0; i < len(extra); i++ { - off, ok = packRR(extra[i], msg, off) - } - if !ok { - return nil, false - } - return msg[0:off], true -} - -func (dns *dnsMsg) Unpack(msg []byte) bool { - // Header. - var dh dnsHeader - off := 0 - var ok bool - if off, ok = unpackStruct(&dh, msg, off); !ok { - return false - } - dns.id = dh.Id - dns.response = (dh.Bits & _QR) != 0 - dns.opcode = int(dh.Bits>>11) & 0xF - dns.authoritative = (dh.Bits & _AA) != 0 - dns.truncated = (dh.Bits & _TC) != 0 - dns.recursion_desired = (dh.Bits & _RD) != 0 - dns.recursion_available = (dh.Bits & _RA) != 0 - dns.rcode = int(dh.Bits & 0xF) - - // Arrays. - dns.question = make([]dnsQuestion, dh.Qdcount) - dns.answer = make([]dnsRR, dh.Ancount) - dns.ns = make([]dnsRR, dh.Nscount) - dns.extra = make([]dnsRR, dh.Arcount) - - for i := 0; i < len(dns.question); i++ { - off, ok = unpackStruct(&dns.question[i], msg, off) - } - for i := 0; i < len(dns.answer); i++ { - dns.answer[i], off, ok = unpackRR(msg, off) - } - for i := 0; i < len(dns.ns); i++ { - dns.ns[i], off, ok = unpackRR(msg, off) - } - for i := 0; i < len(dns.extra); i++ { - dns.extra[i], off, ok = unpackRR(msg, off) - } - if !ok { - return false - } - // if off != len(msg) { - // println("extra bytes in dns packet", off, "<", len(msg)); - // } - return true -} - -func (dns *dnsMsg) String() string { - s := "DNS: " + printStruct(&dns.dnsMsgHdr) + "\n" - if len(dns.question) > 0 { - s += "-- Questions\n" - for i := 0; i < len(dns.question); i++ { - s += printStruct(&dns.question[i]) + "\n" - } - } - if len(dns.answer) > 0 { - s += "-- Answers\n" - for i := 0; i < len(dns.answer); i++ { - s += printStruct(dns.answer[i]) + "\n" - } - } - if len(dns.ns) > 0 { - s += "-- Name servers\n" - for i := 0; i < len(dns.ns); i++ { - s += printStruct(dns.ns[i]) + "\n" - } - } - if len(dns.extra) > 0 { - s += "-- Extra\n" - for i := 0; i < len(dns.extra); i++ { - s += printStruct(dns.extra[i]) + "\n" - } - } - return s -} diff --git a/src/cmd/fix/testdata/reflect.dnsmsg.go.out b/src/cmd/fix/testdata/reflect.dnsmsg.go.out deleted file mode 100644 index c777fe27c..000000000 --- a/src/cmd/fix/testdata/reflect.dnsmsg.go.out +++ /dev/null @@ -1,777 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// DNS packet assembly. See RFC 1035. -// -// This is intended to support name resolution during net.Dial. -// It doesn't have to be blazing fast. -// -// Rather than write the usual handful of routines to pack and -// unpack every message that can appear on the wire, we use -// reflection to write a generic pack/unpack for structs and then -// use it. Thus, if in the future we need to define new message -// structs, no new pack/unpack/printing code needs to be written. -// -// The first half of this file defines the DNS message formats. -// The second half implements the conversion to and from wire format. -// A few of the structure elements have string tags to aid the -// generic pack/unpack routines. -// -// TODO(rsc): There are enough names defined in this file that they're all -// prefixed with dns. Perhaps put this in its own package later. - -package net - -import ( - "fmt" - "os" - "reflect" -) - -// Packet formats - -// Wire constants. -const ( - // valid dnsRR_Header.Rrtype and dnsQuestion.qtype - dnsTypeA = 1 - dnsTypeNS = 2 - dnsTypeMD = 3 - dnsTypeMF = 4 - dnsTypeCNAME = 5 - dnsTypeSOA = 6 - dnsTypeMB = 7 - dnsTypeMG = 8 - dnsTypeMR = 9 - dnsTypeNULL = 10 - dnsTypeWKS = 11 - dnsTypePTR = 12 - dnsTypeHINFO = 13 - dnsTypeMINFO = 14 - dnsTypeMX = 15 - dnsTypeTXT = 16 - dnsTypeAAAA = 28 - dnsTypeSRV = 33 - - // valid dnsQuestion.qtype only - dnsTypeAXFR = 252 - dnsTypeMAILB = 253 - dnsTypeMAILA = 254 - dnsTypeALL = 255 - - // valid dnsQuestion.qclass - dnsClassINET = 1 - dnsClassCSNET = 2 - dnsClassCHAOS = 3 - dnsClassHESIOD = 4 - dnsClassANY = 255 - - // dnsMsg.rcode - dnsRcodeSuccess = 0 - dnsRcodeFormatError = 1 - dnsRcodeServerFailure = 2 - dnsRcodeNameError = 3 - dnsRcodeNotImplemented = 4 - dnsRcodeRefused = 5 -) - -// The wire format for the DNS packet header. -type dnsHeader struct { - Id uint16 - Bits uint16 - Qdcount, Ancount, Nscount, Arcount uint16 -} - -const ( - // dnsHeader.Bits - _QR = 1 << 15 // query/response (response=1) - _AA = 1 << 10 // authoritative - _TC = 1 << 9 // truncated - _RD = 1 << 8 // recursion desired - _RA = 1 << 7 // recursion available -) - -// DNS queries. -type dnsQuestion struct { - Name string "domain-name" // "domain-name" specifies encoding; see packers below - Qtype uint16 - Qclass uint16 -} - -// DNS responses (resource records). -// There are many types of messages, -// but they all share the same header. -type dnsRR_Header struct { - Name string "domain-name" - Rrtype uint16 - Class uint16 - Ttl uint32 - Rdlength uint16 // length of data after header -} - -func (h *dnsRR_Header) Header() *dnsRR_Header { - return h -} - -type dnsRR interface { - Header() *dnsRR_Header -} - -// Specific DNS RR formats for each query type. - -type dnsRR_CNAME struct { - Hdr dnsRR_Header - Cname string "domain-name" -} - -func (rr *dnsRR_CNAME) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_HINFO struct { - Hdr dnsRR_Header - Cpu string - Os string -} - -func (rr *dnsRR_HINFO) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_MB struct { - Hdr dnsRR_Header - Mb string "domain-name" -} - -func (rr *dnsRR_MB) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_MG struct { - Hdr dnsRR_Header - Mg string "domain-name" -} - -func (rr *dnsRR_MG) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_MINFO struct { - Hdr dnsRR_Header - Rmail string "domain-name" - Email string "domain-name" -} - -func (rr *dnsRR_MINFO) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_MR struct { - Hdr dnsRR_Header - Mr string "domain-name" -} - -func (rr *dnsRR_MR) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_MX struct { - Hdr dnsRR_Header - Pref uint16 - Mx string "domain-name" -} - -func (rr *dnsRR_MX) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_NS struct { - Hdr dnsRR_Header - Ns string "domain-name" -} - -func (rr *dnsRR_NS) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_PTR struct { - Hdr dnsRR_Header - Ptr string "domain-name" -} - -func (rr *dnsRR_PTR) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_SOA struct { - Hdr dnsRR_Header - Ns string "domain-name" - Mbox string "domain-name" - Serial uint32 - Refresh uint32 - Retry uint32 - Expire uint32 - Minttl uint32 -} - -func (rr *dnsRR_SOA) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_TXT struct { - Hdr dnsRR_Header - Txt string // not domain name -} - -func (rr *dnsRR_TXT) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_SRV struct { - Hdr dnsRR_Header - Priority uint16 - Weight uint16 - Port uint16 - Target string "domain-name" -} - -func (rr *dnsRR_SRV) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_A struct { - Hdr dnsRR_Header - A uint32 "ipv4" -} - -func (rr *dnsRR_A) Header() *dnsRR_Header { - return &rr.Hdr -} - -type dnsRR_AAAA struct { - Hdr dnsRR_Header - AAAA [16]byte "ipv6" -} - -func (rr *dnsRR_AAAA) Header() *dnsRR_Header { - return &rr.Hdr -} - -// Packing and unpacking. -// -// All the packers and unpackers take a (msg []byte, off int) -// and return (off1 int, ok bool). If they return ok==false, they -// also return off1==len(msg), so that the next unpacker will -// also fail. This lets us avoid checks of ok until the end of a -// packing sequence. - -// Map of constructors for each RR wire type. -var rr_mk = map[int]func() dnsRR{ - dnsTypeCNAME: func() dnsRR { return new(dnsRR_CNAME) }, - dnsTypeHINFO: func() dnsRR { return new(dnsRR_HINFO) }, - dnsTypeMB: func() dnsRR { return new(dnsRR_MB) }, - dnsTypeMG: func() dnsRR { return new(dnsRR_MG) }, - dnsTypeMINFO: func() dnsRR { return new(dnsRR_MINFO) }, - dnsTypeMR: func() dnsRR { return new(dnsRR_MR) }, - dnsTypeMX: func() dnsRR { return new(dnsRR_MX) }, - dnsTypeNS: func() dnsRR { return new(dnsRR_NS) }, - dnsTypePTR: func() dnsRR { return new(dnsRR_PTR) }, - dnsTypeSOA: func() dnsRR { return new(dnsRR_SOA) }, - dnsTypeTXT: func() dnsRR { return new(dnsRR_TXT) }, - dnsTypeSRV: func() dnsRR { return new(dnsRR_SRV) }, - dnsTypeA: func() dnsRR { return new(dnsRR_A) }, - dnsTypeAAAA: func() dnsRR { return new(dnsRR_AAAA) }, -} - -// Pack a domain name s into msg[off:]. -// Domain names are a sequence of counted strings -// split at the dots. They end with a zero-length string. -func packDomainName(s string, msg []byte, off int) (off1 int, ok bool) { - // Add trailing dot to canonicalize name. - if n := len(s); n == 0 || s[n-1] != '.' { - s += "." - } - - // Each dot ends a segment of the name. - // We trade each dot byte for a length byte. - // There is also a trailing zero. - // Check that we have all the space we need. - tot := len(s) + 1 - if off+tot > len(msg) { - return len(msg), false - } - - // Emit sequence of counted strings, chopping at dots. - begin := 0 - for i := 0; i < len(s); i++ { - if s[i] == '.' { - if i-begin >= 1<<6 { // top two bits of length must be clear - return len(msg), false - } - msg[off] = byte(i - begin) - off++ - for j := begin; j < i; j++ { - msg[off] = s[j] - off++ - } - begin = i + 1 - } - } - msg[off] = 0 - off++ - return off, true -} - -// Unpack a domain name. -// In addition to the simple sequences of counted strings above, -// domain names are allowed to refer to strings elsewhere in the -// packet, to avoid repeating common suffixes when returning -// many entries in a single domain. The pointers are marked -// by a length byte with the top two bits set. Ignoring those -// two bits, that byte and the next give a 14 bit offset from msg[0] -// where we should pick up the trail. -// Note that if we jump elsewhere in the packet, -// we return off1 == the offset after the first pointer we found, -// which is where the next record will start. -// In theory, the pointers are only allowed to jump backward. -// We let them jump anywhere and stop jumping after a while. -func unpackDomainName(msg []byte, off int) (s string, off1 int, ok bool) { - s = "" - ptr := 0 // number of pointers followed -Loop: - for { - if off >= len(msg) { - return "", len(msg), false - } - c := int(msg[off]) - off++ - switch c & 0xC0 { - case 0x00: - if c == 0x00 { - // end of name - break Loop - } - // literal string - if off+c > len(msg) { - return "", len(msg), false - } - s += string(msg[off:off+c]) + "." - off += c - case 0xC0: - // pointer to somewhere else in msg. - // remember location after first ptr, - // since that's how many bytes we consumed. - // also, don't follow too many pointers -- - // maybe there's a loop. - if off >= len(msg) { - return "", len(msg), false - } - c1 := msg[off] - off++ - if ptr == 0 { - off1 = off - } - if ptr++; ptr > 10 { - return "", len(msg), false - } - off = (c^0xC0)<<8 | int(c1) - default: - // 0x80 and 0x40 are reserved - return "", len(msg), false - } - } - if ptr == 0 { - off1 = off - } - return s, off1, true -} - -// TODO(rsc): Move into generic library? -// Pack a reflect.StructValue into msg. Struct members can only be uint16, uint32, string, -// [n]byte, and other (often anonymous) structs. -func packStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok bool) { - for i := 0; i < val.NumField(); i++ { - f := val.Type().Field(i) - switch fv := val.Field(i); fv.Kind() { - default: - BadType: - fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) - return len(msg), false - case reflect.Struct: - off, ok = packStructValue(fv, msg, off) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - i := fv.Uint() - switch fv.Type().Kind() { - default: - goto BadType - case reflect.Uint16: - if off+2 > len(msg) { - return len(msg), false - } - msg[off] = byte(i >> 8) - msg[off+1] = byte(i) - off += 2 - case reflect.Uint32: - if off+4 > len(msg) { - return len(msg), false - } - msg[off] = byte(i >> 24) - msg[off+1] = byte(i >> 16) - msg[off+2] = byte(i >> 8) - msg[off+3] = byte(i) - off += 4 - } - case reflect.Array: - if fv.Type().Elem().Kind() != reflect.Uint8 { - goto BadType - } - n := fv.Len() - if off+n > len(msg) { - return len(msg), false - } - reflect.Copy(reflect.ValueOf(msg[off:off+n]), fv) - off += n - case reflect.String: - // There are multiple string encodings. - // The tag distinguishes ordinary strings from domain names. - s := fv.String() - switch f.Tag { - default: - fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag) - return len(msg), false - case "domain-name": - off, ok = packDomainName(s, msg, off) - if !ok { - return len(msg), false - } - case "": - // Counted string: 1 byte length. - if len(s) > 255 || off+1+len(s) > len(msg) { - return len(msg), false - } - msg[off] = byte(len(s)) - off++ - off += copy(msg[off:], s) - } - } - } - return off, true -} - -func structValue(any interface{}) reflect.Value { - return reflect.ValueOf(any).Elem() -} - -func packStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) { - off, ok = packStructValue(structValue(any), msg, off) - return off, ok -} - -// TODO(rsc): Move into generic library? -// Unpack a reflect.StructValue from msg. -// Same restrictions as packStructValue. -func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok bool) { - for i := 0; i < val.NumField(); i++ { - f := val.Type().Field(i) - switch fv := val.Field(i); fv.Kind() { - default: - BadType: - fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) - return len(msg), false - case reflect.Struct: - off, ok = unpackStructValue(fv, msg, off) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - switch fv.Type().Kind() { - default: - goto BadType - case reflect.Uint16: - if off+2 > len(msg) { - return len(msg), false - } - i := uint16(msg[off])<<8 | uint16(msg[off+1]) - fv.SetUint(uint64(i)) - off += 2 - case reflect.Uint32: - if off+4 > len(msg) { - return len(msg), false - } - i := uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | uint32(msg[off+2])<<8 | uint32(msg[off+3]) - fv.SetUint(uint64(i)) - off += 4 - } - case reflect.Array: - if fv.Type().Elem().Kind() != reflect.Uint8 { - goto BadType - } - n := fv.Len() - if off+n > len(msg) { - return len(msg), false - } - reflect.Copy(fv, reflect.ValueOf(msg[off:off+n])) - off += n - case reflect.String: - var s string - switch f.Tag { - default: - fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag) - return len(msg), false - case "domain-name": - s, off, ok = unpackDomainName(msg, off) - if !ok { - return len(msg), false - } - case "": - if off >= len(msg) || off+1+int(msg[off]) > len(msg) { - return len(msg), false - } - n := int(msg[off]) - off++ - b := make([]byte, n) - for i := 0; i < n; i++ { - b[i] = msg[off+i] - } - off += n - s = string(b) - } - fv.SetString(s) - } - } - return off, true -} - -func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) { - off, ok = unpackStructValue(structValue(any), msg, off) - return off, ok -} - -// Generic struct printer. -// Doesn't care about the string tag "domain-name", -// but does look for an "ipv4" tag on uint32 variables -// and the "ipv6" tag on array variables, -// printing them as IP addresses. -func printStructValue(val reflect.Value) string { - s := "{" - for i := 0; i < val.NumField(); i++ { - if i > 0 { - s += ", " - } - f := val.Type().Field(i) - if !f.Anonymous { - s += f.Name + "=" - } - fval := val.Field(i) - if fv := fval; fv.Kind() == reflect.Struct { - s += printStructValue(fv) - } else if fv := fval; (fv.Kind() == reflect.Uint || fv.Kind() == reflect.Uint8 || fv.Kind() == reflect.Uint16 || fv.Kind() == reflect.Uint32 || fv.Kind() == reflect.Uint64 || fv.Kind() == reflect.Uintptr) && f.Tag == "ipv4" { - i := fv.Uint() - s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String() - } else if fv := fval; fv.Kind() == reflect.Array && f.Tag == "ipv6" { - i := fv.Interface().([]byte) - s += IP(i).String() - } else { - s += fmt.Sprint(fval.Interface()) - } - } - s += "}" - return s -} - -func printStruct(any interface{}) string { return printStructValue(structValue(any)) } - -// Resource record packer. -func packRR(rr dnsRR, msg []byte, off int) (off2 int, ok bool) { - var off1 int - // pack twice, once to find end of header - // and again to find end of packet. - // a bit inefficient but this doesn't need to be fast. - // off1 is end of header - // off2 is end of rr - off1, ok = packStruct(rr.Header(), msg, off) - off2, ok = packStruct(rr, msg, off) - if !ok { - return len(msg), false - } - // pack a third time; redo header with correct data length - rr.Header().Rdlength = uint16(off2 - off1) - packStruct(rr.Header(), msg, off) - return off2, true -} - -// Resource record unpacker. -func unpackRR(msg []byte, off int) (rr dnsRR, off1 int, ok bool) { - // unpack just the header, to find the rr type and length - var h dnsRR_Header - off0 := off - if off, ok = unpackStruct(&h, msg, off); !ok { - return nil, len(msg), false - } - end := off + int(h.Rdlength) - - // make an rr of that type and re-unpack. - // again inefficient but doesn't need to be fast. - mk, known := rr_mk[int(h.Rrtype)] - if !known { - return &h, end, true - } - rr = mk() - off, ok = unpackStruct(rr, msg, off0) - if off != end { - return &h, end, true - } - return rr, off, ok -} - -// Usable representation of a DNS packet. - -// A manually-unpacked version of (id, bits). -// This is in its own struct for easy printing. -type dnsMsgHdr struct { - id uint16 - response bool - opcode int - authoritative bool - truncated bool - recursion_desired bool - recursion_available bool - rcode int -} - -type dnsMsg struct { - dnsMsgHdr - question []dnsQuestion - answer []dnsRR - ns []dnsRR - extra []dnsRR -} - -func (dns *dnsMsg) Pack() (msg []byte, ok bool) { - var dh dnsHeader - - // Convert convenient dnsMsg into wire-like dnsHeader. - dh.Id = dns.id - dh.Bits = uint16(dns.opcode)<<11 | uint16(dns.rcode) - if dns.recursion_available { - dh.Bits |= _RA - } - if dns.recursion_desired { - dh.Bits |= _RD - } - if dns.truncated { - dh.Bits |= _TC - } - if dns.authoritative { - dh.Bits |= _AA - } - if dns.response { - dh.Bits |= _QR - } - - // Prepare variable sized arrays. - question := dns.question - answer := dns.answer - ns := dns.ns - extra := dns.extra - - dh.Qdcount = uint16(len(question)) - dh.Ancount = uint16(len(answer)) - dh.Nscount = uint16(len(ns)) - dh.Arcount = uint16(len(extra)) - - // Could work harder to calculate message size, - // but this is far more than we need and not - // big enough to hurt the allocator. - msg = make([]byte, 2000) - - // Pack it in: header and then the pieces. - off := 0 - off, ok = packStruct(&dh, msg, off) - for i := 0; i < len(question); i++ { - off, ok = packStruct(&question[i], msg, off) - } - for i := 0; i < len(answer); i++ { - off, ok = packRR(answer[i], msg, off) - } - for i := 0; i < len(ns); i++ { - off, ok = packRR(ns[i], msg, off) - } - for i := 0; i < len(extra); i++ { - off, ok = packRR(extra[i], msg, off) - } - if !ok { - return nil, false - } - return msg[0:off], true -} - -func (dns *dnsMsg) Unpack(msg []byte) bool { - // Header. - var dh dnsHeader - off := 0 - var ok bool - if off, ok = unpackStruct(&dh, msg, off); !ok { - return false - } - dns.id = dh.Id - dns.response = (dh.Bits & _QR) != 0 - dns.opcode = int(dh.Bits>>11) & 0xF - dns.authoritative = (dh.Bits & _AA) != 0 - dns.truncated = (dh.Bits & _TC) != 0 - dns.recursion_desired = (dh.Bits & _RD) != 0 - dns.recursion_available = (dh.Bits & _RA) != 0 - dns.rcode = int(dh.Bits & 0xF) - - // Arrays. - dns.question = make([]dnsQuestion, dh.Qdcount) - dns.answer = make([]dnsRR, dh.Ancount) - dns.ns = make([]dnsRR, dh.Nscount) - dns.extra = make([]dnsRR, dh.Arcount) - - for i := 0; i < len(dns.question); i++ { - off, ok = unpackStruct(&dns.question[i], msg, off) - } - for i := 0; i < len(dns.answer); i++ { - dns.answer[i], off, ok = unpackRR(msg, off) - } - for i := 0; i < len(dns.ns); i++ { - dns.ns[i], off, ok = unpackRR(msg, off) - } - for i := 0; i < len(dns.extra); i++ { - dns.extra[i], off, ok = unpackRR(msg, off) - } - if !ok { - return false - } - // if off != len(msg) { - // println("extra bytes in dns packet", off, "<", len(msg)); - // } - return true -} - -func (dns *dnsMsg) String() string { - s := "DNS: " + printStruct(&dns.dnsMsgHdr) + "\n" - if len(dns.question) > 0 { - s += "-- Questions\n" - for i := 0; i < len(dns.question); i++ { - s += printStruct(&dns.question[i]) + "\n" - } - } - if len(dns.answer) > 0 { - s += "-- Answers\n" - for i := 0; i < len(dns.answer); i++ { - s += printStruct(dns.answer[i]) + "\n" - } - } - if len(dns.ns) > 0 { - s += "-- Name servers\n" - for i := 0; i < len(dns.ns); i++ { - s += printStruct(dns.ns[i]) + "\n" - } - } - if len(dns.extra) > 0 { - s += "-- Extra\n" - for i := 0; i < len(dns.extra); i++ { - s += printStruct(dns.extra[i]) + "\n" - } - } - return s -} diff --git a/src/cmd/fix/testdata/reflect.encode.go.in b/src/cmd/fix/testdata/reflect.encode.go.in deleted file mode 100644 index 26ce47039..000000000 --- a/src/cmd/fix/testdata/reflect.encode.go.in +++ /dev/null @@ -1,367 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// The json package implements encoding and decoding of JSON objects as -// defined in RFC 4627. -package json - -import ( - "bytes" - "encoding/base64" - "os" - "reflect" - "runtime" - "sort" - "strconv" - "unicode" - "utf8" -) - -// Marshal returns the JSON encoding of v. -// -// Marshal traverses the value v recursively. -// If an encountered value implements the Marshaler interface, -// Marshal calls its MarshalJSON method to produce JSON. -// -// Otherwise, Marshal uses the following type-dependent default encodings: -// -// Boolean values encode as JSON booleans. -// -// Floating point and integer values encode as JSON numbers. -// -// String values encode as JSON strings, with each invalid UTF-8 sequence -// replaced by the encoding of the Unicode replacement character U+FFFD. -// -// Array and slice values encode as JSON arrays, except that -// []byte encodes as a base64-encoded string. -// -// Struct values encode as JSON objects. Each struct field becomes -// a member of the object. By default the object's key name is the -// struct field name. If the struct field has a non-empty tag consisting -// of only Unicode letters, digits, and underscores, that tag will be used -// as the name instead. Only exported fields will be encoded. -// -// Map values encode as JSON objects. -// The map's key type must be string; the object keys are used directly -// as map keys. -// -// Pointer values encode as the value pointed to. -// A nil pointer encodes as the null JSON object. -// -// Interface values encode as the value contained in the interface. -// A nil interface value encodes as the null JSON object. -// -// Channel, complex, and function values cannot be encoded in JSON. -// Attempting to encode such a value causes Marshal to return -// an InvalidTypeError. -// -// JSON cannot represent cyclic data structures and Marshal does not -// handle them. Passing cyclic structures to Marshal will result in -// an infinite recursion. -// -func Marshal(v interface{}) ([]byte, os.Error) { - e := &encodeState{} - err := e.marshal(v) - if err != nil { - return nil, err - } - return e.Bytes(), nil -} - -// MarshalIndent is like Marshal but applies Indent to format the output. -func MarshalIndent(v interface{}, prefix, indent string) ([]byte, os.Error) { - b, err := Marshal(v) - if err != nil { - return nil, err - } - var buf bytes.Buffer - err = Indent(&buf, b, prefix, indent) - if err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -// MarshalForHTML is like Marshal but applies HTMLEscape to the output. -func MarshalForHTML(v interface{}) ([]byte, os.Error) { - b, err := Marshal(v) - if err != nil { - return nil, err - } - var buf bytes.Buffer - HTMLEscape(&buf, b) - return buf.Bytes(), nil -} - -// HTMLEscape appends to dst the JSON-encoded src with <, >, and & -// characters inside string literals changed to \u003c, \u003e, \u0026 -// so that the JSON will be safe to embed inside HTML <script> tags. -// For historical reasons, web browsers don't honor standard HTML -// escaping within <script> tags, so an alternative JSON encoding must -// be used. -func HTMLEscape(dst *bytes.Buffer, src []byte) { - // < > & can only appear in string literals, - // so just scan the string one byte at a time. - start := 0 - for i, c := range src { - if c == '<' || c == '>' || c == '&' { - if start < i { - dst.Write(src[start:i]) - } - dst.WriteString(`\u00`) - dst.WriteByte(hex[c>>4]) - dst.WriteByte(hex[c&0xF]) - start = i + 1 - } - } - if start < len(src) { - dst.Write(src[start:]) - } -} - -// Marshaler is the interface implemented by objects that -// can marshal themselves into valid JSON. -type Marshaler interface { - MarshalJSON() ([]byte, os.Error) -} - -type UnsupportedTypeError struct { - Type reflect.Type -} - -func (e *UnsupportedTypeError) String() string { - return "json: unsupported type: " + e.Type.String() -} - -type InvalidUTF8Error struct { - S string -} - -func (e *InvalidUTF8Error) String() string { - return "json: invalid UTF-8 in string: " + strconv.Quote(e.S) -} - -type MarshalerError struct { - Type reflect.Type - Error os.Error -} - -func (e *MarshalerError) String() string { - return "json: error calling MarshalJSON for type " + e.Type.String() + ": " + e.Error.String() -} - -type interfaceOrPtrValue interface { - IsNil() bool - Elem() reflect.Value -} - -var hex = "0123456789abcdef" - -// An encodeState encodes JSON into a bytes.Buffer. -type encodeState struct { - bytes.Buffer // accumulated output -} - -func (e *encodeState) marshal(v interface{}) (err os.Error) { - defer func() { - if r := recover(); r != nil { - if _, ok := r.(runtime.Error); ok { - panic(r) - } - err = r.(os.Error) - } - }() - e.reflectValue(reflect.NewValue(v)) - return nil -} - -func (e *encodeState) error(err os.Error) { - panic(err) -} - -var byteSliceType = reflect.Typeof([]byte(nil)) - -func (e *encodeState) reflectValue(v reflect.Value) { - if v == nil { - e.WriteString("null") - return - } - - if j, ok := v.Interface().(Marshaler); ok { - b, err := j.MarshalJSON() - if err == nil { - // copy JSON into buffer, checking validity. - err = Compact(&e.Buffer, b) - } - if err != nil { - e.error(&MarshalerError{v.Type(), err}) - } - return - } - - switch v := v.(type) { - case *reflect.BoolValue: - x := v.Get() - if x { - e.WriteString("true") - } else { - e.WriteString("false") - } - - case *reflect.IntValue: - e.WriteString(strconv.Itoa64(v.Get())) - - case *reflect.UintValue: - e.WriteString(strconv.Uitoa64(v.Get())) - - case *reflect.FloatValue: - e.WriteString(strconv.FtoaN(v.Get(), 'g', -1, v.Type().Bits())) - - case *reflect.StringValue: - e.string(v.Get()) - - case *reflect.StructValue: - e.WriteByte('{') - t := v.Type().(*reflect.StructType) - n := v.NumField() - first := true - for i := 0; i < n; i++ { - f := t.Field(i) - if f.PkgPath != "" { - continue - } - if first { - first = false - } else { - e.WriteByte(',') - } - if isValidTag(f.Tag) { - e.string(f.Tag) - } else { - e.string(f.Name) - } - e.WriteByte(':') - e.reflectValue(v.Field(i)) - } - e.WriteByte('}') - - case *reflect.MapValue: - if _, ok := v.Type().(*reflect.MapType).Key().(*reflect.StringType); !ok { - e.error(&UnsupportedTypeError{v.Type()}) - } - if v.IsNil() { - e.WriteString("null") - break - } - e.WriteByte('{') - var sv stringValues = v.Keys() - sort.Sort(sv) - for i, k := range sv { - if i > 0 { - e.WriteByte(',') - } - e.string(k.(*reflect.StringValue).Get()) - e.WriteByte(':') - e.reflectValue(v.Elem(k)) - } - e.WriteByte('}') - - case reflect.ArrayOrSliceValue: - if v.Type() == byteSliceType { - e.WriteByte('"') - s := v.Interface().([]byte) - if len(s) < 1024 { - // for small buffers, using Encode directly is much faster. - dst := make([]byte, base64.StdEncoding.EncodedLen(len(s))) - base64.StdEncoding.Encode(dst, s) - e.Write(dst) - } else { - // for large buffers, avoid unnecessary extra temporary - // buffer space. - enc := base64.NewEncoder(base64.StdEncoding, e) - enc.Write(s) - enc.Close() - } - e.WriteByte('"') - break - } - e.WriteByte('[') - n := v.Len() - for i := 0; i < n; i++ { - if i > 0 { - e.WriteByte(',') - } - e.reflectValue(v.Elem(i)) - } - e.WriteByte(']') - - case interfaceOrPtrValue: - if v.IsNil() { - e.WriteString("null") - return - } - e.reflectValue(v.Elem()) - - default: - e.error(&UnsupportedTypeError{v.Type()}) - } - return -} - -func isValidTag(s string) bool { - if s == "" { - return false - } - for _, c := range s { - if c != '_' && !unicode.IsLetter(c) && !unicode.IsDigit(c) { - return false - } - } - return true -} - -// stringValues is a slice of reflect.Value holding *reflect.StringValue. -// It implements the methods to sort by string. -type stringValues []reflect.Value - -func (sv stringValues) Len() int { return len(sv) } -func (sv stringValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] } -func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) } -func (sv stringValues) get(i int) string { return sv[i].(*reflect.StringValue).Get() } - -func (e *encodeState) string(s string) { - e.WriteByte('"') - start := 0 - for i := 0; i < len(s); { - if b := s[i]; b < utf8.RuneSelf { - if 0x20 <= b && b != '\\' && b != '"' { - i++ - continue - } - if start < i { - e.WriteString(s[start:i]) - } - if b == '\\' || b == '"' { - e.WriteByte('\\') - e.WriteByte(b) - } else { - e.WriteString(`\u00`) - e.WriteByte(hex[b>>4]) - e.WriteByte(hex[b&0xF]) - } - i++ - start = i - continue - } - c, size := utf8.DecodeRuneInString(s[i:]) - if c == utf8.RuneError && size == 1 { - e.error(&InvalidUTF8Error{s}) - } - i += size - } - if start < len(s) { - e.WriteString(s[start:]) - } - e.WriteByte('"') -} diff --git a/src/cmd/fix/testdata/reflect.encode.go.out b/src/cmd/fix/testdata/reflect.encode.go.out deleted file mode 100644 index 9a13a75ab..000000000 --- a/src/cmd/fix/testdata/reflect.encode.go.out +++ /dev/null @@ -1,367 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// The json package implements encoding and decoding of JSON objects as -// defined in RFC 4627. -package json - -import ( - "bytes" - "encoding/base64" - "os" - "reflect" - "runtime" - "sort" - "strconv" - "unicode" - "utf8" -) - -// Marshal returns the JSON encoding of v. -// -// Marshal traverses the value v recursively. -// If an encountered value implements the Marshaler interface, -// Marshal calls its MarshalJSON method to produce JSON. -// -// Otherwise, Marshal uses the following type-dependent default encodings: -// -// Boolean values encode as JSON booleans. -// -// Floating point and integer values encode as JSON numbers. -// -// String values encode as JSON strings, with each invalid UTF-8 sequence -// replaced by the encoding of the Unicode replacement character U+FFFD. -// -// Array and slice values encode as JSON arrays, except that -// []byte encodes as a base64-encoded string. -// -// Struct values encode as JSON objects. Each struct field becomes -// a member of the object. By default the object's key name is the -// struct field name. If the struct field has a non-empty tag consisting -// of only Unicode letters, digits, and underscores, that tag will be used -// as the name instead. Only exported fields will be encoded. -// -// Map values encode as JSON objects. -// The map's key type must be string; the object keys are used directly -// as map keys. -// -// Pointer values encode as the value pointed to. -// A nil pointer encodes as the null JSON object. -// -// Interface values encode as the value contained in the interface. -// A nil interface value encodes as the null JSON object. -// -// Channel, complex, and function values cannot be encoded in JSON. -// Attempting to encode such a value causes Marshal to return -// an InvalidTypeError. -// -// JSON cannot represent cyclic data structures and Marshal does not -// handle them. Passing cyclic structures to Marshal will result in -// an infinite recursion. -// -func Marshal(v interface{}) ([]byte, os.Error) { - e := &encodeState{} - err := e.marshal(v) - if err != nil { - return nil, err - } - return e.Bytes(), nil -} - -// MarshalIndent is like Marshal but applies Indent to format the output. -func MarshalIndent(v interface{}, prefix, indent string) ([]byte, os.Error) { - b, err := Marshal(v) - if err != nil { - return nil, err - } - var buf bytes.Buffer - err = Indent(&buf, b, prefix, indent) - if err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -// MarshalForHTML is like Marshal but applies HTMLEscape to the output. -func MarshalForHTML(v interface{}) ([]byte, os.Error) { - b, err := Marshal(v) - if err != nil { - return nil, err - } - var buf bytes.Buffer - HTMLEscape(&buf, b) - return buf.Bytes(), nil -} - -// HTMLEscape appends to dst the JSON-encoded src with <, >, and & -// characters inside string literals changed to \u003c, \u003e, \u0026 -// so that the JSON will be safe to embed inside HTML <script> tags. -// For historical reasons, web browsers don't honor standard HTML -// escaping within <script> tags, so an alternative JSON encoding must -// be used. -func HTMLEscape(dst *bytes.Buffer, src []byte) { - // < > & can only appear in string literals, - // so just scan the string one byte at a time. - start := 0 - for i, c := range src { - if c == '<' || c == '>' || c == '&' { - if start < i { - dst.Write(src[start:i]) - } - dst.WriteString(`\u00`) - dst.WriteByte(hex[c>>4]) - dst.WriteByte(hex[c&0xF]) - start = i + 1 - } - } - if start < len(src) { - dst.Write(src[start:]) - } -} - -// Marshaler is the interface implemented by objects that -// can marshal themselves into valid JSON. -type Marshaler interface { - MarshalJSON() ([]byte, os.Error) -} - -type UnsupportedTypeError struct { - Type reflect.Type -} - -func (e *UnsupportedTypeError) String() string { - return "json: unsupported type: " + e.Type.String() -} - -type InvalidUTF8Error struct { - S string -} - -func (e *InvalidUTF8Error) String() string { - return "json: invalid UTF-8 in string: " + strconv.Quote(e.S) -} - -type MarshalerError struct { - Type reflect.Type - Error os.Error -} - -func (e *MarshalerError) String() string { - return "json: error calling MarshalJSON for type " + e.Type.String() + ": " + e.Error.String() -} - -type interfaceOrPtrValue interface { - IsNil() bool - Elem() reflect.Value -} - -var hex = "0123456789abcdef" - -// An encodeState encodes JSON into a bytes.Buffer. -type encodeState struct { - bytes.Buffer // accumulated output -} - -func (e *encodeState) marshal(v interface{}) (err os.Error) { - defer func() { - if r := recover(); r != nil { - if _, ok := r.(runtime.Error); ok { - panic(r) - } - err = r.(os.Error) - } - }() - e.reflectValue(reflect.ValueOf(v)) - return nil -} - -func (e *encodeState) error(err os.Error) { - panic(err) -} - -var byteSliceType = reflect.TypeOf([]byte(nil)) - -func (e *encodeState) reflectValue(v reflect.Value) { - if !v.IsValid() { - e.WriteString("null") - return - } - - if j, ok := v.Interface().(Marshaler); ok { - b, err := j.MarshalJSON() - if err == nil { - // copy JSON into buffer, checking validity. - err = Compact(&e.Buffer, b) - } - if err != nil { - e.error(&MarshalerError{v.Type(), err}) - } - return - } - - switch v.Kind() { - case reflect.Bool: - x := v.Bool() - if x { - e.WriteString("true") - } else { - e.WriteString("false") - } - - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - e.WriteString(strconv.Itoa64(v.Int())) - - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - e.WriteString(strconv.Uitoa64(v.Uint())) - - case reflect.Float32, reflect.Float64: - e.WriteString(strconv.FtoaN(v.Float(), 'g', -1, v.Type().Bits())) - - case reflect.String: - e.string(v.String()) - - case reflect.Struct: - e.WriteByte('{') - t := v.Type() - n := v.NumField() - first := true - for i := 0; i < n; i++ { - f := t.Field(i) - if f.PkgPath != "" { - continue - } - if first { - first = false - } else { - e.WriteByte(',') - } - if isValidTag(f.Tag) { - e.string(f.Tag) - } else { - e.string(f.Name) - } - e.WriteByte(':') - e.reflectValue(v.Field(i)) - } - e.WriteByte('}') - - case reflect.Map: - if v.Type().Key().Kind() != reflect.String { - e.error(&UnsupportedTypeError{v.Type()}) - } - if v.IsNil() { - e.WriteString("null") - break - } - e.WriteByte('{') - var sv stringValues = v.MapKeys() - sort.Sort(sv) - for i, k := range sv { - if i > 0 { - e.WriteByte(',') - } - e.string(k.String()) - e.WriteByte(':') - e.reflectValue(v.MapIndex(k)) - } - e.WriteByte('}') - - case reflect.Array, reflect.Slice: - if v.Type() == byteSliceType { - e.WriteByte('"') - s := v.Interface().([]byte) - if len(s) < 1024 { - // for small buffers, using Encode directly is much faster. - dst := make([]byte, base64.StdEncoding.EncodedLen(len(s))) - base64.StdEncoding.Encode(dst, s) - e.Write(dst) - } else { - // for large buffers, avoid unnecessary extra temporary - // buffer space. - enc := base64.NewEncoder(base64.StdEncoding, e) - enc.Write(s) - enc.Close() - } - e.WriteByte('"') - break - } - e.WriteByte('[') - n := v.Len() - for i := 0; i < n; i++ { - if i > 0 { - e.WriteByte(',') - } - e.reflectValue(v.Index(i)) - } - e.WriteByte(']') - - case interfaceOrPtrValue: - if v.IsNil() { - e.WriteString("null") - return - } - e.reflectValue(v.Elem()) - - default: - e.error(&UnsupportedTypeError{v.Type()}) - } - return -} - -func isValidTag(s string) bool { - if s == "" { - return false - } - for _, c := range s { - if c != '_' && !unicode.IsLetter(c) && !unicode.IsDigit(c) { - return false - } - } - return true -} - -// stringValues is a slice of reflect.Value holding *reflect.StringValue. -// It implements the methods to sort by string. -type stringValues []reflect.Value - -func (sv stringValues) Len() int { return len(sv) } -func (sv stringValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] } -func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) } -func (sv stringValues) get(i int) string { return sv[i].String() } - -func (e *encodeState) string(s string) { - e.WriteByte('"') - start := 0 - for i := 0; i < len(s); { - if b := s[i]; b < utf8.RuneSelf { - if 0x20 <= b && b != '\\' && b != '"' { - i++ - continue - } - if start < i { - e.WriteString(s[start:i]) - } - if b == '\\' || b == '"' { - e.WriteByte('\\') - e.WriteByte(b) - } else { - e.WriteString(`\u00`) - e.WriteByte(hex[b>>4]) - e.WriteByte(hex[b&0xF]) - } - i++ - start = i - continue - } - c, size := utf8.DecodeRuneInString(s[i:]) - if c == utf8.RuneError && size == 1 { - e.error(&InvalidUTF8Error{s}) - } - i += size - } - if start < len(s) { - e.WriteString(s[start:]) - } - e.WriteByte('"') -} diff --git a/src/cmd/fix/testdata/reflect.encoder.go.in b/src/cmd/fix/testdata/reflect.encoder.go.in deleted file mode 100644 index 702f6dc06..000000000 --- a/src/cmd/fix/testdata/reflect.encoder.go.in +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package gob - -import ( - "bytes" - "io" - "os" - "reflect" - "sync" -) - -// An Encoder manages the transmission of type and data information to the -// other side of a connection. -type Encoder struct { - mutex sync.Mutex // each item must be sent atomically - w []io.Writer // where to send the data - sent map[reflect.Type]typeId // which types we've already sent - countState *encoderState // stage for writing counts - freeList *encoderState // list of free encoderStates; avoids reallocation - buf []byte // for collecting the output. - byteBuf bytes.Buffer // buffer for top-level encoderState - err os.Error -} - -// NewEncoder returns a new encoder that will transmit on the io.Writer. -func NewEncoder(w io.Writer) *Encoder { - enc := new(Encoder) - enc.w = []io.Writer{w} - enc.sent = make(map[reflect.Type]typeId) - enc.countState = enc.newEncoderState(new(bytes.Buffer)) - return enc -} - -// writer() returns the innermost writer the encoder is using -func (enc *Encoder) writer() io.Writer { - return enc.w[len(enc.w)-1] -} - -// pushWriter adds a writer to the encoder. -func (enc *Encoder) pushWriter(w io.Writer) { - enc.w = append(enc.w, w) -} - -// popWriter pops the innermost writer. -func (enc *Encoder) popWriter() { - enc.w = enc.w[0 : len(enc.w)-1] -} - -func (enc *Encoder) badType(rt reflect.Type) { - enc.setError(os.NewError("gob: can't encode type " + rt.String())) -} - -func (enc *Encoder) setError(err os.Error) { - if enc.err == nil { // remember the first. - enc.err = err - } -} - -// writeMessage sends the data item preceded by a unsigned count of its length. -func (enc *Encoder) writeMessage(w io.Writer, b *bytes.Buffer) { - enc.countState.encodeUint(uint64(b.Len())) - // Build the buffer. - countLen := enc.countState.b.Len() - total := countLen + b.Len() - if total > len(enc.buf) { - enc.buf = make([]byte, total+1000) // extra for growth - } - // Place the length before the data. - // TODO(r): avoid the extra copy here. - enc.countState.b.Read(enc.buf[0:countLen]) - // Now the data. - b.Read(enc.buf[countLen:total]) - // Write the data. - _, err := w.Write(enc.buf[0:total]) - if err != nil { - enc.setError(err) - } -} - -// sendActualType sends the requested type, without further investigation, unless -// it's been sent before. -func (enc *Encoder) sendActualType(w io.Writer, state *encoderState, ut *userTypeInfo, actual reflect.Type) (sent bool) { - if _, alreadySent := enc.sent[actual]; alreadySent { - return false - } - typeLock.Lock() - info, err := getTypeInfo(ut) - typeLock.Unlock() - if err != nil { - enc.setError(err) - return - } - // Send the pair (-id, type) - // Id: - state.encodeInt(-int64(info.id)) - // Type: - enc.encode(state.b, reflect.NewValue(info.wire), wireTypeUserInfo) - enc.writeMessage(w, state.b) - if enc.err != nil { - return - } - - // Remember we've sent this type, both what the user gave us and the base type. - enc.sent[ut.base] = info.id - if ut.user != ut.base { - enc.sent[ut.user] = info.id - } - // Now send the inner types - switch st := actual.(type) { - case *reflect.StructType: - for i := 0; i < st.NumField(); i++ { - enc.sendType(w, state, st.Field(i).Type) - } - case reflect.ArrayOrSliceType: - enc.sendType(w, state, st.Elem()) - } - return true -} - -// sendType sends the type info to the other side, if necessary. -func (enc *Encoder) sendType(w io.Writer, state *encoderState, origt reflect.Type) (sent bool) { - ut := userType(origt) - if ut.isGobEncoder { - // The rules are different: regardless of the underlying type's representation, - // we need to tell the other side that this exact type is a GobEncoder. - return enc.sendActualType(w, state, ut, ut.user) - } - - // It's a concrete value, so drill down to the base type. - switch rt := ut.base.(type) { - default: - // Basic types and interfaces do not need to be described. - return - case *reflect.SliceType: - // If it's []uint8, don't send; it's considered basic. - if rt.Elem().Kind() == reflect.Uint8 { - return - } - // Otherwise we do send. - break - case *reflect.ArrayType: - // arrays must be sent so we know their lengths and element types. - break - case *reflect.MapType: - // maps must be sent so we know their lengths and key/value types. - break - case *reflect.StructType: - // structs must be sent so we know their fields. - break - case *reflect.ChanType, *reflect.FuncType: - // Probably a bad field in a struct. - enc.badType(rt) - return - } - - return enc.sendActualType(w, state, ut, ut.base) -} - -// Encode transmits the data item represented by the empty interface value, -// guaranteeing that all necessary type information has been transmitted first. -func (enc *Encoder) Encode(e interface{}) os.Error { - return enc.EncodeValue(reflect.NewValue(e)) -} - -// sendTypeDescriptor makes sure the remote side knows about this type. -// It will send a descriptor if this is the first time the type has been -// sent. -func (enc *Encoder) sendTypeDescriptor(w io.Writer, state *encoderState, ut *userTypeInfo) { - // Make sure the type is known to the other side. - // First, have we already sent this type? - rt := ut.base - if ut.isGobEncoder { - rt = ut.user - } - if _, alreadySent := enc.sent[rt]; !alreadySent { - // No, so send it. - sent := enc.sendType(w, state, rt) - if enc.err != nil { - return - } - // If the type info has still not been transmitted, it means we have - // a singleton basic type (int, []byte etc.) at top level. We don't - // need to send the type info but we do need to update enc.sent. - if !sent { - typeLock.Lock() - info, err := getTypeInfo(ut) - typeLock.Unlock() - if err != nil { - enc.setError(err) - return - } - enc.sent[rt] = info.id - } - } -} - -// sendTypeId sends the id, which must have already been defined. -func (enc *Encoder) sendTypeId(state *encoderState, ut *userTypeInfo) { - // Identify the type of this top-level value. - state.encodeInt(int64(enc.sent[ut.base])) -} - -// EncodeValue transmits the data item represented by the reflection value, -// guaranteeing that all necessary type information has been transmitted first. -func (enc *Encoder) EncodeValue(value reflect.Value) os.Error { - // Make sure we're single-threaded through here, so multiple - // goroutines can share an encoder. - enc.mutex.Lock() - defer enc.mutex.Unlock() - - // Remove any nested writers remaining due to previous errors. - enc.w = enc.w[0:1] - - ut, err := validUserType(value.Type()) - if err != nil { - return err - } - - enc.err = nil - enc.byteBuf.Reset() - state := enc.newEncoderState(&enc.byteBuf) - - enc.sendTypeDescriptor(enc.writer(), state, ut) - enc.sendTypeId(state, ut) - if enc.err != nil { - return enc.err - } - - // Encode the object. - enc.encode(state.b, value, ut) - if enc.err == nil { - enc.writeMessage(enc.writer(), state.b) - } - - enc.freeEncoderState(state) - return enc.err -} diff --git a/src/cmd/fix/testdata/reflect.encoder.go.out b/src/cmd/fix/testdata/reflect.encoder.go.out deleted file mode 100644 index f1a7b98f1..000000000 --- a/src/cmd/fix/testdata/reflect.encoder.go.out +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package gob - -import ( - "bytes" - "io" - "os" - "reflect" - "sync" -) - -// An Encoder manages the transmission of type and data information to the -// other side of a connection. -type Encoder struct { - mutex sync.Mutex // each item must be sent atomically - w []io.Writer // where to send the data - sent map[reflect.Type]typeId // which types we've already sent - countState *encoderState // stage for writing counts - freeList *encoderState // list of free encoderStates; avoids reallocation - buf []byte // for collecting the output. - byteBuf bytes.Buffer // buffer for top-level encoderState - err os.Error -} - -// NewEncoder returns a new encoder that will transmit on the io.Writer. -func NewEncoder(w io.Writer) *Encoder { - enc := new(Encoder) - enc.w = []io.Writer{w} - enc.sent = make(map[reflect.Type]typeId) - enc.countState = enc.newEncoderState(new(bytes.Buffer)) - return enc -} - -// writer() returns the innermost writer the encoder is using -func (enc *Encoder) writer() io.Writer { - return enc.w[len(enc.w)-1] -} - -// pushWriter adds a writer to the encoder. -func (enc *Encoder) pushWriter(w io.Writer) { - enc.w = append(enc.w, w) -} - -// popWriter pops the innermost writer. -func (enc *Encoder) popWriter() { - enc.w = enc.w[0 : len(enc.w)-1] -} - -func (enc *Encoder) badType(rt reflect.Type) { - enc.setError(os.NewError("gob: can't encode type " + rt.String())) -} - -func (enc *Encoder) setError(err os.Error) { - if enc.err == nil { // remember the first. - enc.err = err - } -} - -// writeMessage sends the data item preceded by a unsigned count of its length. -func (enc *Encoder) writeMessage(w io.Writer, b *bytes.Buffer) { - enc.countState.encodeUint(uint64(b.Len())) - // Build the buffer. - countLen := enc.countState.b.Len() - total := countLen + b.Len() - if total > len(enc.buf) { - enc.buf = make([]byte, total+1000) // extra for growth - } - // Place the length before the data. - // TODO(r): avoid the extra copy here. - enc.countState.b.Read(enc.buf[0:countLen]) - // Now the data. - b.Read(enc.buf[countLen:total]) - // Write the data. - _, err := w.Write(enc.buf[0:total]) - if err != nil { - enc.setError(err) - } -} - -// sendActualType sends the requested type, without further investigation, unless -// it's been sent before. -func (enc *Encoder) sendActualType(w io.Writer, state *encoderState, ut *userTypeInfo, actual reflect.Type) (sent bool) { - if _, alreadySent := enc.sent[actual]; alreadySent { - return false - } - typeLock.Lock() - info, err := getTypeInfo(ut) - typeLock.Unlock() - if err != nil { - enc.setError(err) - return - } - // Send the pair (-id, type) - // Id: - state.encodeInt(-int64(info.id)) - // Type: - enc.encode(state.b, reflect.ValueOf(info.wire), wireTypeUserInfo) - enc.writeMessage(w, state.b) - if enc.err != nil { - return - } - - // Remember we've sent this type, both what the user gave us and the base type. - enc.sent[ut.base] = info.id - if ut.user != ut.base { - enc.sent[ut.user] = info.id - } - // Now send the inner types - switch st := actual; st.Kind() { - case reflect.Struct: - for i := 0; i < st.NumField(); i++ { - enc.sendType(w, state, st.Field(i).Type) - } - case reflect.Array, reflect.Slice: - enc.sendType(w, state, st.Elem()) - } - return true -} - -// sendType sends the type info to the other side, if necessary. -func (enc *Encoder) sendType(w io.Writer, state *encoderState, origt reflect.Type) (sent bool) { - ut := userType(origt) - if ut.isGobEncoder { - // The rules are different: regardless of the underlying type's representation, - // we need to tell the other side that this exact type is a GobEncoder. - return enc.sendActualType(w, state, ut, ut.user) - } - - // It's a concrete value, so drill down to the base type. - switch rt := ut.base; rt.Kind() { - default: - // Basic types and interfaces do not need to be described. - return - case reflect.Slice: - // If it's []uint8, don't send; it's considered basic. - if rt.Elem().Kind() == reflect.Uint8 { - return - } - // Otherwise we do send. - break - case reflect.Array: - // arrays must be sent so we know their lengths and element types. - break - case reflect.Map: - // maps must be sent so we know their lengths and key/value types. - break - case reflect.Struct: - // structs must be sent so we know their fields. - break - case reflect.Chan, reflect.Func: - // Probably a bad field in a struct. - enc.badType(rt) - return - } - - return enc.sendActualType(w, state, ut, ut.base) -} - -// Encode transmits the data item represented by the empty interface value, -// guaranteeing that all necessary type information has been transmitted first. -func (enc *Encoder) Encode(e interface{}) os.Error { - return enc.EncodeValue(reflect.ValueOf(e)) -} - -// sendTypeDescriptor makes sure the remote side knows about this type. -// It will send a descriptor if this is the first time the type has been -// sent. -func (enc *Encoder) sendTypeDescriptor(w io.Writer, state *encoderState, ut *userTypeInfo) { - // Make sure the type is known to the other side. - // First, have we already sent this type? - rt := ut.base - if ut.isGobEncoder { - rt = ut.user - } - if _, alreadySent := enc.sent[rt]; !alreadySent { - // No, so send it. - sent := enc.sendType(w, state, rt) - if enc.err != nil { - return - } - // If the type info has still not been transmitted, it means we have - // a singleton basic type (int, []byte etc.) at top level. We don't - // need to send the type info but we do need to update enc.sent. - if !sent { - typeLock.Lock() - info, err := getTypeInfo(ut) - typeLock.Unlock() - if err != nil { - enc.setError(err) - return - } - enc.sent[rt] = info.id - } - } -} - -// sendTypeId sends the id, which must have already been defined. -func (enc *Encoder) sendTypeId(state *encoderState, ut *userTypeInfo) { - // Identify the type of this top-level value. - state.encodeInt(int64(enc.sent[ut.base])) -} - -// EncodeValue transmits the data item represented by the reflection value, -// guaranteeing that all necessary type information has been transmitted first. -func (enc *Encoder) EncodeValue(value reflect.Value) os.Error { - // Make sure we're single-threaded through here, so multiple - // goroutines can share an encoder. - enc.mutex.Lock() - defer enc.mutex.Unlock() - - // Remove any nested writers remaining due to previous errors. - enc.w = enc.w[0:1] - - ut, err := validUserType(value.Type()) - if err != nil { - return err - } - - enc.err = nil - enc.byteBuf.Reset() - state := enc.newEncoderState(&enc.byteBuf) - - enc.sendTypeDescriptor(enc.writer(), state, ut) - enc.sendTypeId(state, ut) - if enc.err != nil { - return enc.err - } - - // Encode the object. - enc.encode(state.b, value, ut) - if enc.err == nil { - enc.writeMessage(enc.writer(), state.b) - } - - enc.freeEncoderState(state) - return enc.err -} diff --git a/src/cmd/fix/testdata/reflect.export.go.in b/src/cmd/fix/testdata/reflect.export.go.in deleted file mode 100644 index 722387ac5..000000000 --- a/src/cmd/fix/testdata/reflect.export.go.in +++ /dev/null @@ -1,400 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* - The netchan package implements type-safe networked channels: - it allows the two ends of a channel to appear on different - computers connected by a network. It does this by transporting - data sent to a channel on one machine so it can be recovered - by a receive of a channel of the same type on the other. - - An exporter publishes a set of channels by name. An importer - connects to the exporting machine and imports the channels - by name. After importing the channels, the two machines can - use the channels in the usual way. - - Networked channels are not synchronized; they always behave - as if they are buffered channels of at least one element. -*/ -package netchan - -// BUG: can't use range clause to receive when using ImportNValues to limit the count. - -import ( - "io" - "log" - "net" - "os" - "reflect" - "strconv" - "sync" -) - -// Export - -// expLog is a logging convenience function. The first argument must be a string. -func expLog(args ...interface{}) { - args[0] = "netchan export: " + args[0].(string) - log.Print(args...) -} - -// An Exporter allows a set of channels to be published on a single -// network port. A single machine may have multiple Exporters -// but they must use different ports. -type Exporter struct { - *clientSet -} - -type expClient struct { - *encDec - exp *Exporter - chans map[int]*netChan // channels in use by client - mu sync.Mutex // protects remaining fields - errored bool // client has been sent an error - seqNum int64 // sequences messages sent to client; has value of highest sent - ackNum int64 // highest sequence number acknowledged - seqLock sync.Mutex // guarantees messages are in sequence, only locked under mu -} - -func newClient(exp *Exporter, conn io.ReadWriter) *expClient { - client := new(expClient) - client.exp = exp - client.encDec = newEncDec(conn) - client.seqNum = 0 - client.ackNum = 0 - client.chans = make(map[int]*netChan) - return client -} - -func (client *expClient) sendError(hdr *header, err string) { - error := &error{err} - expLog("sending error to client:", error.Error) - client.encode(hdr, payError, error) // ignore any encode error, hope client gets it - client.mu.Lock() - client.errored = true - client.mu.Unlock() -} - -func (client *expClient) newChan(hdr *header, dir Dir, name string, size int, count int64) *netChan { - exp := client.exp - exp.mu.Lock() - ech, ok := exp.names[name] - exp.mu.Unlock() - if !ok { - client.sendError(hdr, "no such channel: "+name) - return nil - } - if ech.dir != dir { - client.sendError(hdr, "wrong direction for channel: "+name) - return nil - } - nch := newNetChan(name, hdr.Id, ech, client.encDec, size, count) - client.chans[hdr.Id] = nch - return nch -} - -func (client *expClient) getChan(hdr *header, dir Dir) *netChan { - nch := client.chans[hdr.Id] - if nch == nil { - return nil - } - if nch.dir != dir { - client.sendError(hdr, "wrong direction for channel: "+nch.name) - } - return nch -} - -// The function run manages sends and receives for a single client. For each -// (client Recv) request, this will launch a serveRecv goroutine to deliver -// the data for that channel, while (client Send) requests are handled as -// data arrives from the client. -func (client *expClient) run() { - hdr := new(header) - hdrValue := reflect.NewValue(hdr) - req := new(request) - reqValue := reflect.NewValue(req) - error := new(error) - for { - *hdr = header{} - if err := client.decode(hdrValue); err != nil { - if err != os.EOF { - expLog("error decoding client header:", err) - } - break - } - switch hdr.PayloadType { - case payRequest: - *req = request{} - if err := client.decode(reqValue); err != nil { - expLog("error decoding client request:", err) - break - } - if req.Size < 1 { - panic("netchan: remote requested " + strconv.Itoa(req.Size) + " values") - } - switch req.Dir { - case Recv: - // look up channel before calling serveRecv to - // avoid a lock around client.chans. - if nch := client.newChan(hdr, Send, req.Name, req.Size, req.Count); nch != nil { - go client.serveRecv(nch, *hdr, req.Count) - } - case Send: - client.newChan(hdr, Recv, req.Name, req.Size, req.Count) - // The actual sends will have payload type payData. - // TODO: manage the count? - default: - error.Error = "request: can't handle channel direction" - expLog(error.Error, req.Dir) - client.encode(hdr, payError, error) - } - case payData: - client.serveSend(*hdr) - case payClosed: - client.serveClosed(*hdr) - case payAck: - client.mu.Lock() - if client.ackNum != hdr.SeqNum-1 { - // Since the sequence number is incremented and the message is sent - // in a single instance of locking client.mu, the messages are guaranteed - // to be sent in order. Therefore receipt of acknowledgement N means - // all messages <=N have been seen by the recipient. We check anyway. - expLog("sequence out of order:", client.ackNum, hdr.SeqNum) - } - if client.ackNum < hdr.SeqNum { // If there has been an error, don't back up the count. - client.ackNum = hdr.SeqNum - } - client.mu.Unlock() - case payAckSend: - if nch := client.getChan(hdr, Send); nch != nil { - nch.acked() - } - default: - log.Fatal("netchan export: unknown payload type", hdr.PayloadType) - } - } - client.exp.delClient(client) -} - -// Send all the data on a single channel to a client asking for a Recv. -// The header is passed by value to avoid issues of overwriting. -func (client *expClient) serveRecv(nch *netChan, hdr header, count int64) { - for { - val, ok := nch.recv() - if !ok { - if err := client.encode(&hdr, payClosed, nil); err != nil { - expLog("error encoding server closed message:", err) - } - break - } - // We hold the lock during transmission to guarantee messages are - // sent in sequence number order. Also, we increment first so the - // value of client.SeqNum is the value of the highest used sequence - // number, not one beyond. - client.mu.Lock() - client.seqNum++ - hdr.SeqNum = client.seqNum - client.seqLock.Lock() // guarantee ordering of messages - client.mu.Unlock() - err := client.encode(&hdr, payData, val.Interface()) - client.seqLock.Unlock() - if err != nil { - expLog("error encoding client response:", err) - client.sendError(&hdr, err.String()) - break - } - // Negative count means run forever. - if count >= 0 { - if count--; count <= 0 { - break - } - } - } -} - -// Receive and deliver locally one item from a client asking for a Send -// The header is passed by value to avoid issues of overwriting. -func (client *expClient) serveSend(hdr header) { - nch := client.getChan(&hdr, Recv) - if nch == nil { - return - } - // Create a new value for each received item. - val := reflect.MakeZero(nch.ch.Type().(*reflect.ChanType).Elem()) - if err := client.decode(val); err != nil { - expLog("value decode:", err, "; type ", nch.ch.Type()) - return - } - nch.send(val) -} - -// Report that client has closed the channel that is sending to us. -// The header is passed by value to avoid issues of overwriting. -func (client *expClient) serveClosed(hdr header) { - nch := client.getChan(&hdr, Recv) - if nch == nil { - return - } - nch.close() -} - -func (client *expClient) unackedCount() int64 { - client.mu.Lock() - n := client.seqNum - client.ackNum - client.mu.Unlock() - return n -} - -func (client *expClient) seq() int64 { - client.mu.Lock() - n := client.seqNum - client.mu.Unlock() - return n -} - -func (client *expClient) ack() int64 { - client.mu.Lock() - n := client.seqNum - client.mu.Unlock() - return n -} - -// Serve waits for incoming connections on the listener -// and serves the Exporter's channels on each. -// It blocks until the listener is closed. -func (exp *Exporter) Serve(listener net.Listener) { - for { - conn, err := listener.Accept() - if err != nil { - expLog("listen:", err) - break - } - go exp.ServeConn(conn) - } -} - -// ServeConn exports the Exporter's channels on conn. -// It blocks until the connection is terminated. -func (exp *Exporter) ServeConn(conn io.ReadWriter) { - exp.addClient(conn).run() -} - -// NewExporter creates a new Exporter that exports a set of channels. -func NewExporter() *Exporter { - e := &Exporter{ - clientSet: &clientSet{ - names: make(map[string]*chanDir), - clients: make(map[unackedCounter]bool), - }, - } - return e -} - -// ListenAndServe exports the exporter's channels through the -// given network and local address defined as in net.Listen. -func (exp *Exporter) ListenAndServe(network, localaddr string) os.Error { - listener, err := net.Listen(network, localaddr) - if err != nil { - return err - } - go exp.Serve(listener) - return nil -} - -// addClient creates a new expClient and records its existence -func (exp *Exporter) addClient(conn io.ReadWriter) *expClient { - client := newClient(exp, conn) - exp.mu.Lock() - exp.clients[client] = true - exp.mu.Unlock() - return client -} - -// delClient forgets the client existed -func (exp *Exporter) delClient(client *expClient) { - exp.mu.Lock() - exp.clients[client] = false, false - exp.mu.Unlock() -} - -// Drain waits until all messages sent from this exporter/importer, including -// those not yet sent to any client and possibly including those sent while -// Drain was executing, have been received by the importer. In short, it -// waits until all the exporter's messages have been received by a client. -// If the timeout (measured in nanoseconds) is positive and Drain takes -// longer than that to complete, an error is returned. -func (exp *Exporter) Drain(timeout int64) os.Error { - // This wrapper function is here so the method's comment will appear in godoc. - return exp.clientSet.drain(timeout) -} - -// Sync waits until all clients of the exporter have received the messages -// that were sent at the time Sync was invoked. Unlike Drain, it does not -// wait for messages sent while it is running or messages that have not been -// dispatched to any client. If the timeout (measured in nanoseconds) is -// positive and Sync takes longer than that to complete, an error is -// returned. -func (exp *Exporter) Sync(timeout int64) os.Error { - // This wrapper function is here so the method's comment will appear in godoc. - return exp.clientSet.sync(timeout) -} - -func checkChan(chT interface{}, dir Dir) (*reflect.ChanValue, os.Error) { - chanType, ok := reflect.Typeof(chT).(*reflect.ChanType) - if !ok { - return nil, os.NewError("not a channel") - } - if dir != Send && dir != Recv { - return nil, os.NewError("unknown channel direction") - } - switch chanType.Dir() { - case reflect.BothDir: - case reflect.SendDir: - if dir != Recv { - return nil, os.NewError("to import/export with Send, must provide <-chan") - } - case reflect.RecvDir: - if dir != Send { - return nil, os.NewError("to import/export with Recv, must provide chan<-") - } - } - return reflect.NewValue(chT).(*reflect.ChanValue), nil -} - -// Export exports a channel of a given type and specified direction. The -// channel to be exported is provided in the call and may be of arbitrary -// channel type. -// Despite the literal signature, the effective signature is -// Export(name string, chT chan T, dir Dir) -func (exp *Exporter) Export(name string, chT interface{}, dir Dir) os.Error { - ch, err := checkChan(chT, dir) - if err != nil { - return err - } - exp.mu.Lock() - defer exp.mu.Unlock() - _, present := exp.names[name] - if present { - return os.NewError("channel name already being exported:" + name) - } - exp.names[name] = &chanDir{ch, dir} - return nil -} - -// Hangup disassociates the named channel from the Exporter and closes -// the channel. Messages in flight for the channel may be dropped. -func (exp *Exporter) Hangup(name string) os.Error { - exp.mu.Lock() - chDir, ok := exp.names[name] - if ok { - exp.names[name] = nil, false - } - // TODO drop all instances of channel from client sets - exp.mu.Unlock() - if !ok { - return os.NewError("netchan export: hangup: no such channel: " + name) - } - chDir.ch.Close() - return nil -} diff --git a/src/cmd/fix/testdata/reflect.export.go.out b/src/cmd/fix/testdata/reflect.export.go.out deleted file mode 100644 index d1324f346..000000000 --- a/src/cmd/fix/testdata/reflect.export.go.out +++ /dev/null @@ -1,400 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* - The netchan package implements type-safe networked channels: - it allows the two ends of a channel to appear on different - computers connected by a network. It does this by transporting - data sent to a channel on one machine so it can be recovered - by a receive of a channel of the same type on the other. - - An exporter publishes a set of channels by name. An importer - connects to the exporting machine and imports the channels - by name. After importing the channels, the two machines can - use the channels in the usual way. - - Networked channels are not synchronized; they always behave - as if they are buffered channels of at least one element. -*/ -package netchan - -// BUG: can't use range clause to receive when using ImportNValues to limit the count. - -import ( - "io" - "log" - "net" - "os" - "reflect" - "strconv" - "sync" -) - -// Export - -// expLog is a logging convenience function. The first argument must be a string. -func expLog(args ...interface{}) { - args[0] = "netchan export: " + args[0].(string) - log.Print(args...) -} - -// An Exporter allows a set of channels to be published on a single -// network port. A single machine may have multiple Exporters -// but they must use different ports. -type Exporter struct { - *clientSet -} - -type expClient struct { - *encDec - exp *Exporter - chans map[int]*netChan // channels in use by client - mu sync.Mutex // protects remaining fields - errored bool // client has been sent an error - seqNum int64 // sequences messages sent to client; has value of highest sent - ackNum int64 // highest sequence number acknowledged - seqLock sync.Mutex // guarantees messages are in sequence, only locked under mu -} - -func newClient(exp *Exporter, conn io.ReadWriter) *expClient { - client := new(expClient) - client.exp = exp - client.encDec = newEncDec(conn) - client.seqNum = 0 - client.ackNum = 0 - client.chans = make(map[int]*netChan) - return client -} - -func (client *expClient) sendError(hdr *header, err string) { - error := &error{err} - expLog("sending error to client:", error.Error) - client.encode(hdr, payError, error) // ignore any encode error, hope client gets it - client.mu.Lock() - client.errored = true - client.mu.Unlock() -} - -func (client *expClient) newChan(hdr *header, dir Dir, name string, size int, count int64) *netChan { - exp := client.exp - exp.mu.Lock() - ech, ok := exp.names[name] - exp.mu.Unlock() - if !ok { - client.sendError(hdr, "no such channel: "+name) - return nil - } - if ech.dir != dir { - client.sendError(hdr, "wrong direction for channel: "+name) - return nil - } - nch := newNetChan(name, hdr.Id, ech, client.encDec, size, count) - client.chans[hdr.Id] = nch - return nch -} - -func (client *expClient) getChan(hdr *header, dir Dir) *netChan { - nch := client.chans[hdr.Id] - if nch == nil { - return nil - } - if nch.dir != dir { - client.sendError(hdr, "wrong direction for channel: "+nch.name) - } - return nch -} - -// The function run manages sends and receives for a single client. For each -// (client Recv) request, this will launch a serveRecv goroutine to deliver -// the data for that channel, while (client Send) requests are handled as -// data arrives from the client. -func (client *expClient) run() { - hdr := new(header) - hdrValue := reflect.ValueOf(hdr) - req := new(request) - reqValue := reflect.ValueOf(req) - error := new(error) - for { - *hdr = header{} - if err := client.decode(hdrValue); err != nil { - if err != os.EOF { - expLog("error decoding client header:", err) - } - break - } - switch hdr.PayloadType { - case payRequest: - *req = request{} - if err := client.decode(reqValue); err != nil { - expLog("error decoding client request:", err) - break - } - if req.Size < 1 { - panic("netchan: remote requested " + strconv.Itoa(req.Size) + " values") - } - switch req.Dir { - case Recv: - // look up channel before calling serveRecv to - // avoid a lock around client.chans. - if nch := client.newChan(hdr, Send, req.Name, req.Size, req.Count); nch != nil { - go client.serveRecv(nch, *hdr, req.Count) - } - case Send: - client.newChan(hdr, Recv, req.Name, req.Size, req.Count) - // The actual sends will have payload type payData. - // TODO: manage the count? - default: - error.Error = "request: can't handle channel direction" - expLog(error.Error, req.Dir) - client.encode(hdr, payError, error) - } - case payData: - client.serveSend(*hdr) - case payClosed: - client.serveClosed(*hdr) - case payAck: - client.mu.Lock() - if client.ackNum != hdr.SeqNum-1 { - // Since the sequence number is incremented and the message is sent - // in a single instance of locking client.mu, the messages are guaranteed - // to be sent in order. Therefore receipt of acknowledgement N means - // all messages <=N have been seen by the recipient. We check anyway. - expLog("sequence out of order:", client.ackNum, hdr.SeqNum) - } - if client.ackNum < hdr.SeqNum { // If there has been an error, don't back up the count. - client.ackNum = hdr.SeqNum - } - client.mu.Unlock() - case payAckSend: - if nch := client.getChan(hdr, Send); nch != nil { - nch.acked() - } - default: - log.Fatal("netchan export: unknown payload type", hdr.PayloadType) - } - } - client.exp.delClient(client) -} - -// Send all the data on a single channel to a client asking for a Recv. -// The header is passed by value to avoid issues of overwriting. -func (client *expClient) serveRecv(nch *netChan, hdr header, count int64) { - for { - val, ok := nch.recv() - if !ok { - if err := client.encode(&hdr, payClosed, nil); err != nil { - expLog("error encoding server closed message:", err) - } - break - } - // We hold the lock during transmission to guarantee messages are - // sent in sequence number order. Also, we increment first so the - // value of client.SeqNum is the value of the highest used sequence - // number, not one beyond. - client.mu.Lock() - client.seqNum++ - hdr.SeqNum = client.seqNum - client.seqLock.Lock() // guarantee ordering of messages - client.mu.Unlock() - err := client.encode(&hdr, payData, val.Interface()) - client.seqLock.Unlock() - if err != nil { - expLog("error encoding client response:", err) - client.sendError(&hdr, err.String()) - break - } - // Negative count means run forever. - if count >= 0 { - if count--; count <= 0 { - break - } - } - } -} - -// Receive and deliver locally one item from a client asking for a Send -// The header is passed by value to avoid issues of overwriting. -func (client *expClient) serveSend(hdr header) { - nch := client.getChan(&hdr, Recv) - if nch == nil { - return - } - // Create a new value for each received item. - val := reflect.Zero(nch.ch.Type().Elem()) - if err := client.decode(val); err != nil { - expLog("value decode:", err, "; type ", nch.ch.Type()) - return - } - nch.send(val) -} - -// Report that client has closed the channel that is sending to us. -// The header is passed by value to avoid issues of overwriting. -func (client *expClient) serveClosed(hdr header) { - nch := client.getChan(&hdr, Recv) - if nch == nil { - return - } - nch.close() -} - -func (client *expClient) unackedCount() int64 { - client.mu.Lock() - n := client.seqNum - client.ackNum - client.mu.Unlock() - return n -} - -func (client *expClient) seq() int64 { - client.mu.Lock() - n := client.seqNum - client.mu.Unlock() - return n -} - -func (client *expClient) ack() int64 { - client.mu.Lock() - n := client.seqNum - client.mu.Unlock() - return n -} - -// Serve waits for incoming connections on the listener -// and serves the Exporter's channels on each. -// It blocks until the listener is closed. -func (exp *Exporter) Serve(listener net.Listener) { - for { - conn, err := listener.Accept() - if err != nil { - expLog("listen:", err) - break - } - go exp.ServeConn(conn) - } -} - -// ServeConn exports the Exporter's channels on conn. -// It blocks until the connection is terminated. -func (exp *Exporter) ServeConn(conn io.ReadWriter) { - exp.addClient(conn).run() -} - -// NewExporter creates a new Exporter that exports a set of channels. -func NewExporter() *Exporter { - e := &Exporter{ - clientSet: &clientSet{ - names: make(map[string]*chanDir), - clients: make(map[unackedCounter]bool), - }, - } - return e -} - -// ListenAndServe exports the exporter's channels through the -// given network and local address defined as in net.Listen. -func (exp *Exporter) ListenAndServe(network, localaddr string) os.Error { - listener, err := net.Listen(network, localaddr) - if err != nil { - return err - } - go exp.Serve(listener) - return nil -} - -// addClient creates a new expClient and records its existence -func (exp *Exporter) addClient(conn io.ReadWriter) *expClient { - client := newClient(exp, conn) - exp.mu.Lock() - exp.clients[client] = true - exp.mu.Unlock() - return client -} - -// delClient forgets the client existed -func (exp *Exporter) delClient(client *expClient) { - exp.mu.Lock() - exp.clients[client] = false, false - exp.mu.Unlock() -} - -// Drain waits until all messages sent from this exporter/importer, including -// those not yet sent to any client and possibly including those sent while -// Drain was executing, have been received by the importer. In short, it -// waits until all the exporter's messages have been received by a client. -// If the timeout (measured in nanoseconds) is positive and Drain takes -// longer than that to complete, an error is returned. -func (exp *Exporter) Drain(timeout int64) os.Error { - // This wrapper function is here so the method's comment will appear in godoc. - return exp.clientSet.drain(timeout) -} - -// Sync waits until all clients of the exporter have received the messages -// that were sent at the time Sync was invoked. Unlike Drain, it does not -// wait for messages sent while it is running or messages that have not been -// dispatched to any client. If the timeout (measured in nanoseconds) is -// positive and Sync takes longer than that to complete, an error is -// returned. -func (exp *Exporter) Sync(timeout int64) os.Error { - // This wrapper function is here so the method's comment will appear in godoc. - return exp.clientSet.sync(timeout) -} - -func checkChan(chT interface{}, dir Dir) (reflect.Value, os.Error) { - chanType := reflect.TypeOf(chT) - if chanType.Kind() != reflect.Chan { - return reflect.Value{}, os.NewError("not a channel") - } - if dir != Send && dir != Recv { - return reflect.Value{}, os.NewError("unknown channel direction") - } - switch chanType.ChanDir() { - case reflect.BothDir: - case reflect.SendDir: - if dir != Recv { - return reflect.Value{}, os.NewError("to import/export with Send, must provide <-chan") - } - case reflect.RecvDir: - if dir != Send { - return reflect.Value{}, os.NewError("to import/export with Recv, must provide chan<-") - } - } - return reflect.ValueOf(chT), nil -} - -// Export exports a channel of a given type and specified direction. The -// channel to be exported is provided in the call and may be of arbitrary -// channel type. -// Despite the literal signature, the effective signature is -// Export(name string, chT chan T, dir Dir) -func (exp *Exporter) Export(name string, chT interface{}, dir Dir) os.Error { - ch, err := checkChan(chT, dir) - if err != nil { - return err - } - exp.mu.Lock() - defer exp.mu.Unlock() - _, present := exp.names[name] - if present { - return os.NewError("channel name already being exported:" + name) - } - exp.names[name] = &chanDir{ch, dir} - return nil -} - -// Hangup disassociates the named channel from the Exporter and closes -// the channel. Messages in flight for the channel may be dropped. -func (exp *Exporter) Hangup(name string) os.Error { - exp.mu.Lock() - chDir, ok := exp.names[name] - if ok { - exp.names[name] = nil, false - } - // TODO drop all instances of channel from client sets - exp.mu.Unlock() - if !ok { - return os.NewError("netchan export: hangup: no such channel: " + name) - } - chDir.ch.Close() - return nil -} diff --git a/src/cmd/fix/testdata/reflect.print.go.in b/src/cmd/fix/testdata/reflect.print.go.in deleted file mode 100644 index 14cf2b215..000000000 --- a/src/cmd/fix/testdata/reflect.print.go.in +++ /dev/null @@ -1,944 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package fmt - -import ( - "bytes" - "io" - "os" - "reflect" - "utf8" -) - -// Some constants in the form of bytes, to avoid string overhead. -// Needlessly fastidious, I suppose. -var ( - commaSpaceBytes = []byte(", ") - nilAngleBytes = []byte("<nil>") - nilParenBytes = []byte("(nil)") - nilBytes = []byte("nil") - mapBytes = []byte("map[") - missingBytes = []byte("(MISSING)") - extraBytes = []byte("%!(EXTRA ") - irparenBytes = []byte("i)") - bytesBytes = []byte("[]byte{") - widthBytes = []byte("%!(BADWIDTH)") - precBytes = []byte("%!(BADPREC)") - noVerbBytes = []byte("%!(NOVERB)") -) - -// State represents the printer state passed to custom formatters. -// It provides access to the io.Writer interface plus information about -// the flags and options for the operand's format specifier. -type State interface { - // Write is the function to call to emit formatted output to be printed. - Write(b []byte) (ret int, err os.Error) - // Width returns the value of the width option and whether it has been set. - Width() (wid int, ok bool) - // Precision returns the value of the precision option and whether it has been set. - Precision() (prec int, ok bool) - - // Flag returns whether the flag c, a character, has been set. - Flag(int) bool -} - -// Formatter is the interface implemented by values with a custom formatter. -// The implementation of Format may call Sprintf or Fprintf(f) etc. -// to generate its output. -type Formatter interface { - Format(f State, c int) -} - -// Stringer is implemented by any value that has a String method(), -// which defines the ``native'' format for that value. -// The String method is used to print values passed as an operand -// to a %s or %v format or to an unformatted printer such as Print. -type Stringer interface { - String() string -} - -// GoStringer is implemented by any value that has a GoString() method, -// which defines the Go syntax for that value. -// The GoString method is used to print values passed as an operand -// to a %#v format. -type GoStringer interface { - GoString() string -} - -type pp struct { - n int - buf bytes.Buffer - runeBuf [utf8.UTFMax]byte - fmt fmt -} - -// A cache holds a set of reusable objects. -// The buffered channel holds the currently available objects. -// If more are needed, the cache creates them by calling new. -type cache struct { - saved chan interface{} - new func() interface{} -} - -func (c *cache) put(x interface{}) { - select { - case c.saved <- x: - // saved in cache - default: - // discard - } -} - -func (c *cache) get() interface{} { - select { - case x := <-c.saved: - return x // reused from cache - default: - return c.new() - } - panic("not reached") -} - -func newCache(f func() interface{}) *cache { - return &cache{make(chan interface{}, 100), f} -} - -var ppFree = newCache(func() interface{} { return new(pp) }) - -// Allocate a new pp struct or grab a cached one. -func newPrinter() *pp { - p := ppFree.get().(*pp) - p.fmt.init(&p.buf) - return p -} - -// Save used pp structs in ppFree; avoids an allocation per invocation. -func (p *pp) free() { - // Don't hold on to pp structs with large buffers. - if cap(p.buf.Bytes()) > 1024 { - return - } - p.buf.Reset() - ppFree.put(p) -} - -func (p *pp) Width() (wid int, ok bool) { return p.fmt.wid, p.fmt.widPresent } - -func (p *pp) Precision() (prec int, ok bool) { return p.fmt.prec, p.fmt.precPresent } - -func (p *pp) Flag(b int) bool { - switch b { - case '-': - return p.fmt.minus - case '+': - return p.fmt.plus - case '#': - return p.fmt.sharp - case ' ': - return p.fmt.space - case '0': - return p.fmt.zero - } - return false -} - -func (p *pp) add(c int) { - p.buf.WriteRune(c) -} - -// Implement Write so we can call Fprintf on a pp (through State), for -// recursive use in custom verbs. -func (p *pp) Write(b []byte) (ret int, err os.Error) { - return p.buf.Write(b) -} - -// These routines end in 'f' and take a format string. - -// Fprintf formats according to a format specifier and writes to w. -// It returns the number of bytes written and any write error encountered. -func Fprintf(w io.Writer, format string, a ...interface{}) (n int, error os.Error) { - p := newPrinter() - p.doPrintf(format, a) - n64, error := p.buf.WriteTo(w) - p.free() - return int(n64), error -} - -// Printf formats according to a format specifier and writes to standard output. -// It returns the number of bytes written and any write error encountered. -func Printf(format string, a ...interface{}) (n int, errno os.Error) { - n, errno = Fprintf(os.Stdout, format, a...) - return n, errno -} - -// Sprintf formats according to a format specifier and returns the resulting string. -func Sprintf(format string, a ...interface{}) string { - p := newPrinter() - p.doPrintf(format, a) - s := p.buf.String() - p.free() - return s -} - -// Errorf formats according to a format specifier and returns the string -// converted to an os.ErrorString, which satisfies the os.Error interface. -func Errorf(format string, a ...interface{}) os.Error { - return os.NewError(Sprintf(format, a...)) -} - -// These routines do not take a format string - -// Fprint formats using the default formats for its operands and writes to w. -// Spaces are added between operands when neither is a string. -// It returns the number of bytes written and any write error encountered. -func Fprint(w io.Writer, a ...interface{}) (n int, error os.Error) { - p := newPrinter() - p.doPrint(a, false, false) - n64, error := p.buf.WriteTo(w) - p.free() - return int(n64), error -} - -// Print formats using the default formats for its operands and writes to standard output. -// Spaces are added between operands when neither is a string. -// It returns the number of bytes written and any write error encountered. -func Print(a ...interface{}) (n int, errno os.Error) { - n, errno = Fprint(os.Stdout, a...) - return n, errno -} - -// Sprint formats using the default formats for its operands and returns the resulting string. -// Spaces are added between operands when neither is a string. -func Sprint(a ...interface{}) string { - p := newPrinter() - p.doPrint(a, false, false) - s := p.buf.String() - p.free() - return s -} - -// These routines end in 'ln', do not take a format string, -// always add spaces between operands, and add a newline -// after the last operand. - -// Fprintln formats using the default formats for its operands and writes to w. -// Spaces are always added between operands and a newline is appended. -// It returns the number of bytes written and any write error encountered. -func Fprintln(w io.Writer, a ...interface{}) (n int, error os.Error) { - p := newPrinter() - p.doPrint(a, true, true) - n64, error := p.buf.WriteTo(w) - p.free() - return int(n64), error -} - -// Println formats using the default formats for its operands and writes to standard output. -// Spaces are always added between operands and a newline is appended. -// It returns the number of bytes written and any write error encountered. -func Println(a ...interface{}) (n int, errno os.Error) { - n, errno = Fprintln(os.Stdout, a...) - return n, errno -} - -// Sprintln formats using the default formats for its operands and returns the resulting string. -// Spaces are always added between operands and a newline is appended. -func Sprintln(a ...interface{}) string { - p := newPrinter() - p.doPrint(a, true, true) - s := p.buf.String() - p.free() - return s -} - -// Get the i'th arg of the struct value. -// If the arg itself is an interface, return a value for -// the thing inside the interface, not the interface itself. -func getField(v *reflect.StructValue, i int) reflect.Value { - val := v.Field(i) - if i, ok := val.(*reflect.InterfaceValue); ok { - if inter := i.Interface(); inter != nil { - return reflect.NewValue(inter) - } - } - return val -} - -// Convert ASCII to integer. n is 0 (and got is false) if no number present. -func parsenum(s string, start, end int) (num int, isnum bool, newi int) { - if start >= end { - return 0, false, end - } - for newi = start; newi < end && '0' <= s[newi] && s[newi] <= '9'; newi++ { - num = num*10 + int(s[newi]-'0') - isnum = true - } - return -} - -func (p *pp) unknownType(v interface{}) { - if v == nil { - p.buf.Write(nilAngleBytes) - return - } - p.buf.WriteByte('?') - p.buf.WriteString(reflect.Typeof(v).String()) - p.buf.WriteByte('?') -} - -func (p *pp) badVerb(verb int, val interface{}) { - p.add('%') - p.add('!') - p.add(verb) - p.add('(') - if val == nil { - p.buf.Write(nilAngleBytes) - } else { - p.buf.WriteString(reflect.Typeof(val).String()) - p.add('=') - p.printField(val, 'v', false, false, 0) - } - p.add(')') -} - -func (p *pp) fmtBool(v bool, verb int, value interface{}) { - switch verb { - case 't', 'v': - p.fmt.fmt_boolean(v) - default: - p.badVerb(verb, value) - } -} - -// fmtC formats a rune for the 'c' format. -func (p *pp) fmtC(c int64) { - rune := int(c) // Check for overflow. - if int64(rune) != c { - rune = utf8.RuneError - } - w := utf8.EncodeRune(p.runeBuf[0:utf8.UTFMax], rune) - p.fmt.pad(p.runeBuf[0:w]) -} - -func (p *pp) fmtInt64(v int64, verb int, value interface{}) { - switch verb { - case 'b': - p.fmt.integer(v, 2, signed, ldigits) - case 'c': - p.fmtC(v) - case 'd', 'v': - p.fmt.integer(v, 10, signed, ldigits) - case 'o': - p.fmt.integer(v, 8, signed, ldigits) - case 'x': - p.fmt.integer(v, 16, signed, ldigits) - case 'U': - p.fmtUnicode(v) - case 'X': - p.fmt.integer(v, 16, signed, udigits) - default: - p.badVerb(verb, value) - } -} - -// fmt0x64 formats a uint64 in hexadecimal and prefixes it with 0x or -// not, as requested, by temporarily setting the sharp flag. -func (p *pp) fmt0x64(v uint64, leading0x bool) { - sharp := p.fmt.sharp - p.fmt.sharp = leading0x - p.fmt.integer(int64(v), 16, unsigned, ldigits) - p.fmt.sharp = sharp -} - -// fmtUnicode formats a uint64 in U+1234 form by -// temporarily turning on the unicode flag and tweaking the precision. -func (p *pp) fmtUnicode(v int64) { - precPresent := p.fmt.precPresent - prec := p.fmt.prec - if !precPresent { - // If prec is already set, leave it alone; otherwise 4 is minimum. - p.fmt.prec = 4 - p.fmt.precPresent = true - } - p.fmt.unicode = true // turn on U+ - p.fmt.integer(int64(v), 16, unsigned, udigits) - p.fmt.unicode = false - p.fmt.prec = prec - p.fmt.precPresent = precPresent -} - -func (p *pp) fmtUint64(v uint64, verb int, goSyntax bool, value interface{}) { - switch verb { - case 'b': - p.fmt.integer(int64(v), 2, unsigned, ldigits) - case 'c': - p.fmtC(int64(v)) - case 'd': - p.fmt.integer(int64(v), 10, unsigned, ldigits) - case 'v': - if goSyntax { - p.fmt0x64(v, true) - } else { - p.fmt.integer(int64(v), 10, unsigned, ldigits) - } - case 'o': - p.fmt.integer(int64(v), 8, unsigned, ldigits) - case 'x': - p.fmt.integer(int64(v), 16, unsigned, ldigits) - case 'X': - p.fmt.integer(int64(v), 16, unsigned, udigits) - default: - p.badVerb(verb, value) - } -} - -func (p *pp) fmtFloat32(v float32, verb int, value interface{}) { - switch verb { - case 'b': - p.fmt.fmt_fb32(v) - case 'e': - p.fmt.fmt_e32(v) - case 'E': - p.fmt.fmt_E32(v) - case 'f': - p.fmt.fmt_f32(v) - case 'g', 'v': - p.fmt.fmt_g32(v) - case 'G': - p.fmt.fmt_G32(v) - default: - p.badVerb(verb, value) - } -} - -func (p *pp) fmtFloat64(v float64, verb int, value interface{}) { - switch verb { - case 'b': - p.fmt.fmt_fb64(v) - case 'e': - p.fmt.fmt_e64(v) - case 'E': - p.fmt.fmt_E64(v) - case 'f': - p.fmt.fmt_f64(v) - case 'g', 'v': - p.fmt.fmt_g64(v) - case 'G': - p.fmt.fmt_G64(v) - default: - p.badVerb(verb, value) - } -} - -func (p *pp) fmtComplex64(v complex64, verb int, value interface{}) { - switch verb { - case 'e', 'E', 'f', 'F', 'g', 'G': - p.fmt.fmt_c64(v, verb) - case 'v': - p.fmt.fmt_c64(v, 'g') - default: - p.badVerb(verb, value) - } -} - -func (p *pp) fmtComplex128(v complex128, verb int, value interface{}) { - switch verb { - case 'e', 'E', 'f', 'F', 'g', 'G': - p.fmt.fmt_c128(v, verb) - case 'v': - p.fmt.fmt_c128(v, 'g') - default: - p.badVerb(verb, value) - } -} - -func (p *pp) fmtString(v string, verb int, goSyntax bool, value interface{}) { - switch verb { - case 'v': - if goSyntax { - p.fmt.fmt_q(v) - } else { - p.fmt.fmt_s(v) - } - case 's': - p.fmt.fmt_s(v) - case 'x': - p.fmt.fmt_sx(v) - case 'X': - p.fmt.fmt_sX(v) - case 'q': - p.fmt.fmt_q(v) - default: - p.badVerb(verb, value) - } -} - -func (p *pp) fmtBytes(v []byte, verb int, goSyntax bool, depth int, value interface{}) { - if verb == 'v' || verb == 'd' { - if goSyntax { - p.buf.Write(bytesBytes) - } else { - p.buf.WriteByte('[') - } - for i, c := range v { - if i > 0 { - if goSyntax { - p.buf.Write(commaSpaceBytes) - } else { - p.buf.WriteByte(' ') - } - } - p.printField(c, 'v', p.fmt.plus, goSyntax, depth+1) - } - if goSyntax { - p.buf.WriteByte('}') - } else { - p.buf.WriteByte(']') - } - return - } - s := string(v) - switch verb { - case 's': - p.fmt.fmt_s(s) - case 'x': - p.fmt.fmt_sx(s) - case 'X': - p.fmt.fmt_sX(s) - case 'q': - p.fmt.fmt_q(s) - default: - p.badVerb(verb, value) - } -} - -func (p *pp) fmtPointer(field interface{}, value reflect.Value, verb int, goSyntax bool) { - var u uintptr - switch value.(type) { - case *reflect.ChanValue, *reflect.FuncValue, *reflect.MapValue, *reflect.PtrValue, *reflect.SliceValue, *reflect.UnsafePointerValue: - u = value.Pointer() - default: - p.badVerb(verb, field) - return - } - if goSyntax { - p.add('(') - p.buf.WriteString(reflect.Typeof(field).String()) - p.add(')') - p.add('(') - if u == 0 { - p.buf.Write(nilBytes) - } else { - p.fmt0x64(uint64(u), true) - } - p.add(')') - } else { - p.fmt0x64(uint64(u), !p.fmt.sharp) - } -} - -var ( - intBits = reflect.Typeof(0).Bits() - floatBits = reflect.Typeof(0.0).Bits() - complexBits = reflect.Typeof(1i).Bits() - uintptrBits = reflect.Typeof(uintptr(0)).Bits() -) - -func (p *pp) printField(field interface{}, verb int, plus, goSyntax bool, depth int) (wasString bool) { - if field == nil { - if verb == 'T' || verb == 'v' { - p.buf.Write(nilAngleBytes) - } else { - p.badVerb(verb, field) - } - return false - } - - // Special processing considerations. - // %T (the value's type) and %p (its address) are special; we always do them first. - switch verb { - case 'T': - p.printField(reflect.Typeof(field).String(), 's', false, false, 0) - return false - case 'p': - p.fmtPointer(field, reflect.NewValue(field), verb, goSyntax) - return false - } - // Is it a Formatter? - if formatter, ok := field.(Formatter); ok { - formatter.Format(p, verb) - return false // this value is not a string - - } - // Must not touch flags before Formatter looks at them. - if plus { - p.fmt.plus = false - } - // If we're doing Go syntax and the field knows how to supply it, take care of it now. - if goSyntax { - p.fmt.sharp = false - if stringer, ok := field.(GoStringer); ok { - // Print the result of GoString unadorned. - p.fmtString(stringer.GoString(), 's', false, field) - return false // this value is not a string - } - } else { - // Is it a Stringer? - if stringer, ok := field.(Stringer); ok { - p.printField(stringer.String(), verb, plus, false, depth) - return false // this value is not a string - } - } - - // Some types can be done without reflection. - switch f := field.(type) { - case bool: - p.fmtBool(f, verb, field) - return false - case float32: - p.fmtFloat32(f, verb, field) - return false - case float64: - p.fmtFloat64(f, verb, field) - return false - case complex64: - p.fmtComplex64(complex64(f), verb, field) - return false - case complex128: - p.fmtComplex128(f, verb, field) - return false - case int: - p.fmtInt64(int64(f), verb, field) - return false - case int8: - p.fmtInt64(int64(f), verb, field) - return false - case int16: - p.fmtInt64(int64(f), verb, field) - return false - case int32: - p.fmtInt64(int64(f), verb, field) - return false - case int64: - p.fmtInt64(f, verb, field) - return false - case uint: - p.fmtUint64(uint64(f), verb, goSyntax, field) - return false - case uint8: - p.fmtUint64(uint64(f), verb, goSyntax, field) - return false - case uint16: - p.fmtUint64(uint64(f), verb, goSyntax, field) - return false - case uint32: - p.fmtUint64(uint64(f), verb, goSyntax, field) - return false - case uint64: - p.fmtUint64(f, verb, goSyntax, field) - return false - case uintptr: - p.fmtUint64(uint64(f), verb, goSyntax, field) - return false - case string: - p.fmtString(f, verb, goSyntax, field) - return verb == 's' || verb == 'v' - case []byte: - p.fmtBytes(f, verb, goSyntax, depth, field) - return verb == 's' - } - - // Need to use reflection - value := reflect.NewValue(field) - -BigSwitch: - switch f := value.(type) { - case *reflect.BoolValue: - p.fmtBool(f.Get(), verb, field) - case *reflect.IntValue: - p.fmtInt64(f.Get(), verb, field) - case *reflect.UintValue: - p.fmtUint64(uint64(f.Get()), verb, goSyntax, field) - case *reflect.FloatValue: - if f.Type().Size() == 4 { - p.fmtFloat32(float32(f.Get()), verb, field) - } else { - p.fmtFloat64(float64(f.Get()), verb, field) - } - case *reflect.ComplexValue: - if f.Type().Size() == 8 { - p.fmtComplex64(complex64(f.Get()), verb, field) - } else { - p.fmtComplex128(complex128(f.Get()), verb, field) - } - case *reflect.StringValue: - p.fmtString(f.Get(), verb, goSyntax, field) - case *reflect.MapValue: - if goSyntax { - p.buf.WriteString(f.Type().String()) - p.buf.WriteByte('{') - } else { - p.buf.Write(mapBytes) - } - keys := f.Keys() - for i, key := range keys { - if i > 0 { - if goSyntax { - p.buf.Write(commaSpaceBytes) - } else { - p.buf.WriteByte(' ') - } - } - p.printField(key.Interface(), verb, plus, goSyntax, depth+1) - p.buf.WriteByte(':') - p.printField(f.Elem(key).Interface(), verb, plus, goSyntax, depth+1) - } - if goSyntax { - p.buf.WriteByte('}') - } else { - p.buf.WriteByte(']') - } - case *reflect.StructValue: - if goSyntax { - p.buf.WriteString(reflect.Typeof(field).String()) - } - p.add('{') - v := f - t := v.Type().(*reflect.StructType) - for i := 0; i < v.NumField(); i++ { - if i > 0 { - if goSyntax { - p.buf.Write(commaSpaceBytes) - } else { - p.buf.WriteByte(' ') - } - } - if plus || goSyntax { - if f := t.Field(i); f.Name != "" { - p.buf.WriteString(f.Name) - p.buf.WriteByte(':') - } - } - p.printField(getField(v, i).Interface(), verb, plus, goSyntax, depth+1) - } - p.buf.WriteByte('}') - case *reflect.InterfaceValue: - value := f.Elem() - if value == nil { - if goSyntax { - p.buf.WriteString(reflect.Typeof(field).String()) - p.buf.Write(nilParenBytes) - } else { - p.buf.Write(nilAngleBytes) - } - } else { - return p.printField(value.Interface(), verb, plus, goSyntax, depth+1) - } - case reflect.ArrayOrSliceValue: - // Byte slices are special. - if f.Type().(reflect.ArrayOrSliceType).Elem().Kind() == reflect.Uint8 { - // We know it's a slice of bytes, but we also know it does not have static type - // []byte, or it would have been caught above. Therefore we cannot convert - // it directly in the (slightly) obvious way: f.Interface().([]byte); it doesn't have - // that type, and we can't write an expression of the right type and do a - // conversion because we don't have a static way to write the right type. - // So we build a slice by hand. This is a rare case but it would be nice - // if reflection could help a little more. - bytes := make([]byte, f.Len()) - for i := range bytes { - bytes[i] = byte(f.Elem(i).(*reflect.UintValue).Get()) - } - p.fmtBytes(bytes, verb, goSyntax, depth, field) - return verb == 's' - } - if goSyntax { - p.buf.WriteString(reflect.Typeof(field).String()) - p.buf.WriteByte('{') - } else { - p.buf.WriteByte('[') - } - for i := 0; i < f.Len(); i++ { - if i > 0 { - if goSyntax { - p.buf.Write(commaSpaceBytes) - } else { - p.buf.WriteByte(' ') - } - } - p.printField(f.Elem(i).Interface(), verb, plus, goSyntax, depth+1) - } - if goSyntax { - p.buf.WriteByte('}') - } else { - p.buf.WriteByte(']') - } - case *reflect.PtrValue: - v := f.Get() - // pointer to array or slice or struct? ok at top level - // but not embedded (avoid loops) - if v != 0 && depth == 0 { - switch a := f.Elem().(type) { - case reflect.ArrayOrSliceValue: - p.buf.WriteByte('&') - p.printField(a.Interface(), verb, plus, goSyntax, depth+1) - break BigSwitch - case *reflect.StructValue: - p.buf.WriteByte('&') - p.printField(a.Interface(), verb, plus, goSyntax, depth+1) - break BigSwitch - } - } - if goSyntax { - p.buf.WriteByte('(') - p.buf.WriteString(reflect.Typeof(field).String()) - p.buf.WriteByte(')') - p.buf.WriteByte('(') - if v == 0 { - p.buf.Write(nilBytes) - } else { - p.fmt0x64(uint64(v), true) - } - p.buf.WriteByte(')') - break - } - if v == 0 { - p.buf.Write(nilAngleBytes) - break - } - p.fmt0x64(uint64(v), true) - case *reflect.ChanValue, *reflect.FuncValue, *reflect.UnsafePointerValue: - p.fmtPointer(field, value, verb, goSyntax) - default: - p.unknownType(f) - } - return false -} - -// intFromArg gets the fieldnumth element of a. On return, isInt reports whether the argument has type int. -func intFromArg(a []interface{}, end, i, fieldnum int) (num int, isInt bool, newi, newfieldnum int) { - newi, newfieldnum = end, fieldnum - if i < end && fieldnum < len(a) { - num, isInt = a[fieldnum].(int) - newi, newfieldnum = i+1, fieldnum+1 - } - return -} - -func (p *pp) doPrintf(format string, a []interface{}) { - end := len(format) - fieldnum := 0 // we process one field per non-trivial format - for i := 0; i < end; { - lasti := i - for i < end && format[i] != '%' { - i++ - } - if i > lasti { - p.buf.WriteString(format[lasti:i]) - } - if i >= end { - // done processing format string - break - } - - // Process one verb - i++ - // flags and widths - p.fmt.clearflags() - F: - for ; i < end; i++ { - switch format[i] { - case '#': - p.fmt.sharp = true - case '0': - p.fmt.zero = true - case '+': - p.fmt.plus = true - case '-': - p.fmt.minus = true - case ' ': - p.fmt.space = true - default: - break F - } - } - // do we have width? - if i < end && format[i] == '*' { - p.fmt.wid, p.fmt.widPresent, i, fieldnum = intFromArg(a, end, i, fieldnum) - if !p.fmt.widPresent { - p.buf.Write(widthBytes) - } - } else { - p.fmt.wid, p.fmt.widPresent, i = parsenum(format, i, end) - } - // do we have precision? - if i < end && format[i] == '.' { - if format[i+1] == '*' { - p.fmt.prec, p.fmt.precPresent, i, fieldnum = intFromArg(a, end, i+1, fieldnum) - if !p.fmt.precPresent { - p.buf.Write(precBytes) - } - } else { - p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i+1, end) - } - } - if i >= end { - p.buf.Write(noVerbBytes) - continue - } - c, w := utf8.DecodeRuneInString(format[i:]) - i += w - // percent is special - absorbs no operand - if c == '%' { - p.buf.WriteByte('%') // We ignore width and prec. - continue - } - if fieldnum >= len(a) { // out of operands - p.buf.WriteByte('%') - p.add(c) - p.buf.Write(missingBytes) - continue - } - field := a[fieldnum] - fieldnum++ - - goSyntax := c == 'v' && p.fmt.sharp - plus := c == 'v' && p.fmt.plus - p.printField(field, c, plus, goSyntax, 0) - } - - if fieldnum < len(a) { - p.buf.Write(extraBytes) - for ; fieldnum < len(a); fieldnum++ { - field := a[fieldnum] - if field != nil { - p.buf.WriteString(reflect.Typeof(field).String()) - p.buf.WriteByte('=') - } - p.printField(field, 'v', false, false, 0) - if fieldnum+1 < len(a) { - p.buf.Write(commaSpaceBytes) - } - } - p.buf.WriteByte(')') - } -} - -func (p *pp) doPrint(a []interface{}, addspace, addnewline bool) { - prevString := false - for fieldnum := 0; fieldnum < len(a); fieldnum++ { - p.fmt.clearflags() - // always add spaces if we're doing println - field := a[fieldnum] - if fieldnum > 0 { - isString := field != nil && reflect.Typeof(field).Kind() == reflect.String - if addspace || !isString && !prevString { - p.buf.WriteByte(' ') - } - } - prevString = p.printField(field, 'v', false, false, 0) - } - if addnewline { - p.buf.WriteByte('\n') - } -} diff --git a/src/cmd/fix/testdata/reflect.print.go.out b/src/cmd/fix/testdata/reflect.print.go.out deleted file mode 100644 index e4e4c7368..000000000 --- a/src/cmd/fix/testdata/reflect.print.go.out +++ /dev/null @@ -1,944 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package fmt - -import ( - "bytes" - "io" - "os" - "reflect" - "utf8" -) - -// Some constants in the form of bytes, to avoid string overhead. -// Needlessly fastidious, I suppose. -var ( - commaSpaceBytes = []byte(", ") - nilAngleBytes = []byte("<nil>") - nilParenBytes = []byte("(nil)") - nilBytes = []byte("nil") - mapBytes = []byte("map[") - missingBytes = []byte("(MISSING)") - extraBytes = []byte("%!(EXTRA ") - irparenBytes = []byte("i)") - bytesBytes = []byte("[]byte{") - widthBytes = []byte("%!(BADWIDTH)") - precBytes = []byte("%!(BADPREC)") - noVerbBytes = []byte("%!(NOVERB)") -) - -// State represents the printer state passed to custom formatters. -// It provides access to the io.Writer interface plus information about -// the flags and options for the operand's format specifier. -type State interface { - // Write is the function to call to emit formatted output to be printed. - Write(b []byte) (ret int, err os.Error) - // Width returns the value of the width option and whether it has been set. - Width() (wid int, ok bool) - // Precision returns the value of the precision option and whether it has been set. - Precision() (prec int, ok bool) - - // Flag returns whether the flag c, a character, has been set. - Flag(int) bool -} - -// Formatter is the interface implemented by values with a custom formatter. -// The implementation of Format may call Sprintf or Fprintf(f) etc. -// to generate its output. -type Formatter interface { - Format(f State, c int) -} - -// Stringer is implemented by any value that has a String method(), -// which defines the ``native'' format for that value. -// The String method is used to print values passed as an operand -// to a %s or %v format or to an unformatted printer such as Print. -type Stringer interface { - String() string -} - -// GoStringer is implemented by any value that has a GoString() method, -// which defines the Go syntax for that value. -// The GoString method is used to print values passed as an operand -// to a %#v format. -type GoStringer interface { - GoString() string -} - -type pp struct { - n int - buf bytes.Buffer - runeBuf [utf8.UTFMax]byte - fmt fmt -} - -// A cache holds a set of reusable objects. -// The buffered channel holds the currently available objects. -// If more are needed, the cache creates them by calling new. -type cache struct { - saved chan interface{} - new func() interface{} -} - -func (c *cache) put(x interface{}) { - select { - case c.saved <- x: - // saved in cache - default: - // discard - } -} - -func (c *cache) get() interface{} { - select { - case x := <-c.saved: - return x // reused from cache - default: - return c.new() - } - panic("not reached") -} - -func newCache(f func() interface{}) *cache { - return &cache{make(chan interface{}, 100), f} -} - -var ppFree = newCache(func() interface{} { return new(pp) }) - -// Allocate a new pp struct or grab a cached one. -func newPrinter() *pp { - p := ppFree.get().(*pp) - p.fmt.init(&p.buf) - return p -} - -// Save used pp structs in ppFree; avoids an allocation per invocation. -func (p *pp) free() { - // Don't hold on to pp structs with large buffers. - if cap(p.buf.Bytes()) > 1024 { - return - } - p.buf.Reset() - ppFree.put(p) -} - -func (p *pp) Width() (wid int, ok bool) { return p.fmt.wid, p.fmt.widPresent } - -func (p *pp) Precision() (prec int, ok bool) { return p.fmt.prec, p.fmt.precPresent } - -func (p *pp) Flag(b int) bool { - switch b { - case '-': - return p.fmt.minus - case '+': - return p.fmt.plus - case '#': - return p.fmt.sharp - case ' ': - return p.fmt.space - case '0': - return p.fmt.zero - } - return false -} - -func (p *pp) add(c int) { - p.buf.WriteRune(c) -} - -// Implement Write so we can call Fprintf on a pp (through State), for -// recursive use in custom verbs. -func (p *pp) Write(b []byte) (ret int, err os.Error) { - return p.buf.Write(b) -} - -// These routines end in 'f' and take a format string. - -// Fprintf formats according to a format specifier and writes to w. -// It returns the number of bytes written and any write error encountered. -func Fprintf(w io.Writer, format string, a ...interface{}) (n int, error os.Error) { - p := newPrinter() - p.doPrintf(format, a) - n64, error := p.buf.WriteTo(w) - p.free() - return int(n64), error -} - -// Printf formats according to a format specifier and writes to standard output. -// It returns the number of bytes written and any write error encountered. -func Printf(format string, a ...interface{}) (n int, errno os.Error) { - n, errno = Fprintf(os.Stdout, format, a...) - return n, errno -} - -// Sprintf formats according to a format specifier and returns the resulting string. -func Sprintf(format string, a ...interface{}) string { - p := newPrinter() - p.doPrintf(format, a) - s := p.buf.String() - p.free() - return s -} - -// Errorf formats according to a format specifier and returns the string -// converted to an os.ErrorString, which satisfies the os.Error interface. -func Errorf(format string, a ...interface{}) os.Error { - return os.NewError(Sprintf(format, a...)) -} - -// These routines do not take a format string - -// Fprint formats using the default formats for its operands and writes to w. -// Spaces are added between operands when neither is a string. -// It returns the number of bytes written and any write error encountered. -func Fprint(w io.Writer, a ...interface{}) (n int, error os.Error) { - p := newPrinter() - p.doPrint(a, false, false) - n64, error := p.buf.WriteTo(w) - p.free() - return int(n64), error -} - -// Print formats using the default formats for its operands and writes to standard output. -// Spaces are added between operands when neither is a string. -// It returns the number of bytes written and any write error encountered. -func Print(a ...interface{}) (n int, errno os.Error) { - n, errno = Fprint(os.Stdout, a...) - return n, errno -} - -// Sprint formats using the default formats for its operands and returns the resulting string. -// Spaces are added between operands when neither is a string. -func Sprint(a ...interface{}) string { - p := newPrinter() - p.doPrint(a, false, false) - s := p.buf.String() - p.free() - return s -} - -// These routines end in 'ln', do not take a format string, -// always add spaces between operands, and add a newline -// after the last operand. - -// Fprintln formats using the default formats for its operands and writes to w. -// Spaces are always added between operands and a newline is appended. -// It returns the number of bytes written and any write error encountered. -func Fprintln(w io.Writer, a ...interface{}) (n int, error os.Error) { - p := newPrinter() - p.doPrint(a, true, true) - n64, error := p.buf.WriteTo(w) - p.free() - return int(n64), error -} - -// Println formats using the default formats for its operands and writes to standard output. -// Spaces are always added between operands and a newline is appended. -// It returns the number of bytes written and any write error encountered. -func Println(a ...interface{}) (n int, errno os.Error) { - n, errno = Fprintln(os.Stdout, a...) - return n, errno -} - -// Sprintln formats using the default formats for its operands and returns the resulting string. -// Spaces are always added between operands and a newline is appended. -func Sprintln(a ...interface{}) string { - p := newPrinter() - p.doPrint(a, true, true) - s := p.buf.String() - p.free() - return s -} - -// Get the i'th arg of the struct value. -// If the arg itself is an interface, return a value for -// the thing inside the interface, not the interface itself. -func getField(v reflect.Value, i int) reflect.Value { - val := v.Field(i) - if i := val; i.Kind() == reflect.Interface { - if inter := i.Interface(); inter != nil { - return reflect.ValueOf(inter) - } - } - return val -} - -// Convert ASCII to integer. n is 0 (and got is false) if no number present. -func parsenum(s string, start, end int) (num int, isnum bool, newi int) { - if start >= end { - return 0, false, end - } - for newi = start; newi < end && '0' <= s[newi] && s[newi] <= '9'; newi++ { - num = num*10 + int(s[newi]-'0') - isnum = true - } - return -} - -func (p *pp) unknownType(v interface{}) { - if v == nil { - p.buf.Write(nilAngleBytes) - return - } - p.buf.WriteByte('?') - p.buf.WriteString(reflect.TypeOf(v).String()) - p.buf.WriteByte('?') -} - -func (p *pp) badVerb(verb int, val interface{}) { - p.add('%') - p.add('!') - p.add(verb) - p.add('(') - if val == nil { - p.buf.Write(nilAngleBytes) - } else { - p.buf.WriteString(reflect.TypeOf(val).String()) - p.add('=') - p.printField(val, 'v', false, false, 0) - } - p.add(')') -} - -func (p *pp) fmtBool(v bool, verb int, value interface{}) { - switch verb { - case 't', 'v': - p.fmt.fmt_boolean(v) - default: - p.badVerb(verb, value) - } -} - -// fmtC formats a rune for the 'c' format. -func (p *pp) fmtC(c int64) { - rune := int(c) // Check for overflow. - if int64(rune) != c { - rune = utf8.RuneError - } - w := utf8.EncodeRune(p.runeBuf[0:utf8.UTFMax], rune) - p.fmt.pad(p.runeBuf[0:w]) -} - -func (p *pp) fmtInt64(v int64, verb int, value interface{}) { - switch verb { - case 'b': - p.fmt.integer(v, 2, signed, ldigits) - case 'c': - p.fmtC(v) - case 'd', 'v': - p.fmt.integer(v, 10, signed, ldigits) - case 'o': - p.fmt.integer(v, 8, signed, ldigits) - case 'x': - p.fmt.integer(v, 16, signed, ldigits) - case 'U': - p.fmtUnicode(v) - case 'X': - p.fmt.integer(v, 16, signed, udigits) - default: - p.badVerb(verb, value) - } -} - -// fmt0x64 formats a uint64 in hexadecimal and prefixes it with 0x or -// not, as requested, by temporarily setting the sharp flag. -func (p *pp) fmt0x64(v uint64, leading0x bool) { - sharp := p.fmt.sharp - p.fmt.sharp = leading0x - p.fmt.integer(int64(v), 16, unsigned, ldigits) - p.fmt.sharp = sharp -} - -// fmtUnicode formats a uint64 in U+1234 form by -// temporarily turning on the unicode flag and tweaking the precision. -func (p *pp) fmtUnicode(v int64) { - precPresent := p.fmt.precPresent - prec := p.fmt.prec - if !precPresent { - // If prec is already set, leave it alone; otherwise 4 is minimum. - p.fmt.prec = 4 - p.fmt.precPresent = true - } - p.fmt.unicode = true // turn on U+ - p.fmt.integer(int64(v), 16, unsigned, udigits) - p.fmt.unicode = false - p.fmt.prec = prec - p.fmt.precPresent = precPresent -} - -func (p *pp) fmtUint64(v uint64, verb int, goSyntax bool, value interface{}) { - switch verb { - case 'b': - p.fmt.integer(int64(v), 2, unsigned, ldigits) - case 'c': - p.fmtC(int64(v)) - case 'd': - p.fmt.integer(int64(v), 10, unsigned, ldigits) - case 'v': - if goSyntax { - p.fmt0x64(v, true) - } else { - p.fmt.integer(int64(v), 10, unsigned, ldigits) - } - case 'o': - p.fmt.integer(int64(v), 8, unsigned, ldigits) - case 'x': - p.fmt.integer(int64(v), 16, unsigned, ldigits) - case 'X': - p.fmt.integer(int64(v), 16, unsigned, udigits) - default: - p.badVerb(verb, value) - } -} - -func (p *pp) fmtFloat32(v float32, verb int, value interface{}) { - switch verb { - case 'b': - p.fmt.fmt_fb32(v) - case 'e': - p.fmt.fmt_e32(v) - case 'E': - p.fmt.fmt_E32(v) - case 'f': - p.fmt.fmt_f32(v) - case 'g', 'v': - p.fmt.fmt_g32(v) - case 'G': - p.fmt.fmt_G32(v) - default: - p.badVerb(verb, value) - } -} - -func (p *pp) fmtFloat64(v float64, verb int, value interface{}) { - switch verb { - case 'b': - p.fmt.fmt_fb64(v) - case 'e': - p.fmt.fmt_e64(v) - case 'E': - p.fmt.fmt_E64(v) - case 'f': - p.fmt.fmt_f64(v) - case 'g', 'v': - p.fmt.fmt_g64(v) - case 'G': - p.fmt.fmt_G64(v) - default: - p.badVerb(verb, value) - } -} - -func (p *pp) fmtComplex64(v complex64, verb int, value interface{}) { - switch verb { - case 'e', 'E', 'f', 'F', 'g', 'G': - p.fmt.fmt_c64(v, verb) - case 'v': - p.fmt.fmt_c64(v, 'g') - default: - p.badVerb(verb, value) - } -} - -func (p *pp) fmtComplex128(v complex128, verb int, value interface{}) { - switch verb { - case 'e', 'E', 'f', 'F', 'g', 'G': - p.fmt.fmt_c128(v, verb) - case 'v': - p.fmt.fmt_c128(v, 'g') - default: - p.badVerb(verb, value) - } -} - -func (p *pp) fmtString(v string, verb int, goSyntax bool, value interface{}) { - switch verb { - case 'v': - if goSyntax { - p.fmt.fmt_q(v) - } else { - p.fmt.fmt_s(v) - } - case 's': - p.fmt.fmt_s(v) - case 'x': - p.fmt.fmt_sx(v) - case 'X': - p.fmt.fmt_sX(v) - case 'q': - p.fmt.fmt_q(v) - default: - p.badVerb(verb, value) - } -} - -func (p *pp) fmtBytes(v []byte, verb int, goSyntax bool, depth int, value interface{}) { - if verb == 'v' || verb == 'd' { - if goSyntax { - p.buf.Write(bytesBytes) - } else { - p.buf.WriteByte('[') - } - for i, c := range v { - if i > 0 { - if goSyntax { - p.buf.Write(commaSpaceBytes) - } else { - p.buf.WriteByte(' ') - } - } - p.printField(c, 'v', p.fmt.plus, goSyntax, depth+1) - } - if goSyntax { - p.buf.WriteByte('}') - } else { - p.buf.WriteByte(']') - } - return - } - s := string(v) - switch verb { - case 's': - p.fmt.fmt_s(s) - case 'x': - p.fmt.fmt_sx(s) - case 'X': - p.fmt.fmt_sX(s) - case 'q': - p.fmt.fmt_q(s) - default: - p.badVerb(verb, value) - } -} - -func (p *pp) fmtPointer(field interface{}, value reflect.Value, verb int, goSyntax bool) { - var u uintptr - switch value.Kind() { - case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer: - u = value.Pointer() - default: - p.badVerb(verb, field) - return - } - if goSyntax { - p.add('(') - p.buf.WriteString(reflect.TypeOf(field).String()) - p.add(')') - p.add('(') - if u == 0 { - p.buf.Write(nilBytes) - } else { - p.fmt0x64(uint64(u), true) - } - p.add(')') - } else { - p.fmt0x64(uint64(u), !p.fmt.sharp) - } -} - -var ( - intBits = reflect.TypeOf(0).Bits() - floatBits = reflect.TypeOf(0.0).Bits() - complexBits = reflect.TypeOf(1i).Bits() - uintptrBits = reflect.TypeOf(uintptr(0)).Bits() -) - -func (p *pp) printField(field interface{}, verb int, plus, goSyntax bool, depth int) (wasString bool) { - if field == nil { - if verb == 'T' || verb == 'v' { - p.buf.Write(nilAngleBytes) - } else { - p.badVerb(verb, field) - } - return false - } - - // Special processing considerations. - // %T (the value's type) and %p (its address) are special; we always do them first. - switch verb { - case 'T': - p.printField(reflect.TypeOf(field).String(), 's', false, false, 0) - return false - case 'p': - p.fmtPointer(field, reflect.ValueOf(field), verb, goSyntax) - return false - } - // Is it a Formatter? - if formatter, ok := field.(Formatter); ok { - formatter.Format(p, verb) - return false // this value is not a string - - } - // Must not touch flags before Formatter looks at them. - if plus { - p.fmt.plus = false - } - // If we're doing Go syntax and the field knows how to supply it, take care of it now. - if goSyntax { - p.fmt.sharp = false - if stringer, ok := field.(GoStringer); ok { - // Print the result of GoString unadorned. - p.fmtString(stringer.GoString(), 's', false, field) - return false // this value is not a string - } - } else { - // Is it a Stringer? - if stringer, ok := field.(Stringer); ok { - p.printField(stringer.String(), verb, plus, false, depth) - return false // this value is not a string - } - } - - // Some types can be done without reflection. - switch f := field.(type) { - case bool: - p.fmtBool(f, verb, field) - return false - case float32: - p.fmtFloat32(f, verb, field) - return false - case float64: - p.fmtFloat64(f, verb, field) - return false - case complex64: - p.fmtComplex64(complex64(f), verb, field) - return false - case complex128: - p.fmtComplex128(f, verb, field) - return false - case int: - p.fmtInt64(int64(f), verb, field) - return false - case int8: - p.fmtInt64(int64(f), verb, field) - return false - case int16: - p.fmtInt64(int64(f), verb, field) - return false - case int32: - p.fmtInt64(int64(f), verb, field) - return false - case int64: - p.fmtInt64(f, verb, field) - return false - case uint: - p.fmtUint64(uint64(f), verb, goSyntax, field) - return false - case uint8: - p.fmtUint64(uint64(f), verb, goSyntax, field) - return false - case uint16: - p.fmtUint64(uint64(f), verb, goSyntax, field) - return false - case uint32: - p.fmtUint64(uint64(f), verb, goSyntax, field) - return false - case uint64: - p.fmtUint64(f, verb, goSyntax, field) - return false - case uintptr: - p.fmtUint64(uint64(f), verb, goSyntax, field) - return false - case string: - p.fmtString(f, verb, goSyntax, field) - return verb == 's' || verb == 'v' - case []byte: - p.fmtBytes(f, verb, goSyntax, depth, field) - return verb == 's' - } - - // Need to use reflection - value := reflect.ValueOf(field) - -BigSwitch: - switch f := value; f.Kind() { - case reflect.Bool: - p.fmtBool(f.Bool(), verb, field) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - p.fmtInt64(f.Int(), verb, field) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - p.fmtUint64(uint64(f.Uint()), verb, goSyntax, field) - case reflect.Float32, reflect.Float64: - if f.Type().Size() == 4 { - p.fmtFloat32(float32(f.Float()), verb, field) - } else { - p.fmtFloat64(float64(f.Float()), verb, field) - } - case reflect.Complex64, reflect.Complex128: - if f.Type().Size() == 8 { - p.fmtComplex64(complex64(f.Complex()), verb, field) - } else { - p.fmtComplex128(complex128(f.Complex()), verb, field) - } - case reflect.String: - p.fmtString(f.String(), verb, goSyntax, field) - case reflect.Map: - if goSyntax { - p.buf.WriteString(f.Type().String()) - p.buf.WriteByte('{') - } else { - p.buf.Write(mapBytes) - } - keys := f.MapKeys() - for i, key := range keys { - if i > 0 { - if goSyntax { - p.buf.Write(commaSpaceBytes) - } else { - p.buf.WriteByte(' ') - } - } - p.printField(key.Interface(), verb, plus, goSyntax, depth+1) - p.buf.WriteByte(':') - p.printField(f.MapIndex(key).Interface(), verb, plus, goSyntax, depth+1) - } - if goSyntax { - p.buf.WriteByte('}') - } else { - p.buf.WriteByte(']') - } - case reflect.Struct: - if goSyntax { - p.buf.WriteString(reflect.TypeOf(field).String()) - } - p.add('{') - v := f - t := v.Type() - for i := 0; i < v.NumField(); i++ { - if i > 0 { - if goSyntax { - p.buf.Write(commaSpaceBytes) - } else { - p.buf.WriteByte(' ') - } - } - if plus || goSyntax { - if f := t.Field(i); f.Name != "" { - p.buf.WriteString(f.Name) - p.buf.WriteByte(':') - } - } - p.printField(getField(v, i).Interface(), verb, plus, goSyntax, depth+1) - } - p.buf.WriteByte('}') - case reflect.Interface: - value := f.Elem() - if !value.IsValid() { - if goSyntax { - p.buf.WriteString(reflect.TypeOf(field).String()) - p.buf.Write(nilParenBytes) - } else { - p.buf.Write(nilAngleBytes) - } - } else { - return p.printField(value.Interface(), verb, plus, goSyntax, depth+1) - } - case reflect.Array, reflect.Slice: - // Byte slices are special. - if f.Type().Elem().Kind() == reflect.Uint8 { - // We know it's a slice of bytes, but we also know it does not have static type - // []byte, or it would have been caught above. Therefore we cannot convert - // it directly in the (slightly) obvious way: f.Interface().([]byte); it doesn't have - // that type, and we can't write an expression of the right type and do a - // conversion because we don't have a static way to write the right type. - // So we build a slice by hand. This is a rare case but it would be nice - // if reflection could help a little more. - bytes := make([]byte, f.Len()) - for i := range bytes { - bytes[i] = byte(f.Index(i).Uint()) - } - p.fmtBytes(bytes, verb, goSyntax, depth, field) - return verb == 's' - } - if goSyntax { - p.buf.WriteString(reflect.TypeOf(field).String()) - p.buf.WriteByte('{') - } else { - p.buf.WriteByte('[') - } - for i := 0; i < f.Len(); i++ { - if i > 0 { - if goSyntax { - p.buf.Write(commaSpaceBytes) - } else { - p.buf.WriteByte(' ') - } - } - p.printField(f.Index(i).Interface(), verb, plus, goSyntax, depth+1) - } - if goSyntax { - p.buf.WriteByte('}') - } else { - p.buf.WriteByte(']') - } - case reflect.Ptr: - v := f.Pointer() - // pointer to array or slice or struct? ok at top level - // but not embedded (avoid loops) - if v != 0 && depth == 0 { - switch a := f.Elem(); a.Kind() { - case reflect.Array, reflect.Slice: - p.buf.WriteByte('&') - p.printField(a.Interface(), verb, plus, goSyntax, depth+1) - break BigSwitch - case reflect.Struct: - p.buf.WriteByte('&') - p.printField(a.Interface(), verb, plus, goSyntax, depth+1) - break BigSwitch - } - } - if goSyntax { - p.buf.WriteByte('(') - p.buf.WriteString(reflect.TypeOf(field).String()) - p.buf.WriteByte(')') - p.buf.WriteByte('(') - if v == 0 { - p.buf.Write(nilBytes) - } else { - p.fmt0x64(uint64(v), true) - } - p.buf.WriteByte(')') - break - } - if v == 0 { - p.buf.Write(nilAngleBytes) - break - } - p.fmt0x64(uint64(v), true) - case reflect.Chan, reflect.Func, reflect.UnsafePointer: - p.fmtPointer(field, value, verb, goSyntax) - default: - p.unknownType(f) - } - return false -} - -// intFromArg gets the fieldnumth element of a. On return, isInt reports whether the argument has type int. -func intFromArg(a []interface{}, end, i, fieldnum int) (num int, isInt bool, newi, newfieldnum int) { - newi, newfieldnum = end, fieldnum - if i < end && fieldnum < len(a) { - num, isInt = a[fieldnum].(int) - newi, newfieldnum = i+1, fieldnum+1 - } - return -} - -func (p *pp) doPrintf(format string, a []interface{}) { - end := len(format) - fieldnum := 0 // we process one field per non-trivial format - for i := 0; i < end; { - lasti := i - for i < end && format[i] != '%' { - i++ - } - if i > lasti { - p.buf.WriteString(format[lasti:i]) - } - if i >= end { - // done processing format string - break - } - - // Process one verb - i++ - // flags and widths - p.fmt.clearflags() - F: - for ; i < end; i++ { - switch format[i] { - case '#': - p.fmt.sharp = true - case '0': - p.fmt.zero = true - case '+': - p.fmt.plus = true - case '-': - p.fmt.minus = true - case ' ': - p.fmt.space = true - default: - break F - } - } - // do we have width? - if i < end && format[i] == '*' { - p.fmt.wid, p.fmt.widPresent, i, fieldnum = intFromArg(a, end, i, fieldnum) - if !p.fmt.widPresent { - p.buf.Write(widthBytes) - } - } else { - p.fmt.wid, p.fmt.widPresent, i = parsenum(format, i, end) - } - // do we have precision? - if i < end && format[i] == '.' { - if format[i+1] == '*' { - p.fmt.prec, p.fmt.precPresent, i, fieldnum = intFromArg(a, end, i+1, fieldnum) - if !p.fmt.precPresent { - p.buf.Write(precBytes) - } - } else { - p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i+1, end) - } - } - if i >= end { - p.buf.Write(noVerbBytes) - continue - } - c, w := utf8.DecodeRuneInString(format[i:]) - i += w - // percent is special - absorbs no operand - if c == '%' { - p.buf.WriteByte('%') // We ignore width and prec. - continue - } - if fieldnum >= len(a) { // out of operands - p.buf.WriteByte('%') - p.add(c) - p.buf.Write(missingBytes) - continue - } - field := a[fieldnum] - fieldnum++ - - goSyntax := c == 'v' && p.fmt.sharp - plus := c == 'v' && p.fmt.plus - p.printField(field, c, plus, goSyntax, 0) - } - - if fieldnum < len(a) { - p.buf.Write(extraBytes) - for ; fieldnum < len(a); fieldnum++ { - field := a[fieldnum] - if field != nil { - p.buf.WriteString(reflect.TypeOf(field).String()) - p.buf.WriteByte('=') - } - p.printField(field, 'v', false, false, 0) - if fieldnum+1 < len(a) { - p.buf.Write(commaSpaceBytes) - } - } - p.buf.WriteByte(')') - } -} - -func (p *pp) doPrint(a []interface{}, addspace, addnewline bool) { - prevString := false - for fieldnum := 0; fieldnum < len(a); fieldnum++ { - p.fmt.clearflags() - // always add spaces if we're doing println - field := a[fieldnum] - if fieldnum > 0 { - isString := field != nil && reflect.TypeOf(field).Kind() == reflect.String - if addspace || !isString && !prevString { - p.buf.WriteByte(' ') - } - } - prevString = p.printField(field, 'v', false, false, 0) - } - if addnewline { - p.buf.WriteByte('\n') - } -} diff --git a/src/cmd/fix/testdata/reflect.quick.go.in b/src/cmd/fix/testdata/reflect.quick.go.in deleted file mode 100644 index a5568b048..000000000 --- a/src/cmd/fix/testdata/reflect.quick.go.in +++ /dev/null @@ -1,364 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This package implements utility functions to help with black box testing. -package quick - -import ( - "flag" - "fmt" - "math" - "os" - "rand" - "reflect" - "strings" -) - -var defaultMaxCount *int = flag.Int("quickchecks", 100, "The default number of iterations for each check") - -// A Generator can generate random values of its own type. -type Generator interface { - // Generate returns a random instance of the type on which it is a - // method using the size as a size hint. - Generate(rand *rand.Rand, size int) reflect.Value -} - -// randFloat32 generates a random float taking the full range of a float32. -func randFloat32(rand *rand.Rand) float32 { - f := rand.Float64() * math.MaxFloat32 - if rand.Int()&1 == 1 { - f = -f - } - return float32(f) -} - -// randFloat64 generates a random float taking the full range of a float64. -func randFloat64(rand *rand.Rand) float64 { - f := rand.Float64() - if rand.Int()&1 == 1 { - f = -f - } - return f -} - -// randInt64 returns a random integer taking half the range of an int64. -func randInt64(rand *rand.Rand) int64 { return rand.Int63() - 1<<62 } - -// complexSize is the maximum length of arbitrary values that contain other -// values. -const complexSize = 50 - -// Value returns an arbitrary value of the given type. -// If the type implements the Generator interface, that will be used. -// Note: in order to create arbitrary values for structs, all the members must be public. -func Value(t reflect.Type, rand *rand.Rand) (value reflect.Value, ok bool) { - if m, ok := reflect.MakeZero(t).Interface().(Generator); ok { - return m.Generate(rand, complexSize), true - } - - switch concrete := t.(type) { - case *reflect.BoolType: - return reflect.NewValue(rand.Int()&1 == 0), true - case *reflect.FloatType, *reflect.IntType, *reflect.UintType, *reflect.ComplexType: - switch t.Kind() { - case reflect.Float32: - return reflect.NewValue(randFloat32(rand)), true - case reflect.Float64: - return reflect.NewValue(randFloat64(rand)), true - case reflect.Complex64: - return reflect.NewValue(complex(randFloat32(rand), randFloat32(rand))), true - case reflect.Complex128: - return reflect.NewValue(complex(randFloat64(rand), randFloat64(rand))), true - case reflect.Int16: - return reflect.NewValue(int16(randInt64(rand))), true - case reflect.Int32: - return reflect.NewValue(int32(randInt64(rand))), true - case reflect.Int64: - return reflect.NewValue(randInt64(rand)), true - case reflect.Int8: - return reflect.NewValue(int8(randInt64(rand))), true - case reflect.Int: - return reflect.NewValue(int(randInt64(rand))), true - case reflect.Uint16: - return reflect.NewValue(uint16(randInt64(rand))), true - case reflect.Uint32: - return reflect.NewValue(uint32(randInt64(rand))), true - case reflect.Uint64: - return reflect.NewValue(uint64(randInt64(rand))), true - case reflect.Uint8: - return reflect.NewValue(uint8(randInt64(rand))), true - case reflect.Uint: - return reflect.NewValue(uint(randInt64(rand))), true - case reflect.Uintptr: - return reflect.NewValue(uintptr(randInt64(rand))), true - } - case *reflect.MapType: - numElems := rand.Intn(complexSize) - m := reflect.MakeMap(concrete) - for i := 0; i < numElems; i++ { - key, ok1 := Value(concrete.Key(), rand) - value, ok2 := Value(concrete.Elem(), rand) - if !ok1 || !ok2 { - return nil, false - } - m.SetElem(key, value) - } - return m, true - case *reflect.PtrType: - v, ok := Value(concrete.Elem(), rand) - if !ok { - return nil, false - } - p := reflect.MakeZero(concrete) - p.(*reflect.PtrValue).PointTo(v) - return p, true - case *reflect.SliceType: - numElems := rand.Intn(complexSize) - s := reflect.MakeSlice(concrete, numElems, numElems) - for i := 0; i < numElems; i++ { - v, ok := Value(concrete.Elem(), rand) - if !ok { - return nil, false - } - s.Elem(i).SetValue(v) - } - return s, true - case *reflect.StringType: - numChars := rand.Intn(complexSize) - codePoints := make([]int, numChars) - for i := 0; i < numChars; i++ { - codePoints[i] = rand.Intn(0x10ffff) - } - return reflect.NewValue(string(codePoints)), true - case *reflect.StructType: - s := reflect.MakeZero(t).(*reflect.StructValue) - for i := 0; i < s.NumField(); i++ { - v, ok := Value(concrete.Field(i).Type, rand) - if !ok { - return nil, false - } - s.Field(i).SetValue(v) - } - return s, true - default: - return nil, false - } - - return -} - -// A Config structure contains options for running a test. -type Config struct { - // MaxCount sets the maximum number of iterations. If zero, - // MaxCountScale is used. - MaxCount int - // MaxCountScale is a non-negative scale factor applied to the default - // maximum. If zero, the default is unchanged. - MaxCountScale float64 - // If non-nil, rand is a source of random numbers. Otherwise a default - // pseudo-random source will be used. - Rand *rand.Rand - // If non-nil, Values is a function which generates a slice of arbitrary - // Values that are congruent with the arguments to the function being - // tested. Otherwise, Values is used to generate the values. - Values func([]reflect.Value, *rand.Rand) -} - -var defaultConfig Config - -// getRand returns the *rand.Rand to use for a given Config. -func (c *Config) getRand() *rand.Rand { - if c.Rand == nil { - return rand.New(rand.NewSource(0)) - } - return c.Rand -} - -// getMaxCount returns the maximum number of iterations to run for a given -// Config. -func (c *Config) getMaxCount() (maxCount int) { - maxCount = c.MaxCount - if maxCount == 0 { - if c.MaxCountScale != 0 { - maxCount = int(c.MaxCountScale * float64(*defaultMaxCount)) - } else { - maxCount = *defaultMaxCount - } - } - - return -} - -// A SetupError is the result of an error in the way that check is being -// used, independent of the functions being tested. -type SetupError string - -func (s SetupError) String() string { return string(s) } - -// A CheckError is the result of Check finding an error. -type CheckError struct { - Count int - In []interface{} -} - -func (s *CheckError) String() string { - return fmt.Sprintf("#%d: failed on input %s", s.Count, toString(s.In)) -} - -// A CheckEqualError is the result CheckEqual finding an error. -type CheckEqualError struct { - CheckError - Out1 []interface{} - Out2 []interface{} -} - -func (s *CheckEqualError) String() string { - return fmt.Sprintf("#%d: failed on input %s. Output 1: %s. Output 2: %s", s.Count, toString(s.In), toString(s.Out1), toString(s.Out2)) -} - -// Check looks for an input to f, any function that returns bool, -// such that f returns false. It calls f repeatedly, with arbitrary -// values for each argument. If f returns false on a given input, -// Check returns that input as a *CheckError. -// For example: -// -// func TestOddMultipleOfThree(t *testing.T) { -// f := func(x int) bool { -// y := OddMultipleOfThree(x) -// return y%2 == 1 && y%3 == 0 -// } -// if err := quick.Check(f, nil); err != nil { -// t.Error(err) -// } -// } -func Check(function interface{}, config *Config) (err os.Error) { - if config == nil { - config = &defaultConfig - } - - f, fType, ok := functionAndType(function) - if !ok { - err = SetupError("argument is not a function") - return - } - - if fType.NumOut() != 1 { - err = SetupError("function returns more than one value.") - return - } - if _, ok := fType.Out(0).(*reflect.BoolType); !ok { - err = SetupError("function does not return a bool") - return - } - - arguments := make([]reflect.Value, fType.NumIn()) - rand := config.getRand() - maxCount := config.getMaxCount() - - for i := 0; i < maxCount; i++ { - err = arbitraryValues(arguments, fType, config, rand) - if err != nil { - return - } - - if !f.Call(arguments)[0].(*reflect.BoolValue).Get() { - err = &CheckError{i + 1, toInterfaces(arguments)} - return - } - } - - return -} - -// CheckEqual looks for an input on which f and g return different results. -// It calls f and g repeatedly with arbitrary values for each argument. -// If f and g return different answers, CheckEqual returns a *CheckEqualError -// describing the input and the outputs. -func CheckEqual(f, g interface{}, config *Config) (err os.Error) { - if config == nil { - config = &defaultConfig - } - - x, xType, ok := functionAndType(f) - if !ok { - err = SetupError("f is not a function") - return - } - y, yType, ok := functionAndType(g) - if !ok { - err = SetupError("g is not a function") - return - } - - if xType != yType { - err = SetupError("functions have different types") - return - } - - arguments := make([]reflect.Value, xType.NumIn()) - rand := config.getRand() - maxCount := config.getMaxCount() - - for i := 0; i < maxCount; i++ { - err = arbitraryValues(arguments, xType, config, rand) - if err != nil { - return - } - - xOut := toInterfaces(x.Call(arguments)) - yOut := toInterfaces(y.Call(arguments)) - - if !reflect.DeepEqual(xOut, yOut) { - err = &CheckEqualError{CheckError{i + 1, toInterfaces(arguments)}, xOut, yOut} - return - } - } - - return -} - -// arbitraryValues writes Values to args such that args contains Values -// suitable for calling f. -func arbitraryValues(args []reflect.Value, f *reflect.FuncType, config *Config, rand *rand.Rand) (err os.Error) { - if config.Values != nil { - config.Values(args, rand) - return - } - - for j := 0; j < len(args); j++ { - var ok bool - args[j], ok = Value(f.In(j), rand) - if !ok { - err = SetupError(fmt.Sprintf("cannot create arbitrary value of type %s for argument %d", f.In(j), j)) - return - } - } - - return -} - -func functionAndType(f interface{}) (v *reflect.FuncValue, t *reflect.FuncType, ok bool) { - v, ok = reflect.NewValue(f).(*reflect.FuncValue) - if !ok { - return - } - t = v.Type().(*reflect.FuncType) - return -} - -func toInterfaces(values []reflect.Value) []interface{} { - ret := make([]interface{}, len(values)) - for i, v := range values { - ret[i] = v.Interface() - } - return ret -} - -func toString(interfaces []interface{}) string { - s := make([]string, len(interfaces)) - for i, v := range interfaces { - s[i] = fmt.Sprintf("%#v", v) - } - return strings.Join(s, ", ") -} diff --git a/src/cmd/fix/testdata/reflect.quick.go.out b/src/cmd/fix/testdata/reflect.quick.go.out deleted file mode 100644 index c62305b83..000000000 --- a/src/cmd/fix/testdata/reflect.quick.go.out +++ /dev/null @@ -1,365 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This package implements utility functions to help with black box testing. -package quick - -import ( - "flag" - "fmt" - "math" - "os" - "rand" - "reflect" - "strings" -) - -var defaultMaxCount *int = flag.Int("quickchecks", 100, "The default number of iterations for each check") - -// A Generator can generate random values of its own type. -type Generator interface { - // Generate returns a random instance of the type on which it is a - // method using the size as a size hint. - Generate(rand *rand.Rand, size int) reflect.Value -} - -// randFloat32 generates a random float taking the full range of a float32. -func randFloat32(rand *rand.Rand) float32 { - f := rand.Float64() * math.MaxFloat32 - if rand.Int()&1 == 1 { - f = -f - } - return float32(f) -} - -// randFloat64 generates a random float taking the full range of a float64. -func randFloat64(rand *rand.Rand) float64 { - f := rand.Float64() - if rand.Int()&1 == 1 { - f = -f - } - return f -} - -// randInt64 returns a random integer taking half the range of an int64. -func randInt64(rand *rand.Rand) int64 { return rand.Int63() - 1<<62 } - -// complexSize is the maximum length of arbitrary values that contain other -// values. -const complexSize = 50 - -// Value returns an arbitrary value of the given type. -// If the type implements the Generator interface, that will be used. -// Note: in order to create arbitrary values for structs, all the members must be public. -func Value(t reflect.Type, rand *rand.Rand) (value reflect.Value, ok bool) { - if m, ok := reflect.Zero(t).Interface().(Generator); ok { - return m.Generate(rand, complexSize), true - } - - switch concrete := t; concrete.Kind() { - case reflect.Bool: - return reflect.ValueOf(rand.Int()&1 == 0), true - case reflect.Float32, reflect.Float64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Complex64, reflect.Complex128: - switch t.Kind() { - case reflect.Float32: - return reflect.ValueOf(randFloat32(rand)), true - case reflect.Float64: - return reflect.ValueOf(randFloat64(rand)), true - case reflect.Complex64: - return reflect.ValueOf(complex(randFloat32(rand), randFloat32(rand))), true - case reflect.Complex128: - return reflect.ValueOf(complex(randFloat64(rand), randFloat64(rand))), true - case reflect.Int16: - return reflect.ValueOf(int16(randInt64(rand))), true - case reflect.Int32: - return reflect.ValueOf(int32(randInt64(rand))), true - case reflect.Int64: - return reflect.ValueOf(randInt64(rand)), true - case reflect.Int8: - return reflect.ValueOf(int8(randInt64(rand))), true - case reflect.Int: - return reflect.ValueOf(int(randInt64(rand))), true - case reflect.Uint16: - return reflect.ValueOf(uint16(randInt64(rand))), true - case reflect.Uint32: - return reflect.ValueOf(uint32(randInt64(rand))), true - case reflect.Uint64: - return reflect.ValueOf(uint64(randInt64(rand))), true - case reflect.Uint8: - return reflect.ValueOf(uint8(randInt64(rand))), true - case reflect.Uint: - return reflect.ValueOf(uint(randInt64(rand))), true - case reflect.Uintptr: - return reflect.ValueOf(uintptr(randInt64(rand))), true - } - case reflect.Map: - numElems := rand.Intn(complexSize) - m := reflect.MakeMap(concrete) - for i := 0; i < numElems; i++ { - key, ok1 := Value(concrete.Key(), rand) - value, ok2 := Value(concrete.Elem(), rand) - if !ok1 || !ok2 { - return reflect.Value{}, false - } - m.SetMapIndex(key, value) - } - return m, true - case reflect.Ptr: - v, ok := Value(concrete.Elem(), rand) - if !ok { - return reflect.Value{}, false - } - p := reflect.Zero(concrete) - p.Set(v.Addr()) - return p, true - case reflect.Slice: - numElems := rand.Intn(complexSize) - s := reflect.MakeSlice(concrete, numElems, numElems) - for i := 0; i < numElems; i++ { - v, ok := Value(concrete.Elem(), rand) - if !ok { - return reflect.Value{}, false - } - s.Index(i).Set(v) - } - return s, true - case reflect.String: - numChars := rand.Intn(complexSize) - codePoints := make([]int, numChars) - for i := 0; i < numChars; i++ { - codePoints[i] = rand.Intn(0x10ffff) - } - return reflect.ValueOf(string(codePoints)), true - case reflect.Struct: - s := reflect.Zero(t) - for i := 0; i < s.NumField(); i++ { - v, ok := Value(concrete.Field(i).Type, rand) - if !ok { - return reflect.Value{}, false - } - s.Field(i).Set(v) - } - return s, true - default: - return reflect.Value{}, false - } - - return -} - -// A Config structure contains options for running a test. -type Config struct { - // MaxCount sets the maximum number of iterations. If zero, - // MaxCountScale is used. - MaxCount int - // MaxCountScale is a non-negative scale factor applied to the default - // maximum. If zero, the default is unchanged. - MaxCountScale float64 - // If non-nil, rand is a source of random numbers. Otherwise a default - // pseudo-random source will be used. - Rand *rand.Rand - // If non-nil, Values is a function which generates a slice of arbitrary - // Values that are congruent with the arguments to the function being - // tested. Otherwise, Values is used to generate the values. - Values func([]reflect.Value, *rand.Rand) -} - -var defaultConfig Config - -// getRand returns the *rand.Rand to use for a given Config. -func (c *Config) getRand() *rand.Rand { - if c.Rand == nil { - return rand.New(rand.NewSource(0)) - } - return c.Rand -} - -// getMaxCount returns the maximum number of iterations to run for a given -// Config. -func (c *Config) getMaxCount() (maxCount int) { - maxCount = c.MaxCount - if maxCount == 0 { - if c.MaxCountScale != 0 { - maxCount = int(c.MaxCountScale * float64(*defaultMaxCount)) - } else { - maxCount = *defaultMaxCount - } - } - - return -} - -// A SetupError is the result of an error in the way that check is being -// used, independent of the functions being tested. -type SetupError string - -func (s SetupError) String() string { return string(s) } - -// A CheckError is the result of Check finding an error. -type CheckError struct { - Count int - In []interface{} -} - -func (s *CheckError) String() string { - return fmt.Sprintf("#%d: failed on input %s", s.Count, toString(s.In)) -} - -// A CheckEqualError is the result CheckEqual finding an error. -type CheckEqualError struct { - CheckError - Out1 []interface{} - Out2 []interface{} -} - -func (s *CheckEqualError) String() string { - return fmt.Sprintf("#%d: failed on input %s. Output 1: %s. Output 2: %s", s.Count, toString(s.In), toString(s.Out1), toString(s.Out2)) -} - -// Check looks for an input to f, any function that returns bool, -// such that f returns false. It calls f repeatedly, with arbitrary -// values for each argument. If f returns false on a given input, -// Check returns that input as a *CheckError. -// For example: -// -// func TestOddMultipleOfThree(t *testing.T) { -// f := func(x int) bool { -// y := OddMultipleOfThree(x) -// return y%2 == 1 && y%3 == 0 -// } -// if err := quick.Check(f, nil); err != nil { -// t.Error(err) -// } -// } -func Check(function interface{}, config *Config) (err os.Error) { - if config == nil { - config = &defaultConfig - } - - f, fType, ok := functionAndType(function) - if !ok { - err = SetupError("argument is not a function") - return - } - - if fType.NumOut() != 1 { - err = SetupError("function returns more than one value.") - return - } - if fType.Out(0).Kind() != reflect.Bool { - err = SetupError("function does not return a bool") - return - } - - arguments := make([]reflect.Value, fType.NumIn()) - rand := config.getRand() - maxCount := config.getMaxCount() - - for i := 0; i < maxCount; i++ { - err = arbitraryValues(arguments, fType, config, rand) - if err != nil { - return - } - - if !f.Call(arguments)[0].Bool() { - err = &CheckError{i + 1, toInterfaces(arguments)} - return - } - } - - return -} - -// CheckEqual looks for an input on which f and g return different results. -// It calls f and g repeatedly with arbitrary values for each argument. -// If f and g return different answers, CheckEqual returns a *CheckEqualError -// describing the input and the outputs. -func CheckEqual(f, g interface{}, config *Config) (err os.Error) { - if config == nil { - config = &defaultConfig - } - - x, xType, ok := functionAndType(f) - if !ok { - err = SetupError("f is not a function") - return - } - y, yType, ok := functionAndType(g) - if !ok { - err = SetupError("g is not a function") - return - } - - if xType != yType { - err = SetupError("functions have different types") - return - } - - arguments := make([]reflect.Value, xType.NumIn()) - rand := config.getRand() - maxCount := config.getMaxCount() - - for i := 0; i < maxCount; i++ { - err = arbitraryValues(arguments, xType, config, rand) - if err != nil { - return - } - - xOut := toInterfaces(x.Call(arguments)) - yOut := toInterfaces(y.Call(arguments)) - - if !reflect.DeepEqual(xOut, yOut) { - err = &CheckEqualError{CheckError{i + 1, toInterfaces(arguments)}, xOut, yOut} - return - } - } - - return -} - -// arbitraryValues writes Values to args such that args contains Values -// suitable for calling f. -func arbitraryValues(args []reflect.Value, f reflect.Type, config *Config, rand *rand.Rand) (err os.Error) { - if config.Values != nil { - config.Values(args, rand) - return - } - - for j := 0; j < len(args); j++ { - var ok bool - args[j], ok = Value(f.In(j), rand) - if !ok { - err = SetupError(fmt.Sprintf("cannot create arbitrary value of type %s for argument %d", f.In(j), j)) - return - } - } - - return -} - -func functionAndType(f interface{}) (v reflect.Value, t reflect.Type, ok bool) { - v = reflect.ValueOf(f) - ok = v.Kind() == reflect.Func - if !ok { - return - } - t = v.Type() - return -} - -func toInterfaces(values []reflect.Value) []interface{} { - ret := make([]interface{}, len(values)) - for i, v := range values { - ret[i] = v.Interface() - } - return ret -} - -func toString(interfaces []interface{}) string { - s := make([]string, len(interfaces)) - for i, v := range interfaces { - s[i] = fmt.Sprintf("%#v", v) - } - return strings.Join(s, ", ") -} diff --git a/src/cmd/fix/testdata/reflect.read.go.in b/src/cmd/fix/testdata/reflect.read.go.in deleted file mode 100644 index 487994ac6..000000000 --- a/src/cmd/fix/testdata/reflect.read.go.in +++ /dev/null @@ -1,620 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xml - -import ( - "bytes" - "fmt" - "io" - "os" - "reflect" - "strconv" - "strings" - "unicode" - "utf8" -) - -// BUG(rsc): Mapping between XML elements and data structures is inherently flawed: -// an XML element is an order-dependent collection of anonymous -// values, while a data structure is an order-independent collection -// of named values. -// See package json for a textual representation more suitable -// to data structures. - -// Unmarshal parses an XML element from r and uses the -// reflect library to fill in an arbitrary struct, slice, or string -// pointed at by val. Well-formed data that does not fit -// into val is discarded. -// -// For example, given these definitions: -// -// type Email struct { -// Where string "attr" -// Addr string -// } -// -// type Result struct { -// XMLName xml.Name "result" -// Name string -// Phone string -// Email []Email -// Groups []string "group>value" -// } -// -// result := Result{Name: "name", Phone: "phone", Email: nil} -// -// unmarshalling the XML input -// -// <result> -// <email where="home"> -// <addr>gre@example.com</addr> -// </email> -// <email where='work'> -// <addr>gre@work.com</addr> -// </email> -// <name>Grace R. Emlin</name> -// <group> -// <value>Friends</value> -// <value>Squash</value> -// </group> -// <address>123 Main Street</address> -// </result> -// -// via Unmarshal(r, &result) is equivalent to assigning -// -// r = Result{xml.Name{"", "result"}, -// "Grace R. Emlin", // name -// "phone", // no phone given -// []Email{ -// Email{"home", "gre@example.com"}, -// Email{"work", "gre@work.com"}, -// }, -// []string{"Friends", "Squash"}, -// } -// -// Note that the field r.Phone has not been modified and -// that the XML <address> element was discarded. Also, the field -// Groups was assigned considering the element path provided in the -// field tag. -// -// Because Unmarshal uses the reflect package, it can only -// assign to upper case fields. Unmarshal uses a case-insensitive -// comparison to match XML element names to struct field names. -// -// Unmarshal maps an XML element to a struct using the following rules: -// -// * If the struct has a field of type []byte or string with tag "innerxml", -// Unmarshal accumulates the raw XML nested inside the element -// in that field. The rest of the rules still apply. -// -// * If the struct has a field named XMLName of type xml.Name, -// Unmarshal records the element name in that field. -// -// * If the XMLName field has an associated tag string of the form -// "tag" or "namespace-URL tag", the XML element must have -// the given tag (and, optionally, name space) or else Unmarshal -// returns an error. -// -// * If the XML element has an attribute whose name matches a -// struct field of type string with tag "attr", Unmarshal records -// the attribute value in that field. -// -// * If the XML element contains character data, that data is -// accumulated in the first struct field that has tag "chardata". -// The struct field may have type []byte or string. -// If there is no such field, the character data is discarded. -// -// * If the XML element contains a sub-element whose name matches -// the prefix of a struct field tag formatted as "a>b>c", unmarshal -// will descend into the XML structure looking for elements with the -// given names, and will map the innermost elements to that struct field. -// A struct field tag starting with ">" is equivalent to one starting -// with the field name followed by ">". -// -// * If the XML element contains a sub-element whose name -// matches a struct field whose tag is neither "attr" nor "chardata", -// Unmarshal maps the sub-element to that struct field. -// Otherwise, if the struct has a field named Any, unmarshal -// maps the sub-element to that struct field. -// -// Unmarshal maps an XML element to a string or []byte by saving the -// concatenation of that element's character data in the string or []byte. -// -// Unmarshal maps an XML element to a slice by extending the length -// of the slice and mapping the element to the newly created value. -// -// Unmarshal maps an XML element to a bool by setting it to the boolean -// value represented by the string. -// -// Unmarshal maps an XML element to an integer or floating-point -// field by setting the field to the result of interpreting the string -// value in decimal. There is no check for overflow. -// -// Unmarshal maps an XML element to an xml.Name by recording the -// element name. -// -// Unmarshal maps an XML element to a pointer by setting the pointer -// to a freshly allocated value and then mapping the element to that value. -// -func Unmarshal(r io.Reader, val interface{}) os.Error { - v, ok := reflect.NewValue(val).(*reflect.PtrValue) - if !ok { - return os.NewError("non-pointer passed to Unmarshal") - } - p := NewParser(r) - elem := v.Elem() - err := p.unmarshal(elem, nil) - if err != nil { - return err - } - return nil -} - -// An UnmarshalError represents an error in the unmarshalling process. -type UnmarshalError string - -func (e UnmarshalError) String() string { return string(e) } - -// A TagPathError represents an error in the unmarshalling process -// caused by the use of field tags with conflicting paths. -type TagPathError struct { - Struct reflect.Type - Field1, Tag1 string - Field2, Tag2 string -} - -func (e *TagPathError) String() string { - return fmt.Sprintf("%s field %q with tag %q conflicts with field %q with tag %q", e.Struct, e.Field1, e.Tag1, e.Field2, e.Tag2) -} - -// The Parser's Unmarshal method is like xml.Unmarshal -// except that it can be passed a pointer to the initial start element, -// useful when a client reads some raw XML tokens itself -// but also defers to Unmarshal for some elements. -// Passing a nil start element indicates that Unmarshal should -// read the token stream to find the start element. -func (p *Parser) Unmarshal(val interface{}, start *StartElement) os.Error { - v, ok := reflect.NewValue(val).(*reflect.PtrValue) - if !ok { - return os.NewError("non-pointer passed to Unmarshal") - } - return p.unmarshal(v.Elem(), start) -} - -// fieldName strips invalid characters from an XML name -// to create a valid Go struct name. It also converts the -// name to lower case letters. -func fieldName(original string) string { - - var i int - //remove leading underscores - for i = 0; i < len(original) && original[i] == '_'; i++ { - } - - return strings.Map( - func(x int) int { - if x == '_' || unicode.IsDigit(x) || unicode.IsLetter(x) { - return unicode.ToLower(x) - } - return -1 - }, - original[i:]) -} - -// Unmarshal a single XML element into val. -func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error { - // Find start element if we need it. - if start == nil { - for { - tok, err := p.Token() - if err != nil { - return err - } - if t, ok := tok.(StartElement); ok { - start = &t - break - } - } - } - - if pv, ok := val.(*reflect.PtrValue); ok { - if pv.Get() == 0 { - zv := reflect.MakeZero(pv.Type().(*reflect.PtrType).Elem()) - pv.PointTo(zv) - val = zv - } else { - val = pv.Elem() - } - } - - var ( - data []byte - saveData reflect.Value - comment []byte - saveComment reflect.Value - saveXML reflect.Value - saveXMLIndex int - saveXMLData []byte - sv *reflect.StructValue - styp *reflect.StructType - fieldPaths map[string]pathInfo - ) - - switch v := val.(type) { - default: - return os.NewError("unknown type " + v.Type().String()) - - case *reflect.SliceValue: - typ := v.Type().(*reflect.SliceType) - if typ.Elem().Kind() == reflect.Uint8 { - // []byte - saveData = v - break - } - - // Slice of element values. - // Grow slice. - n := v.Len() - if n >= v.Cap() { - ncap := 2 * n - if ncap < 4 { - ncap = 4 - } - new := reflect.MakeSlice(typ, n, ncap) - reflect.Copy(new, v) - v.Set(new) - } - v.SetLen(n + 1) - - // Recur to read element into slice. - if err := p.unmarshal(v.Elem(n), start); err != nil { - v.SetLen(n) - return err - } - return nil - - case *reflect.BoolValue, *reflect.FloatValue, *reflect.IntValue, *reflect.UintValue, *reflect.StringValue: - saveData = v - - case *reflect.StructValue: - if _, ok := v.Interface().(Name); ok { - v.Set(reflect.NewValue(start.Name).(*reflect.StructValue)) - break - } - - sv = v - typ := sv.Type().(*reflect.StructType) - styp = typ - // Assign name. - if f, ok := typ.FieldByName("XMLName"); ok { - // Validate element name. - if f.Tag != "" { - tag := f.Tag - ns := "" - i := strings.LastIndex(tag, " ") - if i >= 0 { - ns, tag = tag[0:i], tag[i+1:] - } - if tag != start.Name.Local { - return UnmarshalError("expected element type <" + tag + "> but have <" + start.Name.Local + ">") - } - if ns != "" && ns != start.Name.Space { - e := "expected element <" + tag + "> in name space " + ns + " but have " - if start.Name.Space == "" { - e += "no name space" - } else { - e += start.Name.Space - } - return UnmarshalError(e) - } - } - - // Save - v := sv.FieldByIndex(f.Index) - if _, ok := v.Interface().(Name); !ok { - return UnmarshalError(sv.Type().String() + " field XMLName does not have type xml.Name") - } - v.(*reflect.StructValue).Set(reflect.NewValue(start.Name).(*reflect.StructValue)) - } - - // Assign attributes. - // Also, determine whether we need to save character data or comments. - for i, n := 0, typ.NumField(); i < n; i++ { - f := typ.Field(i) - switch f.Tag { - case "attr": - strv, ok := sv.FieldByIndex(f.Index).(*reflect.StringValue) - if !ok { - return UnmarshalError(sv.Type().String() + " field " + f.Name + " has attr tag but is not type string") - } - // Look for attribute. - val := "" - k := strings.ToLower(f.Name) - for _, a := range start.Attr { - if fieldName(a.Name.Local) == k { - val = a.Value - break - } - } - strv.Set(val) - - case "comment": - if saveComment == nil { - saveComment = sv.FieldByIndex(f.Index) - } - - case "chardata": - if saveData == nil { - saveData = sv.FieldByIndex(f.Index) - } - - case "innerxml": - if saveXML == nil { - saveXML = sv.FieldByIndex(f.Index) - if p.saved == nil { - saveXMLIndex = 0 - p.saved = new(bytes.Buffer) - } else { - saveXMLIndex = p.savedOffset() - } - } - - default: - if strings.Contains(f.Tag, ">") { - if fieldPaths == nil { - fieldPaths = make(map[string]pathInfo) - } - path := strings.ToLower(f.Tag) - if strings.HasPrefix(f.Tag, ">") { - path = strings.ToLower(f.Name) + path - } - if strings.HasSuffix(f.Tag, ">") { - path = path[:len(path)-1] - } - err := addFieldPath(sv, fieldPaths, path, f.Index) - if err != nil { - return err - } - } - } - } - } - - // Find end element. - // Process sub-elements along the way. -Loop: - for { - var savedOffset int - if saveXML != nil { - savedOffset = p.savedOffset() - } - tok, err := p.Token() - if err != nil { - return err - } - switch t := tok.(type) { - case StartElement: - // Sub-element. - // Look up by tag name. - if sv != nil { - k := fieldName(t.Name.Local) - - if fieldPaths != nil { - if _, found := fieldPaths[k]; found { - if err := p.unmarshalPaths(sv, fieldPaths, k, &t); err != nil { - return err - } - continue Loop - } - } - - match := func(s string) bool { - // check if the name matches ignoring case - if strings.ToLower(s) != k { - return false - } - // now check that it's public - c, _ := utf8.DecodeRuneInString(s) - return unicode.IsUpper(c) - } - - f, found := styp.FieldByNameFunc(match) - if !found { // fall back to mop-up field named "Any" - f, found = styp.FieldByName("Any") - } - if found { - if err := p.unmarshal(sv.FieldByIndex(f.Index), &t); err != nil { - return err - } - continue Loop - } - } - // Not saving sub-element but still have to skip over it. - if err := p.Skip(); err != nil { - return err - } - - case EndElement: - if saveXML != nil { - saveXMLData = p.saved.Bytes()[saveXMLIndex:savedOffset] - if saveXMLIndex == 0 { - p.saved = nil - } - } - break Loop - - case CharData: - if saveData != nil { - data = append(data, t...) - } - - case Comment: - if saveComment != nil { - comment = append(comment, t...) - } - } - } - - var err os.Error - // Helper functions for integer and unsigned integer conversions - var itmp int64 - getInt64 := func() bool { - itmp, err = strconv.Atoi64(string(data)) - // TODO: should check sizes - return err == nil - } - var utmp uint64 - getUint64 := func() bool { - utmp, err = strconv.Atoui64(string(data)) - // TODO: check for overflow? - return err == nil - } - var ftmp float64 - getFloat64 := func() bool { - ftmp, err = strconv.Atof64(string(data)) - // TODO: check for overflow? - return err == nil - } - - // Save accumulated data and comments - switch t := saveData.(type) { - case nil: - // Probably a comment, handled below - default: - return os.NewError("cannot happen: unknown type " + t.Type().String()) - case *reflect.IntValue: - if !getInt64() { - return err - } - t.Set(itmp) - case *reflect.UintValue: - if !getUint64() { - return err - } - t.Set(utmp) - case *reflect.FloatValue: - if !getFloat64() { - return err - } - t.Set(ftmp) - case *reflect.BoolValue: - value, err := strconv.Atob(strings.TrimSpace(string(data))) - if err != nil { - return err - } - t.Set(value) - case *reflect.StringValue: - t.Set(string(data)) - case *reflect.SliceValue: - t.Set(reflect.NewValue(data).(*reflect.SliceValue)) - } - - switch t := saveComment.(type) { - case *reflect.StringValue: - t.Set(string(comment)) - case *reflect.SliceValue: - t.Set(reflect.NewValue(comment).(*reflect.SliceValue)) - } - - switch t := saveXML.(type) { - case *reflect.StringValue: - t.Set(string(saveXMLData)) - case *reflect.SliceValue: - t.Set(reflect.NewValue(saveXMLData).(*reflect.SliceValue)) - } - - return nil -} - -type pathInfo struct { - fieldIdx []int - complete bool -} - -// addFieldPath takes an element path such as "a>b>c" and fills the -// paths map with all paths leading to it ("a", "a>b", and "a>b>c"). -// It is okay for paths to share a common, shorter prefix but not ok -// for one path to itself be a prefix of another. -func addFieldPath(sv *reflect.StructValue, paths map[string]pathInfo, path string, fieldIdx []int) os.Error { - if info, found := paths[path]; found { - return tagError(sv, info.fieldIdx, fieldIdx) - } - paths[path] = pathInfo{fieldIdx, true} - for { - i := strings.LastIndex(path, ">") - if i < 0 { - break - } - path = path[:i] - if info, found := paths[path]; found { - if info.complete { - return tagError(sv, info.fieldIdx, fieldIdx) - } - } else { - paths[path] = pathInfo{fieldIdx, false} - } - } - return nil - -} - -func tagError(sv *reflect.StructValue, idx1 []int, idx2 []int) os.Error { - t := sv.Type().(*reflect.StructType) - f1 := t.FieldByIndex(idx1) - f2 := t.FieldByIndex(idx2) - return &TagPathError{t, f1.Name, f1.Tag, f2.Name, f2.Tag} -} - -// unmarshalPaths walks down an XML structure looking for -// wanted paths, and calls unmarshal on them. -func (p *Parser) unmarshalPaths(sv *reflect.StructValue, paths map[string]pathInfo, path string, start *StartElement) os.Error { - if info, _ := paths[path]; info.complete { - return p.unmarshal(sv.FieldByIndex(info.fieldIdx), start) - } - for { - tok, err := p.Token() - if err != nil { - return err - } - switch t := tok.(type) { - case StartElement: - k := path + ">" + fieldName(t.Name.Local) - if _, found := paths[k]; found { - if err := p.unmarshalPaths(sv, paths, k, &t); err != nil { - return err - } - continue - } - if err := p.Skip(); err != nil { - return err - } - case EndElement: - return nil - } - } - panic("unreachable") -} - -// Have already read a start element. -// Read tokens until we find the end element. -// Token is taking care of making sure the -// end element matches the start element we saw. -func (p *Parser) Skip() os.Error { - for { - tok, err := p.Token() - if err != nil { - return err - } - switch t := tok.(type) { - case StartElement: - if err := p.Skip(); err != nil { - return err - } - case EndElement: - return nil - } - } - panic("unreachable") -} diff --git a/src/cmd/fix/testdata/reflect.read.go.out b/src/cmd/fix/testdata/reflect.read.go.out deleted file mode 100644 index a6b126744..000000000 --- a/src/cmd/fix/testdata/reflect.read.go.out +++ /dev/null @@ -1,620 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xml - -import ( - "bytes" - "fmt" - "io" - "os" - "reflect" - "strconv" - "strings" - "unicode" - "utf8" -) - -// BUG(rsc): Mapping between XML elements and data structures is inherently flawed: -// an XML element is an order-dependent collection of anonymous -// values, while a data structure is an order-independent collection -// of named values. -// See package json for a textual representation more suitable -// to data structures. - -// Unmarshal parses an XML element from r and uses the -// reflect library to fill in an arbitrary struct, slice, or string -// pointed at by val. Well-formed data that does not fit -// into val is discarded. -// -// For example, given these definitions: -// -// type Email struct { -// Where string "attr" -// Addr string -// } -// -// type Result struct { -// XMLName xml.Name "result" -// Name string -// Phone string -// Email []Email -// Groups []string "group>value" -// } -// -// result := Result{Name: "name", Phone: "phone", Email: nil} -// -// unmarshalling the XML input -// -// <result> -// <email where="home"> -// <addr>gre@example.com</addr> -// </email> -// <email where='work'> -// <addr>gre@work.com</addr> -// </email> -// <name>Grace R. Emlin</name> -// <group> -// <value>Friends</value> -// <value>Squash</value> -// </group> -// <address>123 Main Street</address> -// </result> -// -// via Unmarshal(r, &result) is equivalent to assigning -// -// r = Result{xml.Name{"", "result"}, -// "Grace R. Emlin", // name -// "phone", // no phone given -// []Email{ -// Email{"home", "gre@example.com"}, -// Email{"work", "gre@work.com"}, -// }, -// []string{"Friends", "Squash"}, -// } -// -// Note that the field r.Phone has not been modified and -// that the XML <address> element was discarded. Also, the field -// Groups was assigned considering the element path provided in the -// field tag. -// -// Because Unmarshal uses the reflect package, it can only -// assign to upper case fields. Unmarshal uses a case-insensitive -// comparison to match XML element names to struct field names. -// -// Unmarshal maps an XML element to a struct using the following rules: -// -// * If the struct has a field of type []byte or string with tag "innerxml", -// Unmarshal accumulates the raw XML nested inside the element -// in that field. The rest of the rules still apply. -// -// * If the struct has a field named XMLName of type xml.Name, -// Unmarshal records the element name in that field. -// -// * If the XMLName field has an associated tag string of the form -// "tag" or "namespace-URL tag", the XML element must have -// the given tag (and, optionally, name space) or else Unmarshal -// returns an error. -// -// * If the XML element has an attribute whose name matches a -// struct field of type string with tag "attr", Unmarshal records -// the attribute value in that field. -// -// * If the XML element contains character data, that data is -// accumulated in the first struct field that has tag "chardata". -// The struct field may have type []byte or string. -// If there is no such field, the character data is discarded. -// -// * If the XML element contains a sub-element whose name matches -// the prefix of a struct field tag formatted as "a>b>c", unmarshal -// will descend into the XML structure looking for elements with the -// given names, and will map the innermost elements to that struct field. -// A struct field tag starting with ">" is equivalent to one starting -// with the field name followed by ">". -// -// * If the XML element contains a sub-element whose name -// matches a struct field whose tag is neither "attr" nor "chardata", -// Unmarshal maps the sub-element to that struct field. -// Otherwise, if the struct has a field named Any, unmarshal -// maps the sub-element to that struct field. -// -// Unmarshal maps an XML element to a string or []byte by saving the -// concatenation of that element's character data in the string or []byte. -// -// Unmarshal maps an XML element to a slice by extending the length -// of the slice and mapping the element to the newly created value. -// -// Unmarshal maps an XML element to a bool by setting it to the boolean -// value represented by the string. -// -// Unmarshal maps an XML element to an integer or floating-point -// field by setting the field to the result of interpreting the string -// value in decimal. There is no check for overflow. -// -// Unmarshal maps an XML element to an xml.Name by recording the -// element name. -// -// Unmarshal maps an XML element to a pointer by setting the pointer -// to a freshly allocated value and then mapping the element to that value. -// -func Unmarshal(r io.Reader, val interface{}) os.Error { - v := reflect.ValueOf(val) - if v.Kind() != reflect.Ptr { - return os.NewError("non-pointer passed to Unmarshal") - } - p := NewParser(r) - elem := v.Elem() - err := p.unmarshal(elem, nil) - if err != nil { - return err - } - return nil -} - -// An UnmarshalError represents an error in the unmarshalling process. -type UnmarshalError string - -func (e UnmarshalError) String() string { return string(e) } - -// A TagPathError represents an error in the unmarshalling process -// caused by the use of field tags with conflicting paths. -type TagPathError struct { - Struct reflect.Type - Field1, Tag1 string - Field2, Tag2 string -} - -func (e *TagPathError) String() string { - return fmt.Sprintf("%s field %q with tag %q conflicts with field %q with tag %q", e.Struct, e.Field1, e.Tag1, e.Field2, e.Tag2) -} - -// The Parser's Unmarshal method is like xml.Unmarshal -// except that it can be passed a pointer to the initial start element, -// useful when a client reads some raw XML tokens itself -// but also defers to Unmarshal for some elements. -// Passing a nil start element indicates that Unmarshal should -// read the token stream to find the start element. -func (p *Parser) Unmarshal(val interface{}, start *StartElement) os.Error { - v := reflect.ValueOf(val) - if v.Kind() != reflect.Ptr { - return os.NewError("non-pointer passed to Unmarshal") - } - return p.unmarshal(v.Elem(), start) -} - -// fieldName strips invalid characters from an XML name -// to create a valid Go struct name. It also converts the -// name to lower case letters. -func fieldName(original string) string { - - var i int - //remove leading underscores - for i = 0; i < len(original) && original[i] == '_'; i++ { - } - - return strings.Map( - func(x int) int { - if x == '_' || unicode.IsDigit(x) || unicode.IsLetter(x) { - return unicode.ToLower(x) - } - return -1 - }, - original[i:]) -} - -// Unmarshal a single XML element into val. -func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error { - // Find start element if we need it. - if start == nil { - for { - tok, err := p.Token() - if err != nil { - return err - } - if t, ok := tok.(StartElement); ok { - start = &t - break - } - } - } - - if pv := val; pv.Kind() == reflect.Ptr { - if pv.Pointer() == 0 { - zv := reflect.Zero(pv.Type().Elem()) - pv.Set(zv.Addr()) - val = zv - } else { - val = pv.Elem() - } - } - - var ( - data []byte - saveData reflect.Value - comment []byte - saveComment reflect.Value - saveXML reflect.Value - saveXMLIndex int - saveXMLData []byte - sv reflect.Value - styp reflect.Type - fieldPaths map[string]pathInfo - ) - - switch v := val; v.Kind() { - default: - return os.NewError("unknown type " + v.Type().String()) - - case reflect.Slice: - typ := v.Type() - if typ.Elem().Kind() == reflect.Uint8 { - // []byte - saveData = v - break - } - - // Slice of element values. - // Grow slice. - n := v.Len() - if n >= v.Cap() { - ncap := 2 * n - if ncap < 4 { - ncap = 4 - } - new := reflect.MakeSlice(typ, n, ncap) - reflect.Copy(new, v) - v.Set(new) - } - v.SetLen(n + 1) - - // Recur to read element into slice. - if err := p.unmarshal(v.Index(n), start); err != nil { - v.SetLen(n) - return err - } - return nil - - case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.String: - saveData = v - - case reflect.Struct: - if _, ok := v.Interface().(Name); ok { - v.Set(reflect.ValueOf(start.Name)) - break - } - - sv = v - typ := sv.Type() - styp = typ - // Assign name. - if f, ok := typ.FieldByName("XMLName"); ok { - // Validate element name. - if f.Tag != "" { - tag := f.Tag - ns := "" - i := strings.LastIndex(tag, " ") - if i >= 0 { - ns, tag = tag[0:i], tag[i+1:] - } - if tag != start.Name.Local { - return UnmarshalError("expected element type <" + tag + "> but have <" + start.Name.Local + ">") - } - if ns != "" && ns != start.Name.Space { - e := "expected element <" + tag + "> in name space " + ns + " but have " - if start.Name.Space == "" { - e += "no name space" - } else { - e += start.Name.Space - } - return UnmarshalError(e) - } - } - - // Save - v := sv.FieldByIndex(f.Index) - if _, ok := v.Interface().(Name); !ok { - return UnmarshalError(sv.Type().String() + " field XMLName does not have type xml.Name") - } - v.Set(reflect.ValueOf(start.Name)) - } - - // Assign attributes. - // Also, determine whether we need to save character data or comments. - for i, n := 0, typ.NumField(); i < n; i++ { - f := typ.Field(i) - switch f.Tag { - case "attr": - strv := sv.FieldByIndex(f.Index) - if strv.Kind() != reflect.String { - return UnmarshalError(sv.Type().String() + " field " + f.Name + " has attr tag but is not type string") - } - // Look for attribute. - val := "" - k := strings.ToLower(f.Name) - for _, a := range start.Attr { - if fieldName(a.Name.Local) == k { - val = a.Value - break - } - } - strv.SetString(val) - - case "comment": - if !saveComment.IsValid() { - saveComment = sv.FieldByIndex(f.Index) - } - - case "chardata": - if !saveData.IsValid() { - saveData = sv.FieldByIndex(f.Index) - } - - case "innerxml": - if !saveXML.IsValid() { - saveXML = sv.FieldByIndex(f.Index) - if p.saved == nil { - saveXMLIndex = 0 - p.saved = new(bytes.Buffer) - } else { - saveXMLIndex = p.savedOffset() - } - } - - default: - if strings.Contains(f.Tag, ">") { - if fieldPaths == nil { - fieldPaths = make(map[string]pathInfo) - } - path := strings.ToLower(f.Tag) - if strings.HasPrefix(f.Tag, ">") { - path = strings.ToLower(f.Name) + path - } - if strings.HasSuffix(f.Tag, ">") { - path = path[:len(path)-1] - } - err := addFieldPath(sv, fieldPaths, path, f.Index) - if err != nil { - return err - } - } - } - } - } - - // Find end element. - // Process sub-elements along the way. -Loop: - for { - var savedOffset int - if saveXML.IsValid() { - savedOffset = p.savedOffset() - } - tok, err := p.Token() - if err != nil { - return err - } - switch t := tok.(type) { - case StartElement: - // Sub-element. - // Look up by tag name. - if sv.IsValid() { - k := fieldName(t.Name.Local) - - if fieldPaths != nil { - if _, found := fieldPaths[k]; found { - if err := p.unmarshalPaths(sv, fieldPaths, k, &t); err != nil { - return err - } - continue Loop - } - } - - match := func(s string) bool { - // check if the name matches ignoring case - if strings.ToLower(s) != k { - return false - } - // now check that it's public - c, _ := utf8.DecodeRuneInString(s) - return unicode.IsUpper(c) - } - - f, found := styp.FieldByNameFunc(match) - if !found { // fall back to mop-up field named "Any" - f, found = styp.FieldByName("Any") - } - if found { - if err := p.unmarshal(sv.FieldByIndex(f.Index), &t); err != nil { - return err - } - continue Loop - } - } - // Not saving sub-element but still have to skip over it. - if err := p.Skip(); err != nil { - return err - } - - case EndElement: - if saveXML.IsValid() { - saveXMLData = p.saved.Bytes()[saveXMLIndex:savedOffset] - if saveXMLIndex == 0 { - p.saved = nil - } - } - break Loop - - case CharData: - if saveData.IsValid() { - data = append(data, t...) - } - - case Comment: - if saveComment.IsValid() { - comment = append(comment, t...) - } - } - } - - var err os.Error - // Helper functions for integer and unsigned integer conversions - var itmp int64 - getInt64 := func() bool { - itmp, err = strconv.Atoi64(string(data)) - // TODO: should check sizes - return err == nil - } - var utmp uint64 - getUint64 := func() bool { - utmp, err = strconv.Atoui64(string(data)) - // TODO: check for overflow? - return err == nil - } - var ftmp float64 - getFloat64 := func() bool { - ftmp, err = strconv.Atof64(string(data)) - // TODO: check for overflow? - return err == nil - } - - // Save accumulated data and comments - switch t := saveData; t.Kind() { - case reflect.Invalid: - // Probably a comment, handled below - default: - return os.NewError("cannot happen: unknown type " + t.Type().String()) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - if !getInt64() { - return err - } - t.SetInt(itmp) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - if !getUint64() { - return err - } - t.SetUint(utmp) - case reflect.Float32, reflect.Float64: - if !getFloat64() { - return err - } - t.SetFloat(ftmp) - case reflect.Bool: - value, err := strconv.Atob(strings.TrimSpace(string(data))) - if err != nil { - return err - } - t.SetBool(value) - case reflect.String: - t.SetString(string(data)) - case reflect.Slice: - t.Set(reflect.ValueOf(data)) - } - - switch t := saveComment; t.Kind() { - case reflect.String: - t.SetString(string(comment)) - case reflect.Slice: - t.Set(reflect.ValueOf(comment)) - } - - switch t := saveXML; t.Kind() { - case reflect.String: - t.SetString(string(saveXMLData)) - case reflect.Slice: - t.Set(reflect.ValueOf(saveXMLData)) - } - - return nil -} - -type pathInfo struct { - fieldIdx []int - complete bool -} - -// addFieldPath takes an element path such as "a>b>c" and fills the -// paths map with all paths leading to it ("a", "a>b", and "a>b>c"). -// It is okay for paths to share a common, shorter prefix but not ok -// for one path to itself be a prefix of another. -func addFieldPath(sv reflect.Value, paths map[string]pathInfo, path string, fieldIdx []int) os.Error { - if info, found := paths[path]; found { - return tagError(sv, info.fieldIdx, fieldIdx) - } - paths[path] = pathInfo{fieldIdx, true} - for { - i := strings.LastIndex(path, ">") - if i < 0 { - break - } - path = path[:i] - if info, found := paths[path]; found { - if info.complete { - return tagError(sv, info.fieldIdx, fieldIdx) - } - } else { - paths[path] = pathInfo{fieldIdx, false} - } - } - return nil - -} - -func tagError(sv reflect.Value, idx1 []int, idx2 []int) os.Error { - t := sv.Type() - f1 := t.FieldByIndex(idx1) - f2 := t.FieldByIndex(idx2) - return &TagPathError{t, f1.Name, f1.Tag, f2.Name, f2.Tag} -} - -// unmarshalPaths walks down an XML structure looking for -// wanted paths, and calls unmarshal on them. -func (p *Parser) unmarshalPaths(sv reflect.Value, paths map[string]pathInfo, path string, start *StartElement) os.Error { - if info, _ := paths[path]; info.complete { - return p.unmarshal(sv.FieldByIndex(info.fieldIdx), start) - } - for { - tok, err := p.Token() - if err != nil { - return err - } - switch t := tok.(type) { - case StartElement: - k := path + ">" + fieldName(t.Name.Local) - if _, found := paths[k]; found { - if err := p.unmarshalPaths(sv, paths, k, &t); err != nil { - return err - } - continue - } - if err := p.Skip(); err != nil { - return err - } - case EndElement: - return nil - } - } - panic("unreachable") -} - -// Have already read a start element. -// Read tokens until we find the end element. -// Token is taking care of making sure the -// end element matches the start element we saw. -func (p *Parser) Skip() os.Error { - for { - tok, err := p.Token() - if err != nil { - return err - } - switch t := tok.(type) { - case StartElement: - if err := p.Skip(); err != nil { - return err - } - case EndElement: - return nil - } - } - panic("unreachable") -} diff --git a/src/cmd/fix/testdata/reflect.scan.go.in b/src/cmd/fix/testdata/reflect.scan.go.in deleted file mode 100644 index 51898181f..000000000 --- a/src/cmd/fix/testdata/reflect.scan.go.in +++ /dev/null @@ -1,1082 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package fmt - -import ( - "bytes" - "io" - "math" - "os" - "reflect" - "strconv" - "strings" - "unicode" - "utf8" -) - -// runeUnreader is the interface to something that can unread runes. -// If the object provided to Scan does not satisfy this interface, -// a local buffer will be used to back up the input, but its contents -// will be lost when Scan returns. -type runeUnreader interface { - UnreadRune() os.Error -} - -// ScanState represents the scanner state passed to custom scanners. -// Scanners may do rune-at-a-time scanning or ask the ScanState -// to discover the next space-delimited token. -type ScanState interface { - // ReadRune reads the next rune (Unicode code point) from the input. - // If invoked during Scanln, Fscanln, or Sscanln, ReadRune() will - // return EOF after returning the first '\n' or when reading beyond - // the specified width. - ReadRune() (rune int, size int, err os.Error) - // UnreadRune causes the next call to ReadRune to return the same rune. - UnreadRune() os.Error - // Token skips space in the input if skipSpace is true, then returns the - // run of Unicode code points c satisfying f(c). If f is nil, - // !unicode.IsSpace(c) is used; that is, the token will hold non-space - // characters. Newlines are treated as space unless the scan operation - // is Scanln, Fscanln or Sscanln, in which case a newline is treated as - // EOF. The returned slice points to shared data that may be overwritten - // by the next call to Token, a call to a Scan function using the ScanState - // as input, or when the calling Scan method returns. - Token(skipSpace bool, f func(int) bool) (token []byte, err os.Error) - // Width returns the value of the width option and whether it has been set. - // The unit is Unicode code points. - Width() (wid int, ok bool) - // Because ReadRune is implemented by the interface, Read should never be - // called by the scanning routines and a valid implementation of - // ScanState may choose always to return an error from Read. - Read(buf []byte) (n int, err os.Error) -} - -// Scanner is implemented by any value that has a Scan method, which scans -// the input for the representation of a value and stores the result in the -// receiver, which must be a pointer to be useful. The Scan method is called -// for any argument to Scan, Scanf, or Scanln that implements it. -type Scanner interface { - Scan(state ScanState, verb int) os.Error -} - -// Scan scans text read from standard input, storing successive -// space-separated values into successive arguments. Newlines count -// as space. It returns the number of items successfully scanned. -// If that is less than the number of arguments, err will report why. -func Scan(a ...interface{}) (n int, err os.Error) { - return Fscan(os.Stdin, a...) -} - -// Scanln is similar to Scan, but stops scanning at a newline and -// after the final item there must be a newline or EOF. -func Scanln(a ...interface{}) (n int, err os.Error) { - return Fscanln(os.Stdin, a...) -} - -// Scanf scans text read from standard input, storing successive -// space-separated values into successive arguments as determined by -// the format. It returns the number of items successfully scanned. -func Scanf(format string, a ...interface{}) (n int, err os.Error) { - return Fscanf(os.Stdin, format, a...) -} - -// Sscan scans the argument string, storing successive space-separated -// values into successive arguments. Newlines count as space. It -// returns the number of items successfully scanned. If that is less -// than the number of arguments, err will report why. -func Sscan(str string, a ...interface{}) (n int, err os.Error) { - return Fscan(strings.NewReader(str), a...) -} - -// Sscanln is similar to Sscan, but stops scanning at a newline and -// after the final item there must be a newline or EOF. -func Sscanln(str string, a ...interface{}) (n int, err os.Error) { - return Fscanln(strings.NewReader(str), a...) -} - -// Sscanf scans the argument string, storing successive space-separated -// values into successive arguments as determined by the format. It -// returns the number of items successfully parsed. -func Sscanf(str string, format string, a ...interface{}) (n int, err os.Error) { - return Fscanf(strings.NewReader(str), format, a...) -} - -// Fscan scans text read from r, storing successive space-separated -// values into successive arguments. Newlines count as space. It -// returns the number of items successfully scanned. If that is less -// than the number of arguments, err will report why. -func Fscan(r io.Reader, a ...interface{}) (n int, err os.Error) { - s, old := newScanState(r, true, false) - n, err = s.doScan(a) - s.free(old) - return -} - -// Fscanln is similar to Fscan, but stops scanning at a newline and -// after the final item there must be a newline or EOF. -func Fscanln(r io.Reader, a ...interface{}) (n int, err os.Error) { - s, old := newScanState(r, false, true) - n, err = s.doScan(a) - s.free(old) - return -} - -// Fscanf scans text read from r, storing successive space-separated -// values into successive arguments as determined by the format. It -// returns the number of items successfully parsed. -func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err os.Error) { - s, old := newScanState(r, false, false) - n, err = s.doScanf(format, a) - s.free(old) - return -} - -// scanError represents an error generated by the scanning software. -// It's used as a unique signature to identify such errors when recovering. -type scanError struct { - err os.Error -} - -const eof = -1 - -// ss is the internal implementation of ScanState. -type ss struct { - rr io.RuneReader // where to read input - buf bytes.Buffer // token accumulator - peekRune int // one-rune lookahead - prevRune int // last rune returned by ReadRune - count int // runes consumed so far. - atEOF bool // already read EOF - ssave -} - -// ssave holds the parts of ss that need to be -// saved and restored on recursive scans. -type ssave struct { - validSave bool // is or was a part of an actual ss. - nlIsEnd bool // whether newline terminates scan - nlIsSpace bool // whether newline counts as white space - fieldLimit int // max value of ss.count for this field; fieldLimit <= limit - limit int // max value of ss.count. - maxWid int // width of this field. -} - -// The Read method is only in ScanState so that ScanState -// satisfies io.Reader. It will never be called when used as -// intended, so there is no need to make it actually work. -func (s *ss) Read(buf []byte) (n int, err os.Error) { - return 0, os.NewError("ScanState's Read should not be called. Use ReadRune") -} - -func (s *ss) ReadRune() (rune int, size int, err os.Error) { - if s.peekRune >= 0 { - s.count++ - rune = s.peekRune - size = utf8.RuneLen(rune) - s.prevRune = rune - s.peekRune = -1 - return - } - if s.atEOF || s.nlIsEnd && s.prevRune == '\n' || s.count >= s.fieldLimit { - err = os.EOF - return - } - - rune, size, err = s.rr.ReadRune() - if err == nil { - s.count++ - s.prevRune = rune - } else if err == os.EOF { - s.atEOF = true - } - return -} - -func (s *ss) Width() (wid int, ok bool) { - if s.maxWid == hugeWid { - return 0, false - } - return s.maxWid, true -} - -// The public method returns an error; this private one panics. -// If getRune reaches EOF, the return value is EOF (-1). -func (s *ss) getRune() (rune int) { - rune, _, err := s.ReadRune() - if err != nil { - if err == os.EOF { - return eof - } - s.error(err) - } - return -} - -// mustReadRune turns os.EOF into a panic(io.ErrUnexpectedEOF). -// It is called in cases such as string scanning where an EOF is a -// syntax error. -func (s *ss) mustReadRune() (rune int) { - rune = s.getRune() - if rune == eof { - s.error(io.ErrUnexpectedEOF) - } - return -} - -func (s *ss) UnreadRune() os.Error { - if u, ok := s.rr.(runeUnreader); ok { - u.UnreadRune() - } else { - s.peekRune = s.prevRune - } - s.count-- - return nil -} - -func (s *ss) error(err os.Error) { - panic(scanError{err}) -} - -func (s *ss) errorString(err string) { - panic(scanError{os.NewError(err)}) -} - -func (s *ss) Token(skipSpace bool, f func(int) bool) (tok []byte, err os.Error) { - defer func() { - if e := recover(); e != nil { - if se, ok := e.(scanError); ok { - err = se.err - } else { - panic(e) - } - } - }() - if f == nil { - f = notSpace - } - s.buf.Reset() - tok = s.token(skipSpace, f) - return -} - -// notSpace is the default scanning function used in Token. -func notSpace(r int) bool { - return !unicode.IsSpace(r) -} - -// readRune is a structure to enable reading UTF-8 encoded code points -// from an io.Reader. It is used if the Reader given to the scanner does -// not already implement io.RuneReader. -type readRune struct { - reader io.Reader - buf [utf8.UTFMax]byte // used only inside ReadRune - pending int // number of bytes in pendBuf; only >0 for bad UTF-8 - pendBuf [utf8.UTFMax]byte // bytes left over -} - -// readByte returns the next byte from the input, which may be -// left over from a previous read if the UTF-8 was ill-formed. -func (r *readRune) readByte() (b byte, err os.Error) { - if r.pending > 0 { - b = r.pendBuf[0] - copy(r.pendBuf[0:], r.pendBuf[1:]) - r.pending-- - return - } - _, err = r.reader.Read(r.pendBuf[0:1]) - return r.pendBuf[0], err -} - -// unread saves the bytes for the next read. -func (r *readRune) unread(buf []byte) { - copy(r.pendBuf[r.pending:], buf) - r.pending += len(buf) -} - -// ReadRune returns the next UTF-8 encoded code point from the -// io.Reader inside r. -func (r *readRune) ReadRune() (rune int, size int, err os.Error) { - r.buf[0], err = r.readByte() - if err != nil { - return 0, 0, err - } - if r.buf[0] < utf8.RuneSelf { // fast check for common ASCII case - rune = int(r.buf[0]) - return - } - var n int - for n = 1; !utf8.FullRune(r.buf[0:n]); n++ { - r.buf[n], err = r.readByte() - if err != nil { - if err == os.EOF { - err = nil - break - } - return - } - } - rune, size = utf8.DecodeRune(r.buf[0:n]) - if size < n { // an error - r.unread(r.buf[size:n]) - } - return -} - -var ssFree = newCache(func() interface{} { return new(ss) }) - -// Allocate a new ss struct or grab a cached one. -func newScanState(r io.Reader, nlIsSpace, nlIsEnd bool) (s *ss, old ssave) { - // If the reader is a *ss, then we've got a recursive - // call to Scan, so re-use the scan state. - s, ok := r.(*ss) - if ok { - old = s.ssave - s.limit = s.fieldLimit - s.nlIsEnd = nlIsEnd || s.nlIsEnd - s.nlIsSpace = nlIsSpace - return - } - - s = ssFree.get().(*ss) - if rr, ok := r.(io.RuneReader); ok { - s.rr = rr - } else { - s.rr = &readRune{reader: r} - } - s.nlIsSpace = nlIsSpace - s.nlIsEnd = nlIsEnd - s.prevRune = -1 - s.peekRune = -1 - s.atEOF = false - s.limit = hugeWid - s.fieldLimit = hugeWid - s.maxWid = hugeWid - s.validSave = true - return -} - -// Save used ss structs in ssFree; avoid an allocation per invocation. -func (s *ss) free(old ssave) { - // If it was used recursively, just restore the old state. - if old.validSave { - s.ssave = old - return - } - // Don't hold on to ss structs with large buffers. - if cap(s.buf.Bytes()) > 1024 { - return - } - s.buf.Reset() - s.rr = nil - ssFree.put(s) -} - -// skipSpace skips spaces and maybe newlines. -func (s *ss) skipSpace(stopAtNewline bool) { - for { - rune := s.getRune() - if rune == eof { - return - } - if rune == '\n' { - if stopAtNewline { - break - } - if s.nlIsSpace { - continue - } - s.errorString("unexpected newline") - return - } - if !unicode.IsSpace(rune) { - s.UnreadRune() - break - } - } -} - -// token returns the next space-delimited string from the input. It -// skips white space. For Scanln, it stops at newlines. For Scan, -// newlines are treated as spaces. -func (s *ss) token(skipSpace bool, f func(int) bool) []byte { - if skipSpace { - s.skipSpace(false) - } - // read until white space or newline - for { - rune := s.getRune() - if rune == eof { - break - } - if !f(rune) { - s.UnreadRune() - break - } - s.buf.WriteRune(rune) - } - return s.buf.Bytes() -} - -// typeError indicates that the type of the operand did not match the format -func (s *ss) typeError(field interface{}, expected string) { - s.errorString("expected field of type pointer to " + expected + "; found " + reflect.Typeof(field).String()) -} - -var complexError = os.NewError("syntax error scanning complex number") -var boolError = os.NewError("syntax error scanning boolean") - -// consume reads the next rune in the input and reports whether it is in the ok string. -// If accept is true, it puts the character into the input token. -func (s *ss) consume(ok string, accept bool) bool { - rune := s.getRune() - if rune == eof { - return false - } - if strings.IndexRune(ok, rune) >= 0 { - if accept { - s.buf.WriteRune(rune) - } - return true - } - if rune != eof && accept { - s.UnreadRune() - } - return false -} - -// peek reports whether the next character is in the ok string, without consuming it. -func (s *ss) peek(ok string) bool { - rune := s.getRune() - if rune != eof { - s.UnreadRune() - } - return strings.IndexRune(ok, rune) >= 0 -} - -// accept checks the next rune in the input. If it's a byte (sic) in the string, it puts it in the -// buffer and returns true. Otherwise it return false. -func (s *ss) accept(ok string) bool { - return s.consume(ok, true) -} - -// okVerb verifies that the verb is present in the list, setting s.err appropriately if not. -func (s *ss) okVerb(verb int, okVerbs, typ string) bool { - for _, v := range okVerbs { - if v == verb { - return true - } - } - s.errorString("bad verb %" + string(verb) + " for " + typ) - return false -} - -// scanBool returns the value of the boolean represented by the next token. -func (s *ss) scanBool(verb int) bool { - if !s.okVerb(verb, "tv", "boolean") { - return false - } - // Syntax-checking a boolean is annoying. We're not fastidious about case. - switch s.mustReadRune() { - case '0': - return false - case '1': - return true - case 't', 'T': - if s.accept("rR") && (!s.accept("uU") || !s.accept("eE")) { - s.error(boolError) - } - return true - case 'f', 'F': - if s.accept("aL") && (!s.accept("lL") || !s.accept("sS") || !s.accept("eE")) { - s.error(boolError) - } - return false - } - return false -} - -// Numerical elements -const ( - binaryDigits = "01" - octalDigits = "01234567" - decimalDigits = "0123456789" - hexadecimalDigits = "0123456789aAbBcCdDeEfF" - sign = "+-" - period = "." - exponent = "eEp" -) - -// getBase returns the numeric base represented by the verb and its digit string. -func (s *ss) getBase(verb int) (base int, digits string) { - s.okVerb(verb, "bdoUxXv", "integer") // sets s.err - base = 10 - digits = decimalDigits - switch verb { - case 'b': - base = 2 - digits = binaryDigits - case 'o': - base = 8 - digits = octalDigits - case 'x', 'X', 'U': - base = 16 - digits = hexadecimalDigits - } - return -} - -// scanNumber returns the numerical string with specified digits starting here. -func (s *ss) scanNumber(digits string, haveDigits bool) string { - if !haveDigits && !s.accept(digits) { - s.errorString("expected integer") - } - for s.accept(digits) { - } - return s.buf.String() -} - -// scanRune returns the next rune value in the input. -func (s *ss) scanRune(bitSize int) int64 { - rune := int64(s.mustReadRune()) - n := uint(bitSize) - x := (rune << (64 - n)) >> (64 - n) - if x != rune { - s.errorString("overflow on character value " + string(rune)) - } - return rune -} - -// scanBasePrefix reports whether the integer begins with a 0 or 0x, -// and returns the base, digit string, and whether a zero was found. -// It is called only if the verb is %v. -func (s *ss) scanBasePrefix() (base int, digits string, found bool) { - if !s.peek("0") { - return 10, decimalDigits, false - } - s.accept("0") - found = true // We've put a digit into the token buffer. - // Special cases for '0' && '0x' - base, digits = 8, octalDigits - if s.peek("xX") { - s.consume("xX", false) - base, digits = 16, hexadecimalDigits - } - return -} - -// scanInt returns the value of the integer represented by the next -// token, checking for overflow. Any error is stored in s.err. -func (s *ss) scanInt(verb int, bitSize int) int64 { - if verb == 'c' { - return s.scanRune(bitSize) - } - s.skipSpace(false) - base, digits := s.getBase(verb) - haveDigits := false - if verb == 'U' { - if !s.consume("U", false) || !s.consume("+", false) { - s.errorString("bad unicode format ") - } - } else { - s.accept(sign) // If there's a sign, it will be left in the token buffer. - if verb == 'v' { - base, digits, haveDigits = s.scanBasePrefix() - } - } - tok := s.scanNumber(digits, haveDigits) - i, err := strconv.Btoi64(tok, base) - if err != nil { - s.error(err) - } - n := uint(bitSize) - x := (i << (64 - n)) >> (64 - n) - if x != i { - s.errorString("integer overflow on token " + tok) - } - return i -} - -// scanUint returns the value of the unsigned integer represented -// by the next token, checking for overflow. Any error is stored in s.err. -func (s *ss) scanUint(verb int, bitSize int) uint64 { - if verb == 'c' { - return uint64(s.scanRune(bitSize)) - } - s.skipSpace(false) - base, digits := s.getBase(verb) - haveDigits := false - if verb == 'U' { - if !s.consume("U", false) || !s.consume("+", false) { - s.errorString("bad unicode format ") - } - } else if verb == 'v' { - base, digits, haveDigits = s.scanBasePrefix() - } - tok := s.scanNumber(digits, haveDigits) - i, err := strconv.Btoui64(tok, base) - if err != nil { - s.error(err) - } - n := uint(bitSize) - x := (i << (64 - n)) >> (64 - n) - if x != i { - s.errorString("unsigned integer overflow on token " + tok) - } - return i -} - -// floatToken returns the floating-point number starting here, no longer than swid -// if the width is specified. It's not rigorous about syntax because it doesn't check that -// we have at least some digits, but Atof will do that. -func (s *ss) floatToken() string { - s.buf.Reset() - // NaN? - if s.accept("nN") && s.accept("aA") && s.accept("nN") { - return s.buf.String() - } - // leading sign? - s.accept(sign) - // Inf? - if s.accept("iI") && s.accept("nN") && s.accept("fF") { - return s.buf.String() - } - // digits? - for s.accept(decimalDigits) { - } - // decimal point? - if s.accept(period) { - // fraction? - for s.accept(decimalDigits) { - } - } - // exponent? - if s.accept(exponent) { - // leading sign? - s.accept(sign) - // digits? - for s.accept(decimalDigits) { - } - } - return s.buf.String() -} - -// complexTokens returns the real and imaginary parts of the complex number starting here. -// The number might be parenthesized and has the format (N+Ni) where N is a floating-point -// number and there are no spaces within. -func (s *ss) complexTokens() (real, imag string) { - // TODO: accept N and Ni independently? - parens := s.accept("(") - real = s.floatToken() - s.buf.Reset() - // Must now have a sign. - if !s.accept("+-") { - s.error(complexError) - } - // Sign is now in buffer - imagSign := s.buf.String() - imag = s.floatToken() - if !s.accept("i") { - s.error(complexError) - } - if parens && !s.accept(")") { - s.error(complexError) - } - return real, imagSign + imag -} - -// convertFloat converts the string to a float64value. -func (s *ss) convertFloat(str string, n int) float64 { - if p := strings.Index(str, "p"); p >= 0 { - // Atof doesn't handle power-of-2 exponents, - // but they're easy to evaluate. - f, err := strconv.AtofN(str[:p], n) - if err != nil { - // Put full string into error. - if e, ok := err.(*strconv.NumError); ok { - e.Num = str - } - s.error(err) - } - n, err := strconv.Atoi(str[p+1:]) - if err != nil { - // Put full string into error. - if e, ok := err.(*strconv.NumError); ok { - e.Num = str - } - s.error(err) - } - return math.Ldexp(f, n) - } - f, err := strconv.AtofN(str, n) - if err != nil { - s.error(err) - } - return f -} - -// convertComplex converts the next token to a complex128 value. -// The atof argument is a type-specific reader for the underlying type. -// If we're reading complex64, atof will parse float32s and convert them -// to float64's to avoid reproducing this code for each complex type. -func (s *ss) scanComplex(verb int, n int) complex128 { - if !s.okVerb(verb, floatVerbs, "complex") { - return 0 - } - s.skipSpace(false) - sreal, simag := s.complexTokens() - real := s.convertFloat(sreal, n/2) - imag := s.convertFloat(simag, n/2) - return complex(real, imag) -} - -// convertString returns the string represented by the next input characters. -// The format of the input is determined by the verb. -func (s *ss) convertString(verb int) (str string) { - if !s.okVerb(verb, "svqx", "string") { - return "" - } - s.skipSpace(false) - switch verb { - case 'q': - str = s.quotedString() - case 'x': - str = s.hexString() - default: - str = string(s.token(true, notSpace)) // %s and %v just return the next word - } - // Empty strings other than with %q are not OK. - if len(str) == 0 && verb != 'q' && s.maxWid > 0 { - s.errorString("Scan: no data for string") - } - return -} - -// quotedString returns the double- or back-quoted string represented by the next input characters. -func (s *ss) quotedString() string { - quote := s.mustReadRune() - switch quote { - case '`': - // Back-quoted: Anything goes until EOF or back quote. - for { - rune := s.mustReadRune() - if rune == quote { - break - } - s.buf.WriteRune(rune) - } - return s.buf.String() - case '"': - // Double-quoted: Include the quotes and let strconv.Unquote do the backslash escapes. - s.buf.WriteRune(quote) - for { - rune := s.mustReadRune() - s.buf.WriteRune(rune) - if rune == '\\' { - // In a legal backslash escape, no matter how long, only the character - // immediately after the escape can itself be a backslash or quote. - // Thus we only need to protect the first character after the backslash. - rune := s.mustReadRune() - s.buf.WriteRune(rune) - } else if rune == '"' { - break - } - } - result, err := strconv.Unquote(s.buf.String()) - if err != nil { - s.error(err) - } - return result - default: - s.errorString("expected quoted string") - } - return "" -} - -// hexDigit returns the value of the hexadecimal digit -func (s *ss) hexDigit(digit int) int { - switch digit { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - return digit - '0' - case 'a', 'b', 'c', 'd', 'e', 'f': - return 10 + digit - 'a' - case 'A', 'B', 'C', 'D', 'E', 'F': - return 10 + digit - 'A' - } - s.errorString("Scan: illegal hex digit") - return 0 -} - -// hexByte returns the next hex-encoded (two-character) byte from the input. -// There must be either two hexadecimal digits or a space character in the input. -func (s *ss) hexByte() (b byte, ok bool) { - rune1 := s.getRune() - if rune1 == eof { - return - } - if unicode.IsSpace(rune1) { - s.UnreadRune() - return - } - rune2 := s.mustReadRune() - return byte(s.hexDigit(rune1)<<4 | s.hexDigit(rune2)), true -} - -// hexString returns the space-delimited hexpair-encoded string. -func (s *ss) hexString() string { - for { - b, ok := s.hexByte() - if !ok { - break - } - s.buf.WriteByte(b) - } - if s.buf.Len() == 0 { - s.errorString("Scan: no hex data for %x string") - return "" - } - return s.buf.String() -} - -const floatVerbs = "beEfFgGv" - -const hugeWid = 1 << 30 - -// scanOne scans a single value, deriving the scanner from the type of the argument. -func (s *ss) scanOne(verb int, field interface{}) { - s.buf.Reset() - var err os.Error - // If the parameter has its own Scan method, use that. - if v, ok := field.(Scanner); ok { - err = v.Scan(s, verb) - if err != nil { - if err == os.EOF { - err = io.ErrUnexpectedEOF - } - s.error(err) - } - return - } - switch v := field.(type) { - case *bool: - *v = s.scanBool(verb) - case *complex64: - *v = complex64(s.scanComplex(verb, 64)) - case *complex128: - *v = s.scanComplex(verb, 128) - case *int: - *v = int(s.scanInt(verb, intBits)) - case *int8: - *v = int8(s.scanInt(verb, 8)) - case *int16: - *v = int16(s.scanInt(verb, 16)) - case *int32: - *v = int32(s.scanInt(verb, 32)) - case *int64: - *v = s.scanInt(verb, 64) - case *uint: - *v = uint(s.scanUint(verb, intBits)) - case *uint8: - *v = uint8(s.scanUint(verb, 8)) - case *uint16: - *v = uint16(s.scanUint(verb, 16)) - case *uint32: - *v = uint32(s.scanUint(verb, 32)) - case *uint64: - *v = s.scanUint(verb, 64) - case *uintptr: - *v = uintptr(s.scanUint(verb, uintptrBits)) - // Floats are tricky because you want to scan in the precision of the result, not - // scan in high precision and convert, in order to preserve the correct error condition. - case *float32: - if s.okVerb(verb, floatVerbs, "float32") { - s.skipSpace(false) - *v = float32(s.convertFloat(s.floatToken(), 32)) - } - case *float64: - if s.okVerb(verb, floatVerbs, "float64") { - s.skipSpace(false) - *v = s.convertFloat(s.floatToken(), 64) - } - case *string: - *v = s.convertString(verb) - case *[]byte: - // We scan to string and convert so we get a copy of the data. - // If we scanned to bytes, the slice would point at the buffer. - *v = []byte(s.convertString(verb)) - default: - val := reflect.NewValue(v) - ptr, ok := val.(*reflect.PtrValue) - if !ok { - s.errorString("Scan: type not a pointer: " + val.Type().String()) - return - } - switch v := ptr.Elem().(type) { - case *reflect.BoolValue: - v.Set(s.scanBool(verb)) - case *reflect.IntValue: - v.Set(s.scanInt(verb, v.Type().Bits())) - case *reflect.UintValue: - v.Set(s.scanUint(verb, v.Type().Bits())) - case *reflect.StringValue: - v.Set(s.convertString(verb)) - case *reflect.SliceValue: - // For now, can only handle (renamed) []byte. - typ := v.Type().(*reflect.SliceType) - if typ.Elem().Kind() != reflect.Uint8 { - goto CantHandle - } - str := s.convertString(verb) - v.Set(reflect.MakeSlice(typ, len(str), len(str))) - for i := 0; i < len(str); i++ { - v.Elem(i).(*reflect.UintValue).Set(uint64(str[i])) - } - case *reflect.FloatValue: - s.skipSpace(false) - v.Set(s.convertFloat(s.floatToken(), v.Type().Bits())) - case *reflect.ComplexValue: - v.Set(s.scanComplex(verb, v.Type().Bits())) - default: - CantHandle: - s.errorString("Scan: can't handle type: " + val.Type().String()) - } - } -} - -// errorHandler turns local panics into error returns. EOFs are benign. -func errorHandler(errp *os.Error) { - if e := recover(); e != nil { - if se, ok := e.(scanError); ok { // catch local error - if se.err != os.EOF { - *errp = se.err - } - } else { - panic(e) - } - } -} - -// doScan does the real work for scanning without a format string. -func (s *ss) doScan(a []interface{}) (numProcessed int, err os.Error) { - defer errorHandler(&err) - for _, field := range a { - s.scanOne('v', field) - numProcessed++ - } - // Check for newline if required. - if !s.nlIsSpace { - for { - rune := s.getRune() - if rune == '\n' || rune == eof { - break - } - if !unicode.IsSpace(rune) { - s.errorString("Scan: expected newline") - break - } - } - } - return -} - -// advance determines whether the next characters in the input match -// those of the format. It returns the number of bytes (sic) consumed -// in the format. Newlines included, all runs of space characters in -// either input or format behave as a single space. This routine also -// handles the %% case. If the return value is zero, either format -// starts with a % (with no following %) or the input is empty. -// If it is negative, the input did not match the string. -func (s *ss) advance(format string) (i int) { - for i < len(format) { - fmtc, w := utf8.DecodeRuneInString(format[i:]) - if fmtc == '%' { - // %% acts like a real percent - nextc, _ := utf8.DecodeRuneInString(format[i+w:]) // will not match % if string is empty - if nextc != '%' { - return - } - i += w // skip the first % - } - sawSpace := false - for unicode.IsSpace(fmtc) && i < len(format) { - sawSpace = true - i += w - fmtc, w = utf8.DecodeRuneInString(format[i:]) - } - if sawSpace { - // There was space in the format, so there should be space (EOF) - // in the input. - inputc := s.getRune() - if inputc == eof { - return - } - if !unicode.IsSpace(inputc) { - // Space in format but not in input: error - s.errorString("expected space in input to match format") - } - s.skipSpace(true) - continue - } - inputc := s.mustReadRune() - if fmtc != inputc { - s.UnreadRune() - return -1 - } - i += w - } - return -} - -// doScanf does the real work when scanning with a format string. -// At the moment, it handles only pointers to basic types. -func (s *ss) doScanf(format string, a []interface{}) (numProcessed int, err os.Error) { - defer errorHandler(&err) - end := len(format) - 1 - // We process one item per non-trivial format - for i := 0; i <= end; { - w := s.advance(format[i:]) - if w > 0 { - i += w - continue - } - // Either we failed to advance, we have a percent character, or we ran out of input. - if format[i] != '%' { - // Can't advance format. Why not? - if w < 0 { - s.errorString("input does not match format") - } - // Otherwise at EOF; "too many operands" error handled below - break - } - i++ // % is one byte - - // do we have 20 (width)? - var widPresent bool - s.maxWid, widPresent, i = parsenum(format, i, end) - if !widPresent { - s.maxWid = hugeWid - } - s.fieldLimit = s.limit - if f := s.count + s.maxWid; f < s.fieldLimit { - s.fieldLimit = f - } - - c, w := utf8.DecodeRuneInString(format[i:]) - i += w - - if numProcessed >= len(a) { // out of operands - s.errorString("too few operands for format %" + format[i-w:]) - break - } - field := a[numProcessed] - - s.scanOne(c, field) - numProcessed++ - s.fieldLimit = s.limit - } - if numProcessed < len(a) { - s.errorString("too many operands") - } - return -} diff --git a/src/cmd/fix/testdata/reflect.scan.go.out b/src/cmd/fix/testdata/reflect.scan.go.out deleted file mode 100644 index 956c13bde..000000000 --- a/src/cmd/fix/testdata/reflect.scan.go.out +++ /dev/null @@ -1,1082 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package fmt - -import ( - "bytes" - "io" - "math" - "os" - "reflect" - "strconv" - "strings" - "unicode" - "utf8" -) - -// runeUnreader is the interface to something that can unread runes. -// If the object provided to Scan does not satisfy this interface, -// a local buffer will be used to back up the input, but its contents -// will be lost when Scan returns. -type runeUnreader interface { - UnreadRune() os.Error -} - -// ScanState represents the scanner state passed to custom scanners. -// Scanners may do rune-at-a-time scanning or ask the ScanState -// to discover the next space-delimited token. -type ScanState interface { - // ReadRune reads the next rune (Unicode code point) from the input. - // If invoked during Scanln, Fscanln, or Sscanln, ReadRune() will - // return EOF after returning the first '\n' or when reading beyond - // the specified width. - ReadRune() (rune int, size int, err os.Error) - // UnreadRune causes the next call to ReadRune to return the same rune. - UnreadRune() os.Error - // Token skips space in the input if skipSpace is true, then returns the - // run of Unicode code points c satisfying f(c). If f is nil, - // !unicode.IsSpace(c) is used; that is, the token will hold non-space - // characters. Newlines are treated as space unless the scan operation - // is Scanln, Fscanln or Sscanln, in which case a newline is treated as - // EOF. The returned slice points to shared data that may be overwritten - // by the next call to Token, a call to a Scan function using the ScanState - // as input, or when the calling Scan method returns. - Token(skipSpace bool, f func(int) bool) (token []byte, err os.Error) - // Width returns the value of the width option and whether it has been set. - // The unit is Unicode code points. - Width() (wid int, ok bool) - // Because ReadRune is implemented by the interface, Read should never be - // called by the scanning routines and a valid implementation of - // ScanState may choose always to return an error from Read. - Read(buf []byte) (n int, err os.Error) -} - -// Scanner is implemented by any value that has a Scan method, which scans -// the input for the representation of a value and stores the result in the -// receiver, which must be a pointer to be useful. The Scan method is called -// for any argument to Scan, Scanf, or Scanln that implements it. -type Scanner interface { - Scan(state ScanState, verb int) os.Error -} - -// Scan scans text read from standard input, storing successive -// space-separated values into successive arguments. Newlines count -// as space. It returns the number of items successfully scanned. -// If that is less than the number of arguments, err will report why. -func Scan(a ...interface{}) (n int, err os.Error) { - return Fscan(os.Stdin, a...) -} - -// Scanln is similar to Scan, but stops scanning at a newline and -// after the final item there must be a newline or EOF. -func Scanln(a ...interface{}) (n int, err os.Error) { - return Fscanln(os.Stdin, a...) -} - -// Scanf scans text read from standard input, storing successive -// space-separated values into successive arguments as determined by -// the format. It returns the number of items successfully scanned. -func Scanf(format string, a ...interface{}) (n int, err os.Error) { - return Fscanf(os.Stdin, format, a...) -} - -// Sscan scans the argument string, storing successive space-separated -// values into successive arguments. Newlines count as space. It -// returns the number of items successfully scanned. If that is less -// than the number of arguments, err will report why. -func Sscan(str string, a ...interface{}) (n int, err os.Error) { - return Fscan(strings.NewReader(str), a...) -} - -// Sscanln is similar to Sscan, but stops scanning at a newline and -// after the final item there must be a newline or EOF. -func Sscanln(str string, a ...interface{}) (n int, err os.Error) { - return Fscanln(strings.NewReader(str), a...) -} - -// Sscanf scans the argument string, storing successive space-separated -// values into successive arguments as determined by the format. It -// returns the number of items successfully parsed. -func Sscanf(str string, format string, a ...interface{}) (n int, err os.Error) { - return Fscanf(strings.NewReader(str), format, a...) -} - -// Fscan scans text read from r, storing successive space-separated -// values into successive arguments. Newlines count as space. It -// returns the number of items successfully scanned. If that is less -// than the number of arguments, err will report why. -func Fscan(r io.Reader, a ...interface{}) (n int, err os.Error) { - s, old := newScanState(r, true, false) - n, err = s.doScan(a) - s.free(old) - return -} - -// Fscanln is similar to Fscan, but stops scanning at a newline and -// after the final item there must be a newline or EOF. -func Fscanln(r io.Reader, a ...interface{}) (n int, err os.Error) { - s, old := newScanState(r, false, true) - n, err = s.doScan(a) - s.free(old) - return -} - -// Fscanf scans text read from r, storing successive space-separated -// values into successive arguments as determined by the format. It -// returns the number of items successfully parsed. -func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err os.Error) { - s, old := newScanState(r, false, false) - n, err = s.doScanf(format, a) - s.free(old) - return -} - -// scanError represents an error generated by the scanning software. -// It's used as a unique signature to identify such errors when recovering. -type scanError struct { - err os.Error -} - -const eof = -1 - -// ss is the internal implementation of ScanState. -type ss struct { - rr io.RuneReader // where to read input - buf bytes.Buffer // token accumulator - peekRune int // one-rune lookahead - prevRune int // last rune returned by ReadRune - count int // runes consumed so far. - atEOF bool // already read EOF - ssave -} - -// ssave holds the parts of ss that need to be -// saved and restored on recursive scans. -type ssave struct { - validSave bool // is or was a part of an actual ss. - nlIsEnd bool // whether newline terminates scan - nlIsSpace bool // whether newline counts as white space - fieldLimit int // max value of ss.count for this field; fieldLimit <= limit - limit int // max value of ss.count. - maxWid int // width of this field. -} - -// The Read method is only in ScanState so that ScanState -// satisfies io.Reader. It will never be called when used as -// intended, so there is no need to make it actually work. -func (s *ss) Read(buf []byte) (n int, err os.Error) { - return 0, os.NewError("ScanState's Read should not be called. Use ReadRune") -} - -func (s *ss) ReadRune() (rune int, size int, err os.Error) { - if s.peekRune >= 0 { - s.count++ - rune = s.peekRune - size = utf8.RuneLen(rune) - s.prevRune = rune - s.peekRune = -1 - return - } - if s.atEOF || s.nlIsEnd && s.prevRune == '\n' || s.count >= s.fieldLimit { - err = os.EOF - return - } - - rune, size, err = s.rr.ReadRune() - if err == nil { - s.count++ - s.prevRune = rune - } else if err == os.EOF { - s.atEOF = true - } - return -} - -func (s *ss) Width() (wid int, ok bool) { - if s.maxWid == hugeWid { - return 0, false - } - return s.maxWid, true -} - -// The public method returns an error; this private one panics. -// If getRune reaches EOF, the return value is EOF (-1). -func (s *ss) getRune() (rune int) { - rune, _, err := s.ReadRune() - if err != nil { - if err == os.EOF { - return eof - } - s.error(err) - } - return -} - -// mustReadRune turns os.EOF into a panic(io.ErrUnexpectedEOF). -// It is called in cases such as string scanning where an EOF is a -// syntax error. -func (s *ss) mustReadRune() (rune int) { - rune = s.getRune() - if rune == eof { - s.error(io.ErrUnexpectedEOF) - } - return -} - -func (s *ss) UnreadRune() os.Error { - if u, ok := s.rr.(runeUnreader); ok { - u.UnreadRune() - } else { - s.peekRune = s.prevRune - } - s.count-- - return nil -} - -func (s *ss) error(err os.Error) { - panic(scanError{err}) -} - -func (s *ss) errorString(err string) { - panic(scanError{os.NewError(err)}) -} - -func (s *ss) Token(skipSpace bool, f func(int) bool) (tok []byte, err os.Error) { - defer func() { - if e := recover(); e != nil { - if se, ok := e.(scanError); ok { - err = se.err - } else { - panic(e) - } - } - }() - if f == nil { - f = notSpace - } - s.buf.Reset() - tok = s.token(skipSpace, f) - return -} - -// notSpace is the default scanning function used in Token. -func notSpace(r int) bool { - return !unicode.IsSpace(r) -} - -// readRune is a structure to enable reading UTF-8 encoded code points -// from an io.Reader. It is used if the Reader given to the scanner does -// not already implement io.RuneReader. -type readRune struct { - reader io.Reader - buf [utf8.UTFMax]byte // used only inside ReadRune - pending int // number of bytes in pendBuf; only >0 for bad UTF-8 - pendBuf [utf8.UTFMax]byte // bytes left over -} - -// readByte returns the next byte from the input, which may be -// left over from a previous read if the UTF-8 was ill-formed. -func (r *readRune) readByte() (b byte, err os.Error) { - if r.pending > 0 { - b = r.pendBuf[0] - copy(r.pendBuf[0:], r.pendBuf[1:]) - r.pending-- - return - } - _, err = r.reader.Read(r.pendBuf[0:1]) - return r.pendBuf[0], err -} - -// unread saves the bytes for the next read. -func (r *readRune) unread(buf []byte) { - copy(r.pendBuf[r.pending:], buf) - r.pending += len(buf) -} - -// ReadRune returns the next UTF-8 encoded code point from the -// io.Reader inside r. -func (r *readRune) ReadRune() (rune int, size int, err os.Error) { - r.buf[0], err = r.readByte() - if err != nil { - return 0, 0, err - } - if r.buf[0] < utf8.RuneSelf { // fast check for common ASCII case - rune = int(r.buf[0]) - return - } - var n int - for n = 1; !utf8.FullRune(r.buf[0:n]); n++ { - r.buf[n], err = r.readByte() - if err != nil { - if err == os.EOF { - err = nil - break - } - return - } - } - rune, size = utf8.DecodeRune(r.buf[0:n]) - if size < n { // an error - r.unread(r.buf[size:n]) - } - return -} - -var ssFree = newCache(func() interface{} { return new(ss) }) - -// Allocate a new ss struct or grab a cached one. -func newScanState(r io.Reader, nlIsSpace, nlIsEnd bool) (s *ss, old ssave) { - // If the reader is a *ss, then we've got a recursive - // call to Scan, so re-use the scan state. - s, ok := r.(*ss) - if ok { - old = s.ssave - s.limit = s.fieldLimit - s.nlIsEnd = nlIsEnd || s.nlIsEnd - s.nlIsSpace = nlIsSpace - return - } - - s = ssFree.get().(*ss) - if rr, ok := r.(io.RuneReader); ok { - s.rr = rr - } else { - s.rr = &readRune{reader: r} - } - s.nlIsSpace = nlIsSpace - s.nlIsEnd = nlIsEnd - s.prevRune = -1 - s.peekRune = -1 - s.atEOF = false - s.limit = hugeWid - s.fieldLimit = hugeWid - s.maxWid = hugeWid - s.validSave = true - return -} - -// Save used ss structs in ssFree; avoid an allocation per invocation. -func (s *ss) free(old ssave) { - // If it was used recursively, just restore the old state. - if old.validSave { - s.ssave = old - return - } - // Don't hold on to ss structs with large buffers. - if cap(s.buf.Bytes()) > 1024 { - return - } - s.buf.Reset() - s.rr = nil - ssFree.put(s) -} - -// skipSpace skips spaces and maybe newlines. -func (s *ss) skipSpace(stopAtNewline bool) { - for { - rune := s.getRune() - if rune == eof { - return - } - if rune == '\n' { - if stopAtNewline { - break - } - if s.nlIsSpace { - continue - } - s.errorString("unexpected newline") - return - } - if !unicode.IsSpace(rune) { - s.UnreadRune() - break - } - } -} - -// token returns the next space-delimited string from the input. It -// skips white space. For Scanln, it stops at newlines. For Scan, -// newlines are treated as spaces. -func (s *ss) token(skipSpace bool, f func(int) bool) []byte { - if skipSpace { - s.skipSpace(false) - } - // read until white space or newline - for { - rune := s.getRune() - if rune == eof { - break - } - if !f(rune) { - s.UnreadRune() - break - } - s.buf.WriteRune(rune) - } - return s.buf.Bytes() -} - -// typeError indicates that the type of the operand did not match the format -func (s *ss) typeError(field interface{}, expected string) { - s.errorString("expected field of type pointer to " + expected + "; found " + reflect.TypeOf(field).String()) -} - -var complexError = os.NewError("syntax error scanning complex number") -var boolError = os.NewError("syntax error scanning boolean") - -// consume reads the next rune in the input and reports whether it is in the ok string. -// If accept is true, it puts the character into the input token. -func (s *ss) consume(ok string, accept bool) bool { - rune := s.getRune() - if rune == eof { - return false - } - if strings.IndexRune(ok, rune) >= 0 { - if accept { - s.buf.WriteRune(rune) - } - return true - } - if rune != eof && accept { - s.UnreadRune() - } - return false -} - -// peek reports whether the next character is in the ok string, without consuming it. -func (s *ss) peek(ok string) bool { - rune := s.getRune() - if rune != eof { - s.UnreadRune() - } - return strings.IndexRune(ok, rune) >= 0 -} - -// accept checks the next rune in the input. If it's a byte (sic) in the string, it puts it in the -// buffer and returns true. Otherwise it return false. -func (s *ss) accept(ok string) bool { - return s.consume(ok, true) -} - -// okVerb verifies that the verb is present in the list, setting s.err appropriately if not. -func (s *ss) okVerb(verb int, okVerbs, typ string) bool { - for _, v := range okVerbs { - if v == verb { - return true - } - } - s.errorString("bad verb %" + string(verb) + " for " + typ) - return false -} - -// scanBool returns the value of the boolean represented by the next token. -func (s *ss) scanBool(verb int) bool { - if !s.okVerb(verb, "tv", "boolean") { - return false - } - // Syntax-checking a boolean is annoying. We're not fastidious about case. - switch s.mustReadRune() { - case '0': - return false - case '1': - return true - case 't', 'T': - if s.accept("rR") && (!s.accept("uU") || !s.accept("eE")) { - s.error(boolError) - } - return true - case 'f', 'F': - if s.accept("aL") && (!s.accept("lL") || !s.accept("sS") || !s.accept("eE")) { - s.error(boolError) - } - return false - } - return false -} - -// Numerical elements -const ( - binaryDigits = "01" - octalDigits = "01234567" - decimalDigits = "0123456789" - hexadecimalDigits = "0123456789aAbBcCdDeEfF" - sign = "+-" - period = "." - exponent = "eEp" -) - -// getBase returns the numeric base represented by the verb and its digit string. -func (s *ss) getBase(verb int) (base int, digits string) { - s.okVerb(verb, "bdoUxXv", "integer") // sets s.err - base = 10 - digits = decimalDigits - switch verb { - case 'b': - base = 2 - digits = binaryDigits - case 'o': - base = 8 - digits = octalDigits - case 'x', 'X', 'U': - base = 16 - digits = hexadecimalDigits - } - return -} - -// scanNumber returns the numerical string with specified digits starting here. -func (s *ss) scanNumber(digits string, haveDigits bool) string { - if !haveDigits && !s.accept(digits) { - s.errorString("expected integer") - } - for s.accept(digits) { - } - return s.buf.String() -} - -// scanRune returns the next rune value in the input. -func (s *ss) scanRune(bitSize int) int64 { - rune := int64(s.mustReadRune()) - n := uint(bitSize) - x := (rune << (64 - n)) >> (64 - n) - if x != rune { - s.errorString("overflow on character value " + string(rune)) - } - return rune -} - -// scanBasePrefix reports whether the integer begins with a 0 or 0x, -// and returns the base, digit string, and whether a zero was found. -// It is called only if the verb is %v. -func (s *ss) scanBasePrefix() (base int, digits string, found bool) { - if !s.peek("0") { - return 10, decimalDigits, false - } - s.accept("0") - found = true // We've put a digit into the token buffer. - // Special cases for '0' && '0x' - base, digits = 8, octalDigits - if s.peek("xX") { - s.consume("xX", false) - base, digits = 16, hexadecimalDigits - } - return -} - -// scanInt returns the value of the integer represented by the next -// token, checking for overflow. Any error is stored in s.err. -func (s *ss) scanInt(verb int, bitSize int) int64 { - if verb == 'c' { - return s.scanRune(bitSize) - } - s.skipSpace(false) - base, digits := s.getBase(verb) - haveDigits := false - if verb == 'U' { - if !s.consume("U", false) || !s.consume("+", false) { - s.errorString("bad unicode format ") - } - } else { - s.accept(sign) // If there's a sign, it will be left in the token buffer. - if verb == 'v' { - base, digits, haveDigits = s.scanBasePrefix() - } - } - tok := s.scanNumber(digits, haveDigits) - i, err := strconv.Btoi64(tok, base) - if err != nil { - s.error(err) - } - n := uint(bitSize) - x := (i << (64 - n)) >> (64 - n) - if x != i { - s.errorString("integer overflow on token " + tok) - } - return i -} - -// scanUint returns the value of the unsigned integer represented -// by the next token, checking for overflow. Any error is stored in s.err. -func (s *ss) scanUint(verb int, bitSize int) uint64 { - if verb == 'c' { - return uint64(s.scanRune(bitSize)) - } - s.skipSpace(false) - base, digits := s.getBase(verb) - haveDigits := false - if verb == 'U' { - if !s.consume("U", false) || !s.consume("+", false) { - s.errorString("bad unicode format ") - } - } else if verb == 'v' { - base, digits, haveDigits = s.scanBasePrefix() - } - tok := s.scanNumber(digits, haveDigits) - i, err := strconv.Btoui64(tok, base) - if err != nil { - s.error(err) - } - n := uint(bitSize) - x := (i << (64 - n)) >> (64 - n) - if x != i { - s.errorString("unsigned integer overflow on token " + tok) - } - return i -} - -// floatToken returns the floating-point number starting here, no longer than swid -// if the width is specified. It's not rigorous about syntax because it doesn't check that -// we have at least some digits, but Atof will do that. -func (s *ss) floatToken() string { - s.buf.Reset() - // NaN? - if s.accept("nN") && s.accept("aA") && s.accept("nN") { - return s.buf.String() - } - // leading sign? - s.accept(sign) - // Inf? - if s.accept("iI") && s.accept("nN") && s.accept("fF") { - return s.buf.String() - } - // digits? - for s.accept(decimalDigits) { - } - // decimal point? - if s.accept(period) { - // fraction? - for s.accept(decimalDigits) { - } - } - // exponent? - if s.accept(exponent) { - // leading sign? - s.accept(sign) - // digits? - for s.accept(decimalDigits) { - } - } - return s.buf.String() -} - -// complexTokens returns the real and imaginary parts of the complex number starting here. -// The number might be parenthesized and has the format (N+Ni) where N is a floating-point -// number and there are no spaces within. -func (s *ss) complexTokens() (real, imag string) { - // TODO: accept N and Ni independently? - parens := s.accept("(") - real = s.floatToken() - s.buf.Reset() - // Must now have a sign. - if !s.accept("+-") { - s.error(complexError) - } - // Sign is now in buffer - imagSign := s.buf.String() - imag = s.floatToken() - if !s.accept("i") { - s.error(complexError) - } - if parens && !s.accept(")") { - s.error(complexError) - } - return real, imagSign + imag -} - -// convertFloat converts the string to a float64value. -func (s *ss) convertFloat(str string, n int) float64 { - if p := strings.Index(str, "p"); p >= 0 { - // Atof doesn't handle power-of-2 exponents, - // but they're easy to evaluate. - f, err := strconv.AtofN(str[:p], n) - if err != nil { - // Put full string into error. - if e, ok := err.(*strconv.NumError); ok { - e.Num = str - } - s.error(err) - } - n, err := strconv.Atoi(str[p+1:]) - if err != nil { - // Put full string into error. - if e, ok := err.(*strconv.NumError); ok { - e.Num = str - } - s.error(err) - } - return math.Ldexp(f, n) - } - f, err := strconv.AtofN(str, n) - if err != nil { - s.error(err) - } - return f -} - -// convertComplex converts the next token to a complex128 value. -// The atof argument is a type-specific reader for the underlying type. -// If we're reading complex64, atof will parse float32s and convert them -// to float64's to avoid reproducing this code for each complex type. -func (s *ss) scanComplex(verb int, n int) complex128 { - if !s.okVerb(verb, floatVerbs, "complex") { - return 0 - } - s.skipSpace(false) - sreal, simag := s.complexTokens() - real := s.convertFloat(sreal, n/2) - imag := s.convertFloat(simag, n/2) - return complex(real, imag) -} - -// convertString returns the string represented by the next input characters. -// The format of the input is determined by the verb. -func (s *ss) convertString(verb int) (str string) { - if !s.okVerb(verb, "svqx", "string") { - return "" - } - s.skipSpace(false) - switch verb { - case 'q': - str = s.quotedString() - case 'x': - str = s.hexString() - default: - str = string(s.token(true, notSpace)) // %s and %v just return the next word - } - // Empty strings other than with %q are not OK. - if len(str) == 0 && verb != 'q' && s.maxWid > 0 { - s.errorString("Scan: no data for string") - } - return -} - -// quotedString returns the double- or back-quoted string represented by the next input characters. -func (s *ss) quotedString() string { - quote := s.mustReadRune() - switch quote { - case '`': - // Back-quoted: Anything goes until EOF or back quote. - for { - rune := s.mustReadRune() - if rune == quote { - break - } - s.buf.WriteRune(rune) - } - return s.buf.String() - case '"': - // Double-quoted: Include the quotes and let strconv.Unquote do the backslash escapes. - s.buf.WriteRune(quote) - for { - rune := s.mustReadRune() - s.buf.WriteRune(rune) - if rune == '\\' { - // In a legal backslash escape, no matter how long, only the character - // immediately after the escape can itself be a backslash or quote. - // Thus we only need to protect the first character after the backslash. - rune := s.mustReadRune() - s.buf.WriteRune(rune) - } else if rune == '"' { - break - } - } - result, err := strconv.Unquote(s.buf.String()) - if err != nil { - s.error(err) - } - return result - default: - s.errorString("expected quoted string") - } - return "" -} - -// hexDigit returns the value of the hexadecimal digit -func (s *ss) hexDigit(digit int) int { - switch digit { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - return digit - '0' - case 'a', 'b', 'c', 'd', 'e', 'f': - return 10 + digit - 'a' - case 'A', 'B', 'C', 'D', 'E', 'F': - return 10 + digit - 'A' - } - s.errorString("Scan: illegal hex digit") - return 0 -} - -// hexByte returns the next hex-encoded (two-character) byte from the input. -// There must be either two hexadecimal digits or a space character in the input. -func (s *ss) hexByte() (b byte, ok bool) { - rune1 := s.getRune() - if rune1 == eof { - return - } - if unicode.IsSpace(rune1) { - s.UnreadRune() - return - } - rune2 := s.mustReadRune() - return byte(s.hexDigit(rune1)<<4 | s.hexDigit(rune2)), true -} - -// hexString returns the space-delimited hexpair-encoded string. -func (s *ss) hexString() string { - for { - b, ok := s.hexByte() - if !ok { - break - } - s.buf.WriteByte(b) - } - if s.buf.Len() == 0 { - s.errorString("Scan: no hex data for %x string") - return "" - } - return s.buf.String() -} - -const floatVerbs = "beEfFgGv" - -const hugeWid = 1 << 30 - -// scanOne scans a single value, deriving the scanner from the type of the argument. -func (s *ss) scanOne(verb int, field interface{}) { - s.buf.Reset() - var err os.Error - // If the parameter has its own Scan method, use that. - if v, ok := field.(Scanner); ok { - err = v.Scan(s, verb) - if err != nil { - if err == os.EOF { - err = io.ErrUnexpectedEOF - } - s.error(err) - } - return - } - switch v := field.(type) { - case *bool: - *v = s.scanBool(verb) - case *complex64: - *v = complex64(s.scanComplex(verb, 64)) - case *complex128: - *v = s.scanComplex(verb, 128) - case *int: - *v = int(s.scanInt(verb, intBits)) - case *int8: - *v = int8(s.scanInt(verb, 8)) - case *int16: - *v = int16(s.scanInt(verb, 16)) - case *int32: - *v = int32(s.scanInt(verb, 32)) - case *int64: - *v = s.scanInt(verb, 64) - case *uint: - *v = uint(s.scanUint(verb, intBits)) - case *uint8: - *v = uint8(s.scanUint(verb, 8)) - case *uint16: - *v = uint16(s.scanUint(verb, 16)) - case *uint32: - *v = uint32(s.scanUint(verb, 32)) - case *uint64: - *v = s.scanUint(verb, 64) - case *uintptr: - *v = uintptr(s.scanUint(verb, uintptrBits)) - // Floats are tricky because you want to scan in the precision of the result, not - // scan in high precision and convert, in order to preserve the correct error condition. - case *float32: - if s.okVerb(verb, floatVerbs, "float32") { - s.skipSpace(false) - *v = float32(s.convertFloat(s.floatToken(), 32)) - } - case *float64: - if s.okVerb(verb, floatVerbs, "float64") { - s.skipSpace(false) - *v = s.convertFloat(s.floatToken(), 64) - } - case *string: - *v = s.convertString(verb) - case *[]byte: - // We scan to string and convert so we get a copy of the data. - // If we scanned to bytes, the slice would point at the buffer. - *v = []byte(s.convertString(verb)) - default: - val := reflect.ValueOf(v) - ptr := val - if ptr.Kind() != reflect.Ptr { - s.errorString("Scan: type not a pointer: " + val.Type().String()) - return - } - switch v := ptr.Elem(); v.Kind() { - case reflect.Bool: - v.SetBool(s.scanBool(verb)) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - v.SetInt(s.scanInt(verb, v.Type().Bits())) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - v.SetUint(s.scanUint(verb, v.Type().Bits())) - case reflect.String: - v.SetString(s.convertString(verb)) - case reflect.Slice: - // For now, can only handle (renamed) []byte. - typ := v.Type() - if typ.Elem().Kind() != reflect.Uint8 { - goto CantHandle - } - str := s.convertString(verb) - v.Set(reflect.MakeSlice(typ, len(str), len(str))) - for i := 0; i < len(str); i++ { - v.Index(i).SetUint(uint64(str[i])) - } - case reflect.Float32, reflect.Float64: - s.skipSpace(false) - v.SetFloat(s.convertFloat(s.floatToken(), v.Type().Bits())) - case reflect.Complex64, reflect.Complex128: - v.SetComplex(s.scanComplex(verb, v.Type().Bits())) - default: - CantHandle: - s.errorString("Scan: can't handle type: " + val.Type().String()) - } - } -} - -// errorHandler turns local panics into error returns. EOFs are benign. -func errorHandler(errp *os.Error) { - if e := recover(); e != nil { - if se, ok := e.(scanError); ok { // catch local error - if se.err != os.EOF { - *errp = se.err - } - } else { - panic(e) - } - } -} - -// doScan does the real work for scanning without a format string. -func (s *ss) doScan(a []interface{}) (numProcessed int, err os.Error) { - defer errorHandler(&err) - for _, field := range a { - s.scanOne('v', field) - numProcessed++ - } - // Check for newline if required. - if !s.nlIsSpace { - for { - rune := s.getRune() - if rune == '\n' || rune == eof { - break - } - if !unicode.IsSpace(rune) { - s.errorString("Scan: expected newline") - break - } - } - } - return -} - -// advance determines whether the next characters in the input match -// those of the format. It returns the number of bytes (sic) consumed -// in the format. Newlines included, all runs of space characters in -// either input or format behave as a single space. This routine also -// handles the %% case. If the return value is zero, either format -// starts with a % (with no following %) or the input is empty. -// If it is negative, the input did not match the string. -func (s *ss) advance(format string) (i int) { - for i < len(format) { - fmtc, w := utf8.DecodeRuneInString(format[i:]) - if fmtc == '%' { - // %% acts like a real percent - nextc, _ := utf8.DecodeRuneInString(format[i+w:]) // will not match % if string is empty - if nextc != '%' { - return - } - i += w // skip the first % - } - sawSpace := false - for unicode.IsSpace(fmtc) && i < len(format) { - sawSpace = true - i += w - fmtc, w = utf8.DecodeRuneInString(format[i:]) - } - if sawSpace { - // There was space in the format, so there should be space (EOF) - // in the input. - inputc := s.getRune() - if inputc == eof { - return - } - if !unicode.IsSpace(inputc) { - // Space in format but not in input: error - s.errorString("expected space in input to match format") - } - s.skipSpace(true) - continue - } - inputc := s.mustReadRune() - if fmtc != inputc { - s.UnreadRune() - return -1 - } - i += w - } - return -} - -// doScanf does the real work when scanning with a format string. -// At the moment, it handles only pointers to basic types. -func (s *ss) doScanf(format string, a []interface{}) (numProcessed int, err os.Error) { - defer errorHandler(&err) - end := len(format) - 1 - // We process one item per non-trivial format - for i := 0; i <= end; { - w := s.advance(format[i:]) - if w > 0 { - i += w - continue - } - // Either we failed to advance, we have a percent character, or we ran out of input. - if format[i] != '%' { - // Can't advance format. Why not? - if w < 0 { - s.errorString("input does not match format") - } - // Otherwise at EOF; "too many operands" error handled below - break - } - i++ // % is one byte - - // do we have 20 (width)? - var widPresent bool - s.maxWid, widPresent, i = parsenum(format, i, end) - if !widPresent { - s.maxWid = hugeWid - } - s.fieldLimit = s.limit - if f := s.count + s.maxWid; f < s.fieldLimit { - s.fieldLimit = f - } - - c, w := utf8.DecodeRuneInString(format[i:]) - i += w - - if numProcessed >= len(a) { // out of operands - s.errorString("too few operands for format %" + format[i-w:]) - break - } - field := a[numProcessed] - - s.scanOne(c, field) - numProcessed++ - s.fieldLimit = s.limit - } - if numProcessed < len(a) { - s.errorString("too many operands") - } - return -} diff --git a/src/cmd/fix/testdata/reflect.script.go.in b/src/cmd/fix/testdata/reflect.script.go.in deleted file mode 100644 index b341b1f89..000000000 --- a/src/cmd/fix/testdata/reflect.script.go.in +++ /dev/null @@ -1,359 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This package aids in the testing of code that uses channels. -package script - -import ( - "fmt" - "os" - "rand" - "reflect" - "strings" -) - -// An Event is an element in a partially ordered set that either sends a value -// to a channel or expects a value from a channel. -type Event struct { - name string - occurred bool - predecessors []*Event - action action -} - -type action interface { - // getSend returns nil if the action is not a send action. - getSend() sendAction - // getRecv returns nil if the action is not a receive action. - getRecv() recvAction - // getChannel returns the channel that the action operates on. - getChannel() interface{} -} - -type recvAction interface { - recvMatch(interface{}) bool -} - -type sendAction interface { - send() -} - -// isReady returns true if all the predecessors of an Event have occurred. -func (e Event) isReady() bool { - for _, predecessor := range e.predecessors { - if !predecessor.occurred { - return false - } - } - - return true -} - -// A Recv action reads a value from a channel and uses reflect.DeepMatch to -// compare it with an expected value. -type Recv struct { - Channel interface{} - Expected interface{} -} - -func (r Recv) getRecv() recvAction { return r } - -func (Recv) getSend() sendAction { return nil } - -func (r Recv) getChannel() interface{} { return r.Channel } - -func (r Recv) recvMatch(chanEvent interface{}) bool { - c, ok := chanEvent.(channelRecv) - if !ok || c.channel != r.Channel { - return false - } - - return reflect.DeepEqual(c.value, r.Expected) -} - -// A RecvMatch action reads a value from a channel and calls a function to -// determine if the value matches. -type RecvMatch struct { - Channel interface{} - Match func(interface{}) bool -} - -func (r RecvMatch) getRecv() recvAction { return r } - -func (RecvMatch) getSend() sendAction { return nil } - -func (r RecvMatch) getChannel() interface{} { return r.Channel } - -func (r RecvMatch) recvMatch(chanEvent interface{}) bool { - c, ok := chanEvent.(channelRecv) - if !ok || c.channel != r.Channel { - return false - } - - return r.Match(c.value) -} - -// A Closed action matches if the given channel is closed. The closing is -// treated as an event, not a state, thus Closed will only match once for a -// given channel. -type Closed struct { - Channel interface{} -} - -func (r Closed) getRecv() recvAction { return r } - -func (Closed) getSend() sendAction { return nil } - -func (r Closed) getChannel() interface{} { return r.Channel } - -func (r Closed) recvMatch(chanEvent interface{}) bool { - c, ok := chanEvent.(channelClosed) - if !ok || c.channel != r.Channel { - return false - } - - return true -} - -// A Send action sends a value to a channel. The value must match the -// type of the channel exactly unless the channel if of type chan interface{}. -type Send struct { - Channel interface{} - Value interface{} -} - -func (Send) getRecv() recvAction { return nil } - -func (s Send) getSend() sendAction { return s } - -func (s Send) getChannel() interface{} { return s.Channel } - -type empty struct { - x interface{} -} - -func newEmptyInterface(e empty) reflect.Value { - return reflect.NewValue(e).(*reflect.StructValue).Field(0) -} - -func (s Send) send() { - // With reflect.ChanValue.Send, we must match the types exactly. So, if - // s.Channel is a chan interface{} we convert s.Value to an interface{} - // first. - c := reflect.NewValue(s.Channel).(*reflect.ChanValue) - var v reflect.Value - if iface, ok := c.Type().(*reflect.ChanType).Elem().(*reflect.InterfaceType); ok && iface.NumMethod() == 0 { - v = newEmptyInterface(empty{s.Value}) - } else { - v = reflect.NewValue(s.Value) - } - c.Send(v) -} - -// A Close action closes the given channel. -type Close struct { - Channel interface{} -} - -func (Close) getRecv() recvAction { return nil } - -func (s Close) getSend() sendAction { return s } - -func (s Close) getChannel() interface{} { return s.Channel } - -func (s Close) send() { reflect.NewValue(s.Channel).(*reflect.ChanValue).Close() } - -// A ReceivedUnexpected error results if no active Events match a value -// received from a channel. -type ReceivedUnexpected struct { - Value interface{} - ready []*Event -} - -func (r ReceivedUnexpected) String() string { - names := make([]string, len(r.ready)) - for i, v := range r.ready { - names[i] = v.name - } - return fmt.Sprintf("received unexpected value on one of the channels: %#v. Runnable events: %s", r.Value, strings.Join(names, ", ")) -} - -// A SetupError results if there is a error with the configuration of a set of -// Events. -type SetupError string - -func (s SetupError) String() string { return string(s) } - -func NewEvent(name string, predecessors []*Event, action action) *Event { - e := &Event{name, false, predecessors, action} - return e -} - -// Given a set of Events, Perform repeatedly iterates over the set and finds the -// subset of ready Events (that is, all of their predecessors have -// occurred). From that subset, it pseudo-randomly selects an Event to perform. -// If the Event is a send event, the send occurs and Perform recalculates the ready -// set. If the event is a receive event, Perform waits for a value from any of the -// channels that are contained in any of the events. That value is then matched -// against the ready events. The first event that matches is considered to -// have occurred and Perform recalculates the ready set. -// -// Perform continues this until all Events have occurred. -// -// Note that uncollected goroutines may still be reading from any of the -// channels read from after Perform returns. -// -// For example, consider the problem of testing a function that reads values on -// one channel and echos them to two output channels. To test this we would -// create three events: a send event and two receive events. Each of the -// receive events must list the send event as a predecessor but there is no -// ordering between the receive events. -// -// send := NewEvent("send", nil, Send{c, 1}) -// recv1 := NewEvent("recv 1", []*Event{send}, Recv{c, 1}) -// recv2 := NewEvent("recv 2", []*Event{send}, Recv{c, 1}) -// Perform(0, []*Event{send, recv1, recv2}) -// -// At first, only the send event would be in the ready set and thus Perform will -// send a value to the input channel. Now the two receive events are ready and -// Perform will match each of them against the values read from the output channels. -// -// It would be invalid to list one of the receive events as a predecessor of -// the other. At each receive step, all the receive channels are considered, -// thus Perform may see a value from a channel that is not in the current ready -// set and fail. -func Perform(seed int64, events []*Event) (err os.Error) { - r := rand.New(rand.NewSource(seed)) - - channels, err := getChannels(events) - if err != nil { - return - } - multiplex := make(chan interface{}) - for _, channel := range channels { - go recvValues(multiplex, channel) - } - -Outer: - for { - ready, err := readyEvents(events) - if err != nil { - return err - } - - if len(ready) == 0 { - // All events occurred. - break - } - - event := ready[r.Intn(len(ready))] - if send := event.action.getSend(); send != nil { - send.send() - event.occurred = true - continue - } - - v := <-multiplex - for _, event := range ready { - if recv := event.action.getRecv(); recv != nil && recv.recvMatch(v) { - event.occurred = true - continue Outer - } - } - - return ReceivedUnexpected{v, ready} - } - - return nil -} - -// getChannels returns all the channels listed in any receive events. -func getChannels(events []*Event) ([]interface{}, os.Error) { - channels := make([]interface{}, len(events)) - - j := 0 - for _, event := range events { - if recv := event.action.getRecv(); recv == nil { - continue - } - c := event.action.getChannel() - if _, ok := reflect.NewValue(c).(*reflect.ChanValue); !ok { - return nil, SetupError("one of the channel values is not a channel") - } - - duplicate := false - for _, other := range channels[0:j] { - if c == other { - duplicate = true - break - } - } - - if !duplicate { - channels[j] = c - j++ - } - } - - return channels[0:j], nil -} - -// recvValues is a multiplexing helper function. It reads values from the given -// channel repeatedly, wrapping them up as either a channelRecv or -// channelClosed structure, and forwards them to the multiplex channel. -func recvValues(multiplex chan<- interface{}, channel interface{}) { - c := reflect.NewValue(channel).(*reflect.ChanValue) - - for { - v, ok := c.Recv() - if !ok { - multiplex <- channelClosed{channel} - return - } - - multiplex <- channelRecv{channel, v.Interface()} - } -} - -type channelClosed struct { - channel interface{} -} - -type channelRecv struct { - channel interface{} - value interface{} -} - -// readyEvents returns the subset of events that are ready. -func readyEvents(events []*Event) ([]*Event, os.Error) { - ready := make([]*Event, len(events)) - - j := 0 - eventsWaiting := false - for _, event := range events { - if event.occurred { - continue - } - - eventsWaiting = true - if event.isReady() { - ready[j] = event - j++ - } - } - - if j == 0 && eventsWaiting { - names := make([]string, len(events)) - for _, event := range events { - if event.occurred { - continue - } - names[j] = event.name - } - - return nil, SetupError("dependency cycle in events. These events are waiting to run but cannot: " + strings.Join(names, ", ")) - } - - return ready[0:j], nil -} diff --git a/src/cmd/fix/testdata/reflect.script.go.out b/src/cmd/fix/testdata/reflect.script.go.out deleted file mode 100644 index bc5a6a41d..000000000 --- a/src/cmd/fix/testdata/reflect.script.go.out +++ /dev/null @@ -1,359 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This package aids in the testing of code that uses channels. -package script - -import ( - "fmt" - "os" - "rand" - "reflect" - "strings" -) - -// An Event is an element in a partially ordered set that either sends a value -// to a channel or expects a value from a channel. -type Event struct { - name string - occurred bool - predecessors []*Event - action action -} - -type action interface { - // getSend returns nil if the action is not a send action. - getSend() sendAction - // getRecv returns nil if the action is not a receive action. - getRecv() recvAction - // getChannel returns the channel that the action operates on. - getChannel() interface{} -} - -type recvAction interface { - recvMatch(interface{}) bool -} - -type sendAction interface { - send() -} - -// isReady returns true if all the predecessors of an Event have occurred. -func (e Event) isReady() bool { - for _, predecessor := range e.predecessors { - if !predecessor.occurred { - return false - } - } - - return true -} - -// A Recv action reads a value from a channel and uses reflect.DeepMatch to -// compare it with an expected value. -type Recv struct { - Channel interface{} - Expected interface{} -} - -func (r Recv) getRecv() recvAction { return r } - -func (Recv) getSend() sendAction { return nil } - -func (r Recv) getChannel() interface{} { return r.Channel } - -func (r Recv) recvMatch(chanEvent interface{}) bool { - c, ok := chanEvent.(channelRecv) - if !ok || c.channel != r.Channel { - return false - } - - return reflect.DeepEqual(c.value, r.Expected) -} - -// A RecvMatch action reads a value from a channel and calls a function to -// determine if the value matches. -type RecvMatch struct { - Channel interface{} - Match func(interface{}) bool -} - -func (r RecvMatch) getRecv() recvAction { return r } - -func (RecvMatch) getSend() sendAction { return nil } - -func (r RecvMatch) getChannel() interface{} { return r.Channel } - -func (r RecvMatch) recvMatch(chanEvent interface{}) bool { - c, ok := chanEvent.(channelRecv) - if !ok || c.channel != r.Channel { - return false - } - - return r.Match(c.value) -} - -// A Closed action matches if the given channel is closed. The closing is -// treated as an event, not a state, thus Closed will only match once for a -// given channel. -type Closed struct { - Channel interface{} -} - -func (r Closed) getRecv() recvAction { return r } - -func (Closed) getSend() sendAction { return nil } - -func (r Closed) getChannel() interface{} { return r.Channel } - -func (r Closed) recvMatch(chanEvent interface{}) bool { - c, ok := chanEvent.(channelClosed) - if !ok || c.channel != r.Channel { - return false - } - - return true -} - -// A Send action sends a value to a channel. The value must match the -// type of the channel exactly unless the channel if of type chan interface{}. -type Send struct { - Channel interface{} - Value interface{} -} - -func (Send) getRecv() recvAction { return nil } - -func (s Send) getSend() sendAction { return s } - -func (s Send) getChannel() interface{} { return s.Channel } - -type empty struct { - x interface{} -} - -func newEmptyInterface(e empty) reflect.Value { - return reflect.ValueOf(e).Field(0) -} - -func (s Send) send() { - // With reflect.ChanValue.Send, we must match the types exactly. So, if - // s.Channel is a chan interface{} we convert s.Value to an interface{} - // first. - c := reflect.ValueOf(s.Channel) - var v reflect.Value - if iface := c.Type().Elem(); iface.Kind() == reflect.Interface && iface.NumMethod() == 0 { - v = newEmptyInterface(empty{s.Value}) - } else { - v = reflect.ValueOf(s.Value) - } - c.Send(v) -} - -// A Close action closes the given channel. -type Close struct { - Channel interface{} -} - -func (Close) getRecv() recvAction { return nil } - -func (s Close) getSend() sendAction { return s } - -func (s Close) getChannel() interface{} { return s.Channel } - -func (s Close) send() { reflect.ValueOf(s.Channel).Close() } - -// A ReceivedUnexpected error results if no active Events match a value -// received from a channel. -type ReceivedUnexpected struct { - Value interface{} - ready []*Event -} - -func (r ReceivedUnexpected) String() string { - names := make([]string, len(r.ready)) - for i, v := range r.ready { - names[i] = v.name - } - return fmt.Sprintf("received unexpected value on one of the channels: %#v. Runnable events: %s", r.Value, strings.Join(names, ", ")) -} - -// A SetupError results if there is a error with the configuration of a set of -// Events. -type SetupError string - -func (s SetupError) String() string { return string(s) } - -func NewEvent(name string, predecessors []*Event, action action) *Event { - e := &Event{name, false, predecessors, action} - return e -} - -// Given a set of Events, Perform repeatedly iterates over the set and finds the -// subset of ready Events (that is, all of their predecessors have -// occurred). From that subset, it pseudo-randomly selects an Event to perform. -// If the Event is a send event, the send occurs and Perform recalculates the ready -// set. If the event is a receive event, Perform waits for a value from any of the -// channels that are contained in any of the events. That value is then matched -// against the ready events. The first event that matches is considered to -// have occurred and Perform recalculates the ready set. -// -// Perform continues this until all Events have occurred. -// -// Note that uncollected goroutines may still be reading from any of the -// channels read from after Perform returns. -// -// For example, consider the problem of testing a function that reads values on -// one channel and echos them to two output channels. To test this we would -// create three events: a send event and two receive events. Each of the -// receive events must list the send event as a predecessor but there is no -// ordering between the receive events. -// -// send := NewEvent("send", nil, Send{c, 1}) -// recv1 := NewEvent("recv 1", []*Event{send}, Recv{c, 1}) -// recv2 := NewEvent("recv 2", []*Event{send}, Recv{c, 1}) -// Perform(0, []*Event{send, recv1, recv2}) -// -// At first, only the send event would be in the ready set and thus Perform will -// send a value to the input channel. Now the two receive events are ready and -// Perform will match each of them against the values read from the output channels. -// -// It would be invalid to list one of the receive events as a predecessor of -// the other. At each receive step, all the receive channels are considered, -// thus Perform may see a value from a channel that is not in the current ready -// set and fail. -func Perform(seed int64, events []*Event) (err os.Error) { - r := rand.New(rand.NewSource(seed)) - - channels, err := getChannels(events) - if err != nil { - return - } - multiplex := make(chan interface{}) - for _, channel := range channels { - go recvValues(multiplex, channel) - } - -Outer: - for { - ready, err := readyEvents(events) - if err != nil { - return err - } - - if len(ready) == 0 { - // All events occurred. - break - } - - event := ready[r.Intn(len(ready))] - if send := event.action.getSend(); send != nil { - send.send() - event.occurred = true - continue - } - - v := <-multiplex - for _, event := range ready { - if recv := event.action.getRecv(); recv != nil && recv.recvMatch(v) { - event.occurred = true - continue Outer - } - } - - return ReceivedUnexpected{v, ready} - } - - return nil -} - -// getChannels returns all the channels listed in any receive events. -func getChannels(events []*Event) ([]interface{}, os.Error) { - channels := make([]interface{}, len(events)) - - j := 0 - for _, event := range events { - if recv := event.action.getRecv(); recv == nil { - continue - } - c := event.action.getChannel() - if reflect.ValueOf(c).Kind() != reflect.Chan { - return nil, SetupError("one of the channel values is not a channel") - } - - duplicate := false - for _, other := range channels[0:j] { - if c == other { - duplicate = true - break - } - } - - if !duplicate { - channels[j] = c - j++ - } - } - - return channels[0:j], nil -} - -// recvValues is a multiplexing helper function. It reads values from the given -// channel repeatedly, wrapping them up as either a channelRecv or -// channelClosed structure, and forwards them to the multiplex channel. -func recvValues(multiplex chan<- interface{}, channel interface{}) { - c := reflect.ValueOf(channel) - - for { - v, ok := c.Recv() - if !ok { - multiplex <- channelClosed{channel} - return - } - - multiplex <- channelRecv{channel, v.Interface()} - } -} - -type channelClosed struct { - channel interface{} -} - -type channelRecv struct { - channel interface{} - value interface{} -} - -// readyEvents returns the subset of events that are ready. -func readyEvents(events []*Event) ([]*Event, os.Error) { - ready := make([]*Event, len(events)) - - j := 0 - eventsWaiting := false - for _, event := range events { - if event.occurred { - continue - } - - eventsWaiting = true - if event.isReady() { - ready[j] = event - j++ - } - } - - if j == 0 && eventsWaiting { - names := make([]string, len(events)) - for _, event := range events { - if event.occurred { - continue - } - names[j] = event.name - } - - return nil, SetupError("dependency cycle in events. These events are waiting to run but cannot: " + strings.Join(names, ", ")) - } - - return ready[0:j], nil -} diff --git a/src/cmd/fix/testdata/reflect.template.go.in b/src/cmd/fix/testdata/reflect.template.go.in deleted file mode 100644 index 1f5a8128f..000000000 --- a/src/cmd/fix/testdata/reflect.template.go.in +++ /dev/null @@ -1,1043 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* - Data-driven templates for generating textual output such as - HTML. - - Templates are executed by applying them to a data structure. - Annotations in the template refer to elements of the data - structure (typically a field of a struct or a key in a map) - to control execution and derive values to be displayed. - The template walks the structure as it executes and the - "cursor" @ represents the value at the current location - in the structure. - - Data items may be values or pointers; the interface hides the - indirection. - - In the following, 'field' is one of several things, according to the data. - - - The name of a field of a struct (result = data.field), - - The value stored in a map under that key (result = data[field]), or - - The result of invoking a niladic single-valued method with that name - (result = data.field()) - - Major constructs ({} are the default delimiters for template actions; - [] are the notation in this comment for optional elements): - - {# comment } - - A one-line comment. - - {.section field} XXX [ {.or} YYY ] {.end} - - Set @ to the value of the field. It may be an explicit @ - to stay at the same point in the data. If the field is nil - or empty, execute YYY; otherwise execute XXX. - - {.repeated section field} XXX [ {.alternates with} ZZZ ] [ {.or} YYY ] {.end} - - Like .section, but field must be an array or slice. XXX - is executed for each element. If the array is nil or empty, - YYY is executed instead. If the {.alternates with} marker - is present, ZZZ is executed between iterations of XXX. - - {field} - {field1 field2 ...} - {field|formatter} - {field1 field2...|formatter} - {field|formatter1|formatter2} - - Insert the value of the fields into the output. Each field is - first looked for in the cursor, as in .section and .repeated. - If it is not found, the search continues in outer sections - until the top level is reached. - - If the field value is a pointer, leading asterisks indicate - that the value to be inserted should be evaluated through the - pointer. For example, if x.p is of type *int, {x.p} will - insert the value of the pointer but {*x.p} will insert the - value of the underlying integer. If the value is nil or not a - pointer, asterisks have no effect. - - If a formatter is specified, it must be named in the formatter - map passed to the template set up routines or in the default - set ("html","str","") and is used to process the data for - output. The formatter function has signature - func(wr io.Writer, formatter string, data ...interface{}) - where wr is the destination for output, data holds the field - values at the instantiation, and formatter is its name at - the invocation site. The default formatter just concatenates - the string representations of the fields. - - Multiple formatters separated by the pipeline character | are - executed sequentially, with each formatter receiving the bytes - emitted by the one to its left. - - The delimiter strings get their default value, "{" and "}", from - JSON-template. They may be set to any non-empty, space-free - string using the SetDelims method. Their value can be printed - in the output using {.meta-left} and {.meta-right}. -*/ -package template - -import ( - "bytes" - "container/vector" - "fmt" - "io" - "io/ioutil" - "os" - "reflect" - "strings" - "unicode" - "utf8" -) - -// Errors returned during parsing and execution. Users may extract the information and reformat -// if they desire. -type Error struct { - Line int - Msg string -} - -func (e *Error) String() string { return fmt.Sprintf("line %d: %s", e.Line, e.Msg) } - -// Most of the literals are aces. -var lbrace = []byte{'{'} -var rbrace = []byte{'}'} -var space = []byte{' '} -var tab = []byte{'\t'} - -// The various types of "tokens", which are plain text or (usually) brace-delimited descriptors -const ( - tokAlternates = iota - tokComment - tokEnd - tokLiteral - tokOr - tokRepeated - tokSection - tokText - tokVariable -) - -// FormatterMap is the type describing the mapping from formatter -// names to the functions that implement them. -type FormatterMap map[string]func(io.Writer, string, ...interface{}) - -// Built-in formatters. -var builtins = FormatterMap{ - "html": HTMLFormatter, - "str": StringFormatter, - "": StringFormatter, -} - -// The parsed state of a template is a vector of xxxElement structs. -// Sections have line numbers so errors can be reported better during execution. - -// Plain text. -type textElement struct { - text []byte -} - -// A literal such as .meta-left or .meta-right -type literalElement struct { - text []byte -} - -// A variable invocation to be evaluated -type variableElement struct { - linenum int - word []string // The fields in the invocation. - fmts []string // Names of formatters to apply. len(fmts) > 0 -} - -// A .section block, possibly with a .or -type sectionElement struct { - linenum int // of .section itself - field string // cursor field for this block - start int // first element - or int // first element of .or block - end int // one beyond last element -} - -// A .repeated block, possibly with a .or and a .alternates -type repeatedElement struct { - sectionElement // It has the same structure... - altstart int // ... except for alternates - altend int -} - -// Template is the type that represents a template definition. -// It is unchanged after parsing. -type Template struct { - fmap FormatterMap // formatters for variables - // Used during parsing: - ldelim, rdelim []byte // delimiters; default {} - buf []byte // input text to process - p int // position in buf - linenum int // position in input - // Parsed results: - elems *vector.Vector -} - -// Internal state for executing a Template. As we evaluate the struct, -// the data item descends into the fields associated with sections, etc. -// Parent is used to walk upwards to find variables higher in the tree. -type state struct { - parent *state // parent in hierarchy - data reflect.Value // the driver data for this section etc. - wr io.Writer // where to send output - buf [2]bytes.Buffer // alternating buffers used when chaining formatters -} - -func (parent *state) clone(data reflect.Value) *state { - return &state{parent: parent, data: data, wr: parent.wr} -} - -// New creates a new template with the specified formatter map (which -// may be nil) to define auxiliary functions for formatting variables. -func New(fmap FormatterMap) *Template { - t := new(Template) - t.fmap = fmap - t.ldelim = lbrace - t.rdelim = rbrace - t.elems = new(vector.Vector) - return t -} - -// Report error and stop executing. The line number must be provided explicitly. -func (t *Template) execError(st *state, line int, err string, args ...interface{}) { - panic(&Error{line, fmt.Sprintf(err, args...)}) -} - -// Report error, panic to terminate parsing. -// The line number comes from the template state. -func (t *Template) parseError(err string, args ...interface{}) { - panic(&Error{t.linenum, fmt.Sprintf(err, args...)}) -} - -// Is this an exported - upper case - name? -func isExported(name string) bool { - rune, _ := utf8.DecodeRuneInString(name) - return unicode.IsUpper(rune) -} - -// -- Lexical analysis - -// Is c a white space character? -func white(c uint8) bool { return c == ' ' || c == '\t' || c == '\r' || c == '\n' } - -// Safely, does s[n:n+len(t)] == t? -func equal(s []byte, n int, t []byte) bool { - b := s[n:] - if len(t) > len(b) { // not enough space left for a match. - return false - } - for i, c := range t { - if c != b[i] { - return false - } - } - return true -} - -// nextItem returns the next item from the input buffer. If the returned -// item is empty, we are at EOF. The item will be either a -// delimited string or a non-empty string between delimited -// strings. Tokens stop at (but include, if plain text) a newline. -// Action tokens on a line by themselves drop any space on -// either side, up to and including the newline. -func (t *Template) nextItem() []byte { - startOfLine := t.p == 0 || t.buf[t.p-1] == '\n' - start := t.p - var i int - newline := func() { - t.linenum++ - i++ - } - // Leading white space up to but not including newline - for i = start; i < len(t.buf); i++ { - if t.buf[i] == '\n' || !white(t.buf[i]) { - break - } - } - leadingSpace := i > start - // What's left is nothing, newline, delimited string, or plain text - switch { - case i == len(t.buf): - // EOF; nothing to do - case t.buf[i] == '\n': - newline() - case equal(t.buf, i, t.ldelim): - left := i // Start of left delimiter. - right := -1 // Will be (immediately after) right delimiter. - haveText := false // Delimiters contain text. - i += len(t.ldelim) - // Find the end of the action. - for ; i < len(t.buf); i++ { - if t.buf[i] == '\n' { - break - } - if equal(t.buf, i, t.rdelim) { - i += len(t.rdelim) - right = i - break - } - haveText = true - } - if right < 0 { - t.parseError("unmatched opening delimiter") - return nil - } - // Is this a special action (starts with '.' or '#') and the only thing on the line? - if startOfLine && haveText { - firstChar := t.buf[left+len(t.ldelim)] - if firstChar == '.' || firstChar == '#' { - // It's special and the first thing on the line. Is it the last? - for j := right; j < len(t.buf) && white(t.buf[j]); j++ { - if t.buf[j] == '\n' { - // Yes it is. Drop the surrounding space and return the {.foo} - t.linenum++ - t.p = j + 1 - return t.buf[left:right] - } - } - } - } - // No it's not. If there's leading space, return that. - if leadingSpace { - // not trimming space: return leading white space if there is some. - t.p = left - return t.buf[start:left] - } - // Return the word, leave the trailing space. - start = left - break - default: - for ; i < len(t.buf); i++ { - if t.buf[i] == '\n' { - newline() - break - } - if equal(t.buf, i, t.ldelim) { - break - } - } - } - item := t.buf[start:i] - t.p = i - return item -} - -// Turn a byte array into a white-space-split array of strings. -func words(buf []byte) []string { - s := make([]string, 0, 5) - p := 0 // position in buf - // one word per loop - for i := 0; ; i++ { - // skip white space - for ; p < len(buf) && white(buf[p]); p++ { - } - // grab word - start := p - for ; p < len(buf) && !white(buf[p]); p++ { - } - if start == p { // no text left - break - } - s = append(s, string(buf[start:p])) - } - return s -} - -// Analyze an item and return its token type and, if it's an action item, an array of -// its constituent words. -func (t *Template) analyze(item []byte) (tok int, w []string) { - // item is known to be non-empty - if !equal(item, 0, t.ldelim) { // doesn't start with left delimiter - tok = tokText - return - } - if !equal(item, len(item)-len(t.rdelim), t.rdelim) { // doesn't end with right delimiter - t.parseError("internal error: unmatched opening delimiter") // lexing should prevent this - return - } - if len(item) <= len(t.ldelim)+len(t.rdelim) { // no contents - t.parseError("empty directive") - return - } - // Comment - if item[len(t.ldelim)] == '#' { - tok = tokComment - return - } - // Split into words - w = words(item[len(t.ldelim) : len(item)-len(t.rdelim)]) // drop final delimiter - if len(w) == 0 { - t.parseError("empty directive") - return - } - if len(w) > 0 && w[0][0] != '.' { - tok = tokVariable - return - } - switch w[0] { - case ".meta-left", ".meta-right", ".space", ".tab": - tok = tokLiteral - return - case ".or": - tok = tokOr - return - case ".end": - tok = tokEnd - return - case ".section": - if len(w) != 2 { - t.parseError("incorrect fields for .section: %s", item) - return - } - tok = tokSection - return - case ".repeated": - if len(w) != 3 || w[1] != "section" { - t.parseError("incorrect fields for .repeated: %s", item) - return - } - tok = tokRepeated - return - case ".alternates": - if len(w) != 2 || w[1] != "with" { - t.parseError("incorrect fields for .alternates: %s", item) - return - } - tok = tokAlternates - return - } - t.parseError("bad directive: %s", item) - return -} - -// formatter returns the Formatter with the given name in the Template, or nil if none exists. -func (t *Template) formatter(name string) func(io.Writer, string, ...interface{}) { - if t.fmap != nil { - if fn := t.fmap[name]; fn != nil { - return fn - } - } - return builtins[name] -} - -// -- Parsing - -// Allocate a new variable-evaluation element. -func (t *Template) newVariable(words []string) *variableElement { - // After the final space-separated argument, formatters may be specified separated - // by pipe symbols, for example: {a b c|d|e} - - // Until we learn otherwise, formatters contains a single name: "", the default formatter. - formatters := []string{""} - lastWord := words[len(words)-1] - bar := strings.IndexRune(lastWord, '|') - if bar >= 0 { - words[len(words)-1] = lastWord[0:bar] - formatters = strings.Split(lastWord[bar+1:], "|") - } - - // We could remember the function address here and avoid the lookup later, - // but it's more dynamic to let the user change the map contents underfoot. - // We do require the name to be present, though. - - // Is it in user-supplied map? - for _, f := range formatters { - if t.formatter(f) == nil { - t.parseError("unknown formatter: %q", f) - } - } - return &variableElement{t.linenum, words, formatters} -} - -// Grab the next item. If it's simple, just append it to the template. -// Otherwise return its details. -func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) { - tok, w = t.analyze(item) - done = true // assume for simplicity - switch tok { - case tokComment: - return - case tokText: - t.elems.Push(&textElement{item}) - return - case tokLiteral: - switch w[0] { - case ".meta-left": - t.elems.Push(&literalElement{t.ldelim}) - case ".meta-right": - t.elems.Push(&literalElement{t.rdelim}) - case ".space": - t.elems.Push(&literalElement{space}) - case ".tab": - t.elems.Push(&literalElement{tab}) - default: - t.parseError("internal error: unknown literal: %s", w[0]) - } - return - case tokVariable: - t.elems.Push(t.newVariable(w)) - return - } - return false, tok, w -} - -// parseRepeated and parseSection are mutually recursive - -func (t *Template) parseRepeated(words []string) *repeatedElement { - r := new(repeatedElement) - t.elems.Push(r) - r.linenum = t.linenum - r.field = words[2] - // Scan section, collecting true and false (.or) blocks. - r.start = t.elems.Len() - r.or = -1 - r.altstart = -1 - r.altend = -1 -Loop: - for { - item := t.nextItem() - if len(item) == 0 { - t.parseError("missing .end for .repeated section") - break - } - done, tok, w := t.parseSimple(item) - if done { - continue - } - switch tok { - case tokEnd: - break Loop - case tokOr: - if r.or >= 0 { - t.parseError("extra .or in .repeated section") - break Loop - } - r.altend = t.elems.Len() - r.or = t.elems.Len() - case tokSection: - t.parseSection(w) - case tokRepeated: - t.parseRepeated(w) - case tokAlternates: - if r.altstart >= 0 { - t.parseError("extra .alternates in .repeated section") - break Loop - } - if r.or >= 0 { - t.parseError(".alternates inside .or block in .repeated section") - break Loop - } - r.altstart = t.elems.Len() - default: - t.parseError("internal error: unknown repeated section item: %s", item) - break Loop - } - } - if r.altend < 0 { - r.altend = t.elems.Len() - } - r.end = t.elems.Len() - return r -} - -func (t *Template) parseSection(words []string) *sectionElement { - s := new(sectionElement) - t.elems.Push(s) - s.linenum = t.linenum - s.field = words[1] - // Scan section, collecting true and false (.or) blocks. - s.start = t.elems.Len() - s.or = -1 -Loop: - for { - item := t.nextItem() - if len(item) == 0 { - t.parseError("missing .end for .section") - break - } - done, tok, w := t.parseSimple(item) - if done { - continue - } - switch tok { - case tokEnd: - break Loop - case tokOr: - if s.or >= 0 { - t.parseError("extra .or in .section") - break Loop - } - s.or = t.elems.Len() - case tokSection: - t.parseSection(w) - case tokRepeated: - t.parseRepeated(w) - case tokAlternates: - t.parseError(".alternates not in .repeated") - default: - t.parseError("internal error: unknown section item: %s", item) - } - } - s.end = t.elems.Len() - return s -} - -func (t *Template) parse() { - for { - item := t.nextItem() - if len(item) == 0 { - break - } - done, tok, w := t.parseSimple(item) - if done { - continue - } - switch tok { - case tokOr, tokEnd, tokAlternates: - t.parseError("unexpected %s", w[0]) - case tokSection: - t.parseSection(w) - case tokRepeated: - t.parseRepeated(w) - default: - t.parseError("internal error: bad directive in parse: %s", item) - } - } -} - -// -- Execution - -// Evaluate interfaces and pointers looking for a value that can look up the name, via a -// struct field, method, or map key, and return the result of the lookup. -func (t *Template) lookup(st *state, v reflect.Value, name string) reflect.Value { - for v != nil { - typ := v.Type() - if n := v.Type().NumMethod(); n > 0 { - for i := 0; i < n; i++ { - m := typ.Method(i) - mtyp := m.Type - if m.Name == name && mtyp.NumIn() == 1 && mtyp.NumOut() == 1 { - if !isExported(name) { - t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type()) - } - return v.Method(i).Call(nil)[0] - } - } - } - switch av := v.(type) { - case *reflect.PtrValue: - v = av.Elem() - case *reflect.InterfaceValue: - v = av.Elem() - case *reflect.StructValue: - if !isExported(name) { - t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type()) - } - return av.FieldByName(name) - case *reflect.MapValue: - if v := av.Elem(reflect.NewValue(name)); v != nil { - return v - } - return reflect.MakeZero(typ.(*reflect.MapType).Elem()) - default: - return nil - } - } - return v -} - -// indirectPtr returns the item numLevels levels of indirection below the value. -// It is forgiving: if the value is not a pointer, it returns it rather than giving -// an error. If the pointer is nil, it is returned as is. -func indirectPtr(v reflect.Value, numLevels int) reflect.Value { - for i := numLevels; v != nil && i > 0; i++ { - if p, ok := v.(*reflect.PtrValue); ok { - if p.IsNil() { - return v - } - v = p.Elem() - } else { - break - } - } - return v -} - -// Walk v through pointers and interfaces, extracting the elements within. -func indirect(v reflect.Value) reflect.Value { -loop: - for v != nil { - switch av := v.(type) { - case *reflect.PtrValue: - v = av.Elem() - case *reflect.InterfaceValue: - v = av.Elem() - default: - break loop - } - } - return v -} - -// If the data for this template is a struct, find the named variable. -// Names of the form a.b.c are walked down the data tree. -// The special name "@" (the "cursor") denotes the current data. -// The value coming in (st.data) might need indirecting to reach -// a struct while the return value is not indirected - that is, -// it represents the actual named field. Leading stars indicate -// levels of indirection to be applied to the value. -func (t *Template) findVar(st *state, s string) reflect.Value { - data := st.data - flattenedName := strings.TrimLeft(s, "*") - numStars := len(s) - len(flattenedName) - s = flattenedName - if s == "@" { - return indirectPtr(data, numStars) - } - for _, elem := range strings.Split(s, ".") { - // Look up field; data must be a struct or map. - data = t.lookup(st, data, elem) - if data == nil { - return nil - } - } - return indirectPtr(data, numStars) -} - -// Is there no data to look at? -func empty(v reflect.Value) bool { - v = indirect(v) - if v == nil { - return true - } - switch v := v.(type) { - case *reflect.BoolValue: - return v.Get() == false - case *reflect.StringValue: - return v.Get() == "" - case *reflect.StructValue: - return false - case *reflect.MapValue: - return false - case *reflect.ArrayValue: - return v.Len() == 0 - case *reflect.SliceValue: - return v.Len() == 0 - } - return false -} - -// Look up a variable or method, up through the parent if necessary. -func (t *Template) varValue(name string, st *state) reflect.Value { - field := t.findVar(st, name) - if field == nil { - if st.parent == nil { - t.execError(st, t.linenum, "name not found: %s in type %s", name, st.data.Type()) - } - return t.varValue(name, st.parent) - } - return field -} - -func (t *Template) format(wr io.Writer, fmt string, val []interface{}, v *variableElement, st *state) { - fn := t.formatter(fmt) - if fn == nil { - t.execError(st, v.linenum, "missing formatter %s for variable %s", fmt, v.word[0]) - } - fn(wr, fmt, val...) -} - -// Evaluate a variable, looking up through the parent if necessary. -// If it has a formatter attached ({var|formatter}) run that too. -func (t *Template) writeVariable(v *variableElement, st *state) { - // Turn the words of the invocation into values. - val := make([]interface{}, len(v.word)) - for i, word := range v.word { - val[i] = t.varValue(word, st).Interface() - } - - for i, fmt := range v.fmts[:len(v.fmts)-1] { - b := &st.buf[i&1] - b.Reset() - t.format(b, fmt, val, v, st) - val = val[0:1] - val[0] = b.Bytes() - } - t.format(st.wr, v.fmts[len(v.fmts)-1], val, v, st) -} - -// Execute element i. Return next index to execute. -func (t *Template) executeElement(i int, st *state) int { - switch elem := t.elems.At(i).(type) { - case *textElement: - st.wr.Write(elem.text) - return i + 1 - case *literalElement: - st.wr.Write(elem.text) - return i + 1 - case *variableElement: - t.writeVariable(elem, st) - return i + 1 - case *sectionElement: - t.executeSection(elem, st) - return elem.end - case *repeatedElement: - t.executeRepeated(elem, st) - return elem.end - } - e := t.elems.At(i) - t.execError(st, 0, "internal error: bad directive in execute: %v %T\n", reflect.NewValue(e).Interface(), e) - return 0 -} - -// Execute the template. -func (t *Template) execute(start, end int, st *state) { - for i := start; i < end; { - i = t.executeElement(i, st) - } -} - -// Execute a .section -func (t *Template) executeSection(s *sectionElement, st *state) { - // Find driver data for this section. It must be in the current struct. - field := t.varValue(s.field, st) - if field == nil { - t.execError(st, s.linenum, ".section: cannot find field %s in %s", s.field, st.data.Type()) - } - st = st.clone(field) - start, end := s.start, s.or - if !empty(field) { - // Execute the normal block. - if end < 0 { - end = s.end - } - } else { - // Execute the .or block. If it's missing, do nothing. - start, end = s.or, s.end - if start < 0 { - return - } - } - for i := start; i < end; { - i = t.executeElement(i, st) - } -} - -// Return the result of calling the Iter method on v, or nil. -func iter(v reflect.Value) *reflect.ChanValue { - for j := 0; j < v.Type().NumMethod(); j++ { - mth := v.Type().Method(j) - fv := v.Method(j) - ft := fv.Type().(*reflect.FuncType) - // TODO(rsc): NumIn() should return 0 here, because ft is from a curried FuncValue. - if mth.Name != "Iter" || ft.NumIn() != 1 || ft.NumOut() != 1 { - continue - } - ct, ok := ft.Out(0).(*reflect.ChanType) - if !ok || ct.Dir()&reflect.RecvDir == 0 { - continue - } - return fv.Call(nil)[0].(*reflect.ChanValue) - } - return nil -} - -// Execute a .repeated section -func (t *Template) executeRepeated(r *repeatedElement, st *state) { - // Find driver data for this section. It must be in the current struct. - field := t.varValue(r.field, st) - if field == nil { - t.execError(st, r.linenum, ".repeated: cannot find field %s in %s", r.field, st.data.Type()) - } - field = indirect(field) - - start, end := r.start, r.or - if end < 0 { - end = r.end - } - if r.altstart >= 0 { - end = r.altstart - } - first := true - - // Code common to all the loops. - loopBody := func(newst *state) { - // .alternates between elements - if !first && r.altstart >= 0 { - for i := r.altstart; i < r.altend; { - i = t.executeElement(i, newst) - } - } - first = false - for i := start; i < end; { - i = t.executeElement(i, newst) - } - } - - if array, ok := field.(reflect.ArrayOrSliceValue); ok { - for j := 0; j < array.Len(); j++ { - loopBody(st.clone(array.Elem(j))) - } - } else if m, ok := field.(*reflect.MapValue); ok { - for _, key := range m.Keys() { - loopBody(st.clone(m.Elem(key))) - } - } else if ch := iter(field); ch != nil { - for { - e, ok := ch.Recv() - if !ok { - break - } - loopBody(st.clone(e)) - } - } else { - t.execError(st, r.linenum, ".repeated: cannot repeat %s (type %s)", - r.field, field.Type()) - } - - if first { - // Empty. Execute the .or block, once. If it's missing, do nothing. - start, end := r.or, r.end - if start >= 0 { - newst := st.clone(field) - for i := start; i < end; { - i = t.executeElement(i, newst) - } - } - return - } -} - -// A valid delimiter must contain no white space and be non-empty. -func validDelim(d []byte) bool { - if len(d) == 0 { - return false - } - for _, c := range d { - if white(c) { - return false - } - } - return true -} - -// checkError is a deferred function to turn a panic with type *Error into a plain error return. -// Other panics are unexpected and so are re-enabled. -func checkError(error *os.Error) { - if v := recover(); v != nil { - if e, ok := v.(*Error); ok { - *error = e - } else { - // runtime errors should crash - panic(v) - } - } -} - -// -- Public interface - -// Parse initializes a Template by parsing its definition. The string -// s contains the template text. If any errors occur, Parse returns -// the error. -func (t *Template) Parse(s string) (err os.Error) { - if t.elems == nil { - return &Error{1, "template not allocated with New"} - } - if !validDelim(t.ldelim) || !validDelim(t.rdelim) { - return &Error{1, fmt.Sprintf("bad delimiter strings %q %q", t.ldelim, t.rdelim)} - } - defer checkError(&err) - t.buf = []byte(s) - t.p = 0 - t.linenum = 1 - t.parse() - return nil -} - -// ParseFile is like Parse but reads the template definition from the -// named file. -func (t *Template) ParseFile(filename string) (err os.Error) { - b, err := ioutil.ReadFile(filename) - if err != nil { - return err - } - return t.Parse(string(b)) -} - -// Execute applies a parsed template to the specified data object, -// generating output to wr. -func (t *Template) Execute(wr io.Writer, data interface{}) (err os.Error) { - // Extract the driver data. - val := reflect.NewValue(data) - defer checkError(&err) - t.p = 0 - t.execute(0, t.elems.Len(), &state{parent: nil, data: val, wr: wr}) - return nil -} - -// SetDelims sets the left and right delimiters for operations in the -// template. They are validated during parsing. They could be -// validated here but it's better to keep the routine simple. The -// delimiters are very rarely invalid and Parse has the necessary -// error-handling interface already. -func (t *Template) SetDelims(left, right string) { - t.ldelim = []byte(left) - t.rdelim = []byte(right) -} - -// Parse creates a Template with default parameters (such as {} for -// metacharacters). The string s contains the template text while -// the formatter map fmap, which may be nil, defines auxiliary functions -// for formatting variables. The template is returned. If any errors -// occur, err will be non-nil. -func Parse(s string, fmap FormatterMap) (t *Template, err os.Error) { - t = New(fmap) - err = t.Parse(s) - if err != nil { - t = nil - } - return -} - -// ParseFile is a wrapper function that creates a Template with default -// parameters (such as {} for metacharacters). The filename identifies -// a file containing the template text, while the formatter map fmap, which -// may be nil, defines auxiliary functions for formatting variables. -// The template is returned. If any errors occur, err will be non-nil. -func ParseFile(filename string, fmap FormatterMap) (t *Template, err os.Error) { - b, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err - } - return Parse(string(b), fmap) -} - -// MustParse is like Parse but panics if the template cannot be parsed. -func MustParse(s string, fmap FormatterMap) *Template { - t, err := Parse(s, fmap) - if err != nil { - panic("template.MustParse error: " + err.String()) - } - return t -} - -// MustParseFile is like ParseFile but panics if the file cannot be read -// or the template cannot be parsed. -func MustParseFile(filename string, fmap FormatterMap) *Template { - b, err := ioutil.ReadFile(filename) - if err != nil { - panic("template.MustParseFile error: " + err.String()) - } - return MustParse(string(b), fmap) -} diff --git a/src/cmd/fix/testdata/reflect.template.go.out b/src/cmd/fix/testdata/reflect.template.go.out deleted file mode 100644 index f2f56ef3c..000000000 --- a/src/cmd/fix/testdata/reflect.template.go.out +++ /dev/null @@ -1,1044 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* - Data-driven templates for generating textual output such as - HTML. - - Templates are executed by applying them to a data structure. - Annotations in the template refer to elements of the data - structure (typically a field of a struct or a key in a map) - to control execution and derive values to be displayed. - The template walks the structure as it executes and the - "cursor" @ represents the value at the current location - in the structure. - - Data items may be values or pointers; the interface hides the - indirection. - - In the following, 'field' is one of several things, according to the data. - - - The name of a field of a struct (result = data.field), - - The value stored in a map under that key (result = data[field]), or - - The result of invoking a niladic single-valued method with that name - (result = data.field()) - - Major constructs ({} are the default delimiters for template actions; - [] are the notation in this comment for optional elements): - - {# comment } - - A one-line comment. - - {.section field} XXX [ {.or} YYY ] {.end} - - Set @ to the value of the field. It may be an explicit @ - to stay at the same point in the data. If the field is nil - or empty, execute YYY; otherwise execute XXX. - - {.repeated section field} XXX [ {.alternates with} ZZZ ] [ {.or} YYY ] {.end} - - Like .section, but field must be an array or slice. XXX - is executed for each element. If the array is nil or empty, - YYY is executed instead. If the {.alternates with} marker - is present, ZZZ is executed between iterations of XXX. - - {field} - {field1 field2 ...} - {field|formatter} - {field1 field2...|formatter} - {field|formatter1|formatter2} - - Insert the value of the fields into the output. Each field is - first looked for in the cursor, as in .section and .repeated. - If it is not found, the search continues in outer sections - until the top level is reached. - - If the field value is a pointer, leading asterisks indicate - that the value to be inserted should be evaluated through the - pointer. For example, if x.p is of type *int, {x.p} will - insert the value of the pointer but {*x.p} will insert the - value of the underlying integer. If the value is nil or not a - pointer, asterisks have no effect. - - If a formatter is specified, it must be named in the formatter - map passed to the template set up routines or in the default - set ("html","str","") and is used to process the data for - output. The formatter function has signature - func(wr io.Writer, formatter string, data ...interface{}) - where wr is the destination for output, data holds the field - values at the instantiation, and formatter is its name at - the invocation site. The default formatter just concatenates - the string representations of the fields. - - Multiple formatters separated by the pipeline character | are - executed sequentially, with each formatter receiving the bytes - emitted by the one to its left. - - The delimiter strings get their default value, "{" and "}", from - JSON-template. They may be set to any non-empty, space-free - string using the SetDelims method. Their value can be printed - in the output using {.meta-left} and {.meta-right}. -*/ -package template - -import ( - "bytes" - "container/vector" - "fmt" - "io" - "io/ioutil" - "os" - "reflect" - "strings" - "unicode" - "utf8" -) - -// Errors returned during parsing and execution. Users may extract the information and reformat -// if they desire. -type Error struct { - Line int - Msg string -} - -func (e *Error) String() string { return fmt.Sprintf("line %d: %s", e.Line, e.Msg) } - -// Most of the literals are aces. -var lbrace = []byte{'{'} -var rbrace = []byte{'}'} -var space = []byte{' '} -var tab = []byte{'\t'} - -// The various types of "tokens", which are plain text or (usually) brace-delimited descriptors -const ( - tokAlternates = iota - tokComment - tokEnd - tokLiteral - tokOr - tokRepeated - tokSection - tokText - tokVariable -) - -// FormatterMap is the type describing the mapping from formatter -// names to the functions that implement them. -type FormatterMap map[string]func(io.Writer, string, ...interface{}) - -// Built-in formatters. -var builtins = FormatterMap{ - "html": HTMLFormatter, - "str": StringFormatter, - "": StringFormatter, -} - -// The parsed state of a template is a vector of xxxElement structs. -// Sections have line numbers so errors can be reported better during execution. - -// Plain text. -type textElement struct { - text []byte -} - -// A literal such as .meta-left or .meta-right -type literalElement struct { - text []byte -} - -// A variable invocation to be evaluated -type variableElement struct { - linenum int - word []string // The fields in the invocation. - fmts []string // Names of formatters to apply. len(fmts) > 0 -} - -// A .section block, possibly with a .or -type sectionElement struct { - linenum int // of .section itself - field string // cursor field for this block - start int // first element - or int // first element of .or block - end int // one beyond last element -} - -// A .repeated block, possibly with a .or and a .alternates -type repeatedElement struct { - sectionElement // It has the same structure... - altstart int // ... except for alternates - altend int -} - -// Template is the type that represents a template definition. -// It is unchanged after parsing. -type Template struct { - fmap FormatterMap // formatters for variables - // Used during parsing: - ldelim, rdelim []byte // delimiters; default {} - buf []byte // input text to process - p int // position in buf - linenum int // position in input - // Parsed results: - elems *vector.Vector -} - -// Internal state for executing a Template. As we evaluate the struct, -// the data item descends into the fields associated with sections, etc. -// Parent is used to walk upwards to find variables higher in the tree. -type state struct { - parent *state // parent in hierarchy - data reflect.Value // the driver data for this section etc. - wr io.Writer // where to send output - buf [2]bytes.Buffer // alternating buffers used when chaining formatters -} - -func (parent *state) clone(data reflect.Value) *state { - return &state{parent: parent, data: data, wr: parent.wr} -} - -// New creates a new template with the specified formatter map (which -// may be nil) to define auxiliary functions for formatting variables. -func New(fmap FormatterMap) *Template { - t := new(Template) - t.fmap = fmap - t.ldelim = lbrace - t.rdelim = rbrace - t.elems = new(vector.Vector) - return t -} - -// Report error and stop executing. The line number must be provided explicitly. -func (t *Template) execError(st *state, line int, err string, args ...interface{}) { - panic(&Error{line, fmt.Sprintf(err, args...)}) -} - -// Report error, panic to terminate parsing. -// The line number comes from the template state. -func (t *Template) parseError(err string, args ...interface{}) { - panic(&Error{t.linenum, fmt.Sprintf(err, args...)}) -} - -// Is this an exported - upper case - name? -func isExported(name string) bool { - rune, _ := utf8.DecodeRuneInString(name) - return unicode.IsUpper(rune) -} - -// -- Lexical analysis - -// Is c a white space character? -func white(c uint8) bool { return c == ' ' || c == '\t' || c == '\r' || c == '\n' } - -// Safely, does s[n:n+len(t)] == t? -func equal(s []byte, n int, t []byte) bool { - b := s[n:] - if len(t) > len(b) { // not enough space left for a match. - return false - } - for i, c := range t { - if c != b[i] { - return false - } - } - return true -} - -// nextItem returns the next item from the input buffer. If the returned -// item is empty, we are at EOF. The item will be either a -// delimited string or a non-empty string between delimited -// strings. Tokens stop at (but include, if plain text) a newline. -// Action tokens on a line by themselves drop any space on -// either side, up to and including the newline. -func (t *Template) nextItem() []byte { - startOfLine := t.p == 0 || t.buf[t.p-1] == '\n' - start := t.p - var i int - newline := func() { - t.linenum++ - i++ - } - // Leading white space up to but not including newline - for i = start; i < len(t.buf); i++ { - if t.buf[i] == '\n' || !white(t.buf[i]) { - break - } - } - leadingSpace := i > start - // What's left is nothing, newline, delimited string, or plain text - switch { - case i == len(t.buf): - // EOF; nothing to do - case t.buf[i] == '\n': - newline() - case equal(t.buf, i, t.ldelim): - left := i // Start of left delimiter. - right := -1 // Will be (immediately after) right delimiter. - haveText := false // Delimiters contain text. - i += len(t.ldelim) - // Find the end of the action. - for ; i < len(t.buf); i++ { - if t.buf[i] == '\n' { - break - } - if equal(t.buf, i, t.rdelim) { - i += len(t.rdelim) - right = i - break - } - haveText = true - } - if right < 0 { - t.parseError("unmatched opening delimiter") - return nil - } - // Is this a special action (starts with '.' or '#') and the only thing on the line? - if startOfLine && haveText { - firstChar := t.buf[left+len(t.ldelim)] - if firstChar == '.' || firstChar == '#' { - // It's special and the first thing on the line. Is it the last? - for j := right; j < len(t.buf) && white(t.buf[j]); j++ { - if t.buf[j] == '\n' { - // Yes it is. Drop the surrounding space and return the {.foo} - t.linenum++ - t.p = j + 1 - return t.buf[left:right] - } - } - } - } - // No it's not. If there's leading space, return that. - if leadingSpace { - // not trimming space: return leading white space if there is some. - t.p = left - return t.buf[start:left] - } - // Return the word, leave the trailing space. - start = left - break - default: - for ; i < len(t.buf); i++ { - if t.buf[i] == '\n' { - newline() - break - } - if equal(t.buf, i, t.ldelim) { - break - } - } - } - item := t.buf[start:i] - t.p = i - return item -} - -// Turn a byte array into a white-space-split array of strings. -func words(buf []byte) []string { - s := make([]string, 0, 5) - p := 0 // position in buf - // one word per loop - for i := 0; ; i++ { - // skip white space - for ; p < len(buf) && white(buf[p]); p++ { - } - // grab word - start := p - for ; p < len(buf) && !white(buf[p]); p++ { - } - if start == p { // no text left - break - } - s = append(s, string(buf[start:p])) - } - return s -} - -// Analyze an item and return its token type and, if it's an action item, an array of -// its constituent words. -func (t *Template) analyze(item []byte) (tok int, w []string) { - // item is known to be non-empty - if !equal(item, 0, t.ldelim) { // doesn't start with left delimiter - tok = tokText - return - } - if !equal(item, len(item)-len(t.rdelim), t.rdelim) { // doesn't end with right delimiter - t.parseError("internal error: unmatched opening delimiter") // lexing should prevent this - return - } - if len(item) <= len(t.ldelim)+len(t.rdelim) { // no contents - t.parseError("empty directive") - return - } - // Comment - if item[len(t.ldelim)] == '#' { - tok = tokComment - return - } - // Split into words - w = words(item[len(t.ldelim) : len(item)-len(t.rdelim)]) // drop final delimiter - if len(w) == 0 { - t.parseError("empty directive") - return - } - if len(w) > 0 && w[0][0] != '.' { - tok = tokVariable - return - } - switch w[0] { - case ".meta-left", ".meta-right", ".space", ".tab": - tok = tokLiteral - return - case ".or": - tok = tokOr - return - case ".end": - tok = tokEnd - return - case ".section": - if len(w) != 2 { - t.parseError("incorrect fields for .section: %s", item) - return - } - tok = tokSection - return - case ".repeated": - if len(w) != 3 || w[1] != "section" { - t.parseError("incorrect fields for .repeated: %s", item) - return - } - tok = tokRepeated - return - case ".alternates": - if len(w) != 2 || w[1] != "with" { - t.parseError("incorrect fields for .alternates: %s", item) - return - } - tok = tokAlternates - return - } - t.parseError("bad directive: %s", item) - return -} - -// formatter returns the Formatter with the given name in the Template, or nil if none exists. -func (t *Template) formatter(name string) func(io.Writer, string, ...interface{}) { - if t.fmap != nil { - if fn := t.fmap[name]; fn != nil { - return fn - } - } - return builtins[name] -} - -// -- Parsing - -// Allocate a new variable-evaluation element. -func (t *Template) newVariable(words []string) *variableElement { - // After the final space-separated argument, formatters may be specified separated - // by pipe symbols, for example: {a b c|d|e} - - // Until we learn otherwise, formatters contains a single name: "", the default formatter. - formatters := []string{""} - lastWord := words[len(words)-1] - bar := strings.IndexRune(lastWord, '|') - if bar >= 0 { - words[len(words)-1] = lastWord[0:bar] - formatters = strings.Split(lastWord[bar+1:], "|") - } - - // We could remember the function address here and avoid the lookup later, - // but it's more dynamic to let the user change the map contents underfoot. - // We do require the name to be present, though. - - // Is it in user-supplied map? - for _, f := range formatters { - if t.formatter(f) == nil { - t.parseError("unknown formatter: %q", f) - } - } - return &variableElement{t.linenum, words, formatters} -} - -// Grab the next item. If it's simple, just append it to the template. -// Otherwise return its details. -func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) { - tok, w = t.analyze(item) - done = true // assume for simplicity - switch tok { - case tokComment: - return - case tokText: - t.elems.Push(&textElement{item}) - return - case tokLiteral: - switch w[0] { - case ".meta-left": - t.elems.Push(&literalElement{t.ldelim}) - case ".meta-right": - t.elems.Push(&literalElement{t.rdelim}) - case ".space": - t.elems.Push(&literalElement{space}) - case ".tab": - t.elems.Push(&literalElement{tab}) - default: - t.parseError("internal error: unknown literal: %s", w[0]) - } - return - case tokVariable: - t.elems.Push(t.newVariable(w)) - return - } - return false, tok, w -} - -// parseRepeated and parseSection are mutually recursive - -func (t *Template) parseRepeated(words []string) *repeatedElement { - r := new(repeatedElement) - t.elems.Push(r) - r.linenum = t.linenum - r.field = words[2] - // Scan section, collecting true and false (.or) blocks. - r.start = t.elems.Len() - r.or = -1 - r.altstart = -1 - r.altend = -1 -Loop: - for { - item := t.nextItem() - if len(item) == 0 { - t.parseError("missing .end for .repeated section") - break - } - done, tok, w := t.parseSimple(item) - if done { - continue - } - switch tok { - case tokEnd: - break Loop - case tokOr: - if r.or >= 0 { - t.parseError("extra .or in .repeated section") - break Loop - } - r.altend = t.elems.Len() - r.or = t.elems.Len() - case tokSection: - t.parseSection(w) - case tokRepeated: - t.parseRepeated(w) - case tokAlternates: - if r.altstart >= 0 { - t.parseError("extra .alternates in .repeated section") - break Loop - } - if r.or >= 0 { - t.parseError(".alternates inside .or block in .repeated section") - break Loop - } - r.altstart = t.elems.Len() - default: - t.parseError("internal error: unknown repeated section item: %s", item) - break Loop - } - } - if r.altend < 0 { - r.altend = t.elems.Len() - } - r.end = t.elems.Len() - return r -} - -func (t *Template) parseSection(words []string) *sectionElement { - s := new(sectionElement) - t.elems.Push(s) - s.linenum = t.linenum - s.field = words[1] - // Scan section, collecting true and false (.or) blocks. - s.start = t.elems.Len() - s.or = -1 -Loop: - for { - item := t.nextItem() - if len(item) == 0 { - t.parseError("missing .end for .section") - break - } - done, tok, w := t.parseSimple(item) - if done { - continue - } - switch tok { - case tokEnd: - break Loop - case tokOr: - if s.or >= 0 { - t.parseError("extra .or in .section") - break Loop - } - s.or = t.elems.Len() - case tokSection: - t.parseSection(w) - case tokRepeated: - t.parseRepeated(w) - case tokAlternates: - t.parseError(".alternates not in .repeated") - default: - t.parseError("internal error: unknown section item: %s", item) - } - } - s.end = t.elems.Len() - return s -} - -func (t *Template) parse() { - for { - item := t.nextItem() - if len(item) == 0 { - break - } - done, tok, w := t.parseSimple(item) - if done { - continue - } - switch tok { - case tokOr, tokEnd, tokAlternates: - t.parseError("unexpected %s", w[0]) - case tokSection: - t.parseSection(w) - case tokRepeated: - t.parseRepeated(w) - default: - t.parseError("internal error: bad directive in parse: %s", item) - } - } -} - -// -- Execution - -// Evaluate interfaces and pointers looking for a value that can look up the name, via a -// struct field, method, or map key, and return the result of the lookup. -func (t *Template) lookup(st *state, v reflect.Value, name string) reflect.Value { - for v.IsValid() { - typ := v.Type() - if n := v.Type().NumMethod(); n > 0 { - for i := 0; i < n; i++ { - m := typ.Method(i) - mtyp := m.Type - if m.Name == name && mtyp.NumIn() == 1 && mtyp.NumOut() == 1 { - if !isExported(name) { - t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type()) - } - return v.Method(i).Call(nil)[0] - } - } - } - switch av := v; av.Kind() { - case reflect.Ptr: - v = av.Elem() - case reflect.Interface: - v = av.Elem() - case reflect.Struct: - if !isExported(name) { - t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type()) - } - return av.FieldByName(name) - case reflect.Map: - if v := av.MapIndex(reflect.ValueOf(name)); v.IsValid() { - return v - } - return reflect.Zero(typ.Elem()) - default: - return reflect.Value{} - } - } - return v -} - -// indirectPtr returns the item numLevels levels of indirection below the value. -// It is forgiving: if the value is not a pointer, it returns it rather than giving -// an error. If the pointer is nil, it is returned as is. -func indirectPtr(v reflect.Value, numLevels int) reflect.Value { - for i := numLevels; v.IsValid() && i > 0; i++ { - if p := v; p.Kind() == reflect.Ptr { - if p.IsNil() { - return v - } - v = p.Elem() - } else { - break - } - } - return v -} - -// Walk v through pointers and interfaces, extracting the elements within. -func indirect(v reflect.Value) reflect.Value { -loop: - for v.IsValid() { - switch av := v; av.Kind() { - case reflect.Ptr: - v = av.Elem() - case reflect.Interface: - v = av.Elem() - default: - break loop - } - } - return v -} - -// If the data for this template is a struct, find the named variable. -// Names of the form a.b.c are walked down the data tree. -// The special name "@" (the "cursor") denotes the current data. -// The value coming in (st.data) might need indirecting to reach -// a struct while the return value is not indirected - that is, -// it represents the actual named field. Leading stars indicate -// levels of indirection to be applied to the value. -func (t *Template) findVar(st *state, s string) reflect.Value { - data := st.data - flattenedName := strings.TrimLeft(s, "*") - numStars := len(s) - len(flattenedName) - s = flattenedName - if s == "@" { - return indirectPtr(data, numStars) - } - for _, elem := range strings.Split(s, ".") { - // Look up field; data must be a struct or map. - data = t.lookup(st, data, elem) - if !data.IsValid() { - return reflect.Value{} - } - } - return indirectPtr(data, numStars) -} - -// Is there no data to look at? -func empty(v reflect.Value) bool { - v = indirect(v) - if !v.IsValid() { - return true - } - switch v.Kind() { - case reflect.Bool: - return v.Bool() == false - case reflect.String: - return v.String() == "" - case reflect.Struct: - return false - case reflect.Map: - return false - case reflect.Array: - return v.Len() == 0 - case reflect.Slice: - return v.Len() == 0 - } - return false -} - -// Look up a variable or method, up through the parent if necessary. -func (t *Template) varValue(name string, st *state) reflect.Value { - field := t.findVar(st, name) - if !field.IsValid() { - if st.parent == nil { - t.execError(st, t.linenum, "name not found: %s in type %s", name, st.data.Type()) - } - return t.varValue(name, st.parent) - } - return field -} - -func (t *Template) format(wr io.Writer, fmt string, val []interface{}, v *variableElement, st *state) { - fn := t.formatter(fmt) - if fn == nil { - t.execError(st, v.linenum, "missing formatter %s for variable %s", fmt, v.word[0]) - } - fn(wr, fmt, val...) -} - -// Evaluate a variable, looking up through the parent if necessary. -// If it has a formatter attached ({var|formatter}) run that too. -func (t *Template) writeVariable(v *variableElement, st *state) { - // Turn the words of the invocation into values. - val := make([]interface{}, len(v.word)) - for i, word := range v.word { - val[i] = t.varValue(word, st).Interface() - } - - for i, fmt := range v.fmts[:len(v.fmts)-1] { - b := &st.buf[i&1] - b.Reset() - t.format(b, fmt, val, v, st) - val = val[0:1] - val[0] = b.Bytes() - } - t.format(st.wr, v.fmts[len(v.fmts)-1], val, v, st) -} - -// Execute element i. Return next index to execute. -func (t *Template) executeElement(i int, st *state) int { - switch elem := t.elems.At(i).(type) { - case *textElement: - st.wr.Write(elem.text) - return i + 1 - case *literalElement: - st.wr.Write(elem.text) - return i + 1 - case *variableElement: - t.writeVariable(elem, st) - return i + 1 - case *sectionElement: - t.executeSection(elem, st) - return elem.end - case *repeatedElement: - t.executeRepeated(elem, st) - return elem.end - } - e := t.elems.At(i) - t.execError(st, 0, "internal error: bad directive in execute: %v %T\n", reflect.ValueOf(e).Interface(), e) - return 0 -} - -// Execute the template. -func (t *Template) execute(start, end int, st *state) { - for i := start; i < end; { - i = t.executeElement(i, st) - } -} - -// Execute a .section -func (t *Template) executeSection(s *sectionElement, st *state) { - // Find driver data for this section. It must be in the current struct. - field := t.varValue(s.field, st) - if !field.IsValid() { - t.execError(st, s.linenum, ".section: cannot find field %s in %s", s.field, st.data.Type()) - } - st = st.clone(field) - start, end := s.start, s.or - if !empty(field) { - // Execute the normal block. - if end < 0 { - end = s.end - } - } else { - // Execute the .or block. If it's missing, do nothing. - start, end = s.or, s.end - if start < 0 { - return - } - } - for i := start; i < end; { - i = t.executeElement(i, st) - } -} - -// Return the result of calling the Iter method on v, or nil. -func iter(v reflect.Value) reflect.Value { - for j := 0; j < v.Type().NumMethod(); j++ { - mth := v.Type().Method(j) - fv := v.Method(j) - ft := fv.Type() - // TODO(rsc): NumIn() should return 0 here, because ft is from a curried FuncValue. - if mth.Name != "Iter" || ft.NumIn() != 1 || ft.NumOut() != 1 { - continue - } - ct := ft.Out(0) - if ct.Kind() != reflect.Chan || - ct.ChanDir()&reflect.RecvDir == 0 { - continue - } - return fv.Call(nil)[0] - } - return reflect.Value{} -} - -// Execute a .repeated section -func (t *Template) executeRepeated(r *repeatedElement, st *state) { - // Find driver data for this section. It must be in the current struct. - field := t.varValue(r.field, st) - if !field.IsValid() { - t.execError(st, r.linenum, ".repeated: cannot find field %s in %s", r.field, st.data.Type()) - } - field = indirect(field) - - start, end := r.start, r.or - if end < 0 { - end = r.end - } - if r.altstart >= 0 { - end = r.altstart - } - first := true - - // Code common to all the loops. - loopBody := func(newst *state) { - // .alternates between elements - if !first && r.altstart >= 0 { - for i := r.altstart; i < r.altend; { - i = t.executeElement(i, newst) - } - } - first = false - for i := start; i < end; { - i = t.executeElement(i, newst) - } - } - - if array := field; array.Kind() == reflect.Array || array.Kind() == reflect.Slice { - for j := 0; j < array.Len(); j++ { - loopBody(st.clone(array.Index(j))) - } - } else if m := field; m.Kind() == reflect.Map { - for _, key := range m.MapKeys() { - loopBody(st.clone(m.MapIndex(key))) - } - } else if ch := iter(field); ch.IsValid() { - for { - e, ok := ch.Recv() - if !ok { - break - } - loopBody(st.clone(e)) - } - } else { - t.execError(st, r.linenum, ".repeated: cannot repeat %s (type %s)", - r.field, field.Type()) - } - - if first { - // Empty. Execute the .or block, once. If it's missing, do nothing. - start, end := r.or, r.end - if start >= 0 { - newst := st.clone(field) - for i := start; i < end; { - i = t.executeElement(i, newst) - } - } - return - } -} - -// A valid delimiter must contain no white space and be non-empty. -func validDelim(d []byte) bool { - if len(d) == 0 { - return false - } - for _, c := range d { - if white(c) { - return false - } - } - return true -} - -// checkError is a deferred function to turn a panic with type *Error into a plain error return. -// Other panics are unexpected and so are re-enabled. -func checkError(error *os.Error) { - if v := recover(); v != nil { - if e, ok := v.(*Error); ok { - *error = e - } else { - // runtime errors should crash - panic(v) - } - } -} - -// -- Public interface - -// Parse initializes a Template by parsing its definition. The string -// s contains the template text. If any errors occur, Parse returns -// the error. -func (t *Template) Parse(s string) (err os.Error) { - if t.elems == nil { - return &Error{1, "template not allocated with New"} - } - if !validDelim(t.ldelim) || !validDelim(t.rdelim) { - return &Error{1, fmt.Sprintf("bad delimiter strings %q %q", t.ldelim, t.rdelim)} - } - defer checkError(&err) - t.buf = []byte(s) - t.p = 0 - t.linenum = 1 - t.parse() - return nil -} - -// ParseFile is like Parse but reads the template definition from the -// named file. -func (t *Template) ParseFile(filename string) (err os.Error) { - b, err := ioutil.ReadFile(filename) - if err != nil { - return err - } - return t.Parse(string(b)) -} - -// Execute applies a parsed template to the specified data object, -// generating output to wr. -func (t *Template) Execute(wr io.Writer, data interface{}) (err os.Error) { - // Extract the driver data. - val := reflect.ValueOf(data) - defer checkError(&err) - t.p = 0 - t.execute(0, t.elems.Len(), &state{parent: nil, data: val, wr: wr}) - return nil -} - -// SetDelims sets the left and right delimiters for operations in the -// template. They are validated during parsing. They could be -// validated here but it's better to keep the routine simple. The -// delimiters are very rarely invalid and Parse has the necessary -// error-handling interface already. -func (t *Template) SetDelims(left, right string) { - t.ldelim = []byte(left) - t.rdelim = []byte(right) -} - -// Parse creates a Template with default parameters (such as {} for -// metacharacters). The string s contains the template text while -// the formatter map fmap, which may be nil, defines auxiliary functions -// for formatting variables. The template is returned. If any errors -// occur, err will be non-nil. -func Parse(s string, fmap FormatterMap) (t *Template, err os.Error) { - t = New(fmap) - err = t.Parse(s) - if err != nil { - t = nil - } - return -} - -// ParseFile is a wrapper function that creates a Template with default -// parameters (such as {} for metacharacters). The filename identifies -// a file containing the template text, while the formatter map fmap, which -// may be nil, defines auxiliary functions for formatting variables. -// The template is returned. If any errors occur, err will be non-nil. -func ParseFile(filename string, fmap FormatterMap) (t *Template, err os.Error) { - b, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err - } - return Parse(string(b), fmap) -} - -// MustParse is like Parse but panics if the template cannot be parsed. -func MustParse(s string, fmap FormatterMap) *Template { - t, err := Parse(s, fmap) - if err != nil { - panic("template.MustParse error: " + err.String()) - } - return t -} - -// MustParseFile is like ParseFile but panics if the file cannot be read -// or the template cannot be parsed. -func MustParseFile(filename string, fmap FormatterMap) *Template { - b, err := ioutil.ReadFile(filename) - if err != nil { - panic("template.MustParseFile error: " + err.String()) - } - return MustParse(string(b), fmap) -} diff --git a/src/cmd/fix/testdata/reflect.type.go.in b/src/cmd/fix/testdata/reflect.type.go.in deleted file mode 100644 index 34963bef9..000000000 --- a/src/cmd/fix/testdata/reflect.type.go.in +++ /dev/null @@ -1,790 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package gob - -import ( - "fmt" - "os" - "reflect" - "sync" - "unicode" - "utf8" -) - -// userTypeInfo stores the information associated with a type the user has handed -// to the package. It's computed once and stored in a map keyed by reflection -// type. -type userTypeInfo struct { - user reflect.Type // the type the user handed us - base reflect.Type // the base type after all indirections - indir int // number of indirections to reach the base type - isGobEncoder bool // does the type implement GobEncoder? - isGobDecoder bool // does the type implement GobDecoder? - encIndir int8 // number of indirections to reach the receiver type; may be negative - decIndir int8 // number of indirections to reach the receiver type; may be negative -} - -var ( - // Protected by an RWMutex because we read it a lot and write - // it only when we see a new type, typically when compiling. - userTypeLock sync.RWMutex - userTypeCache = make(map[reflect.Type]*userTypeInfo) -) - -// validType returns, and saves, the information associated with user-provided type rt. -// If the user type is not valid, err will be non-nil. To be used when the error handler -// is not set up. -func validUserType(rt reflect.Type) (ut *userTypeInfo, err os.Error) { - userTypeLock.RLock() - ut = userTypeCache[rt] - userTypeLock.RUnlock() - if ut != nil { - return - } - // Now set the value under the write lock. - userTypeLock.Lock() - defer userTypeLock.Unlock() - if ut = userTypeCache[rt]; ut != nil { - // Lost the race; not a problem. - return - } - ut = new(userTypeInfo) - ut.base = rt - ut.user = rt - // A type that is just a cycle of pointers (such as type T *T) cannot - // be represented in gobs, which need some concrete data. We use a - // cycle detection algorithm from Knuth, Vol 2, Section 3.1, Ex 6, - // pp 539-540. As we step through indirections, run another type at - // half speed. If they meet up, there's a cycle. - slowpoke := ut.base // walks half as fast as ut.base - for { - pt, ok := ut.base.(*reflect.PtrType) - if !ok { - break - } - ut.base = pt.Elem() - if ut.base == slowpoke { // ut.base lapped slowpoke - // recursive pointer type. - return nil, os.NewError("can't represent recursive pointer type " + ut.base.String()) - } - if ut.indir%2 == 0 { - slowpoke = slowpoke.(*reflect.PtrType).Elem() - } - ut.indir++ - } - ut.isGobEncoder, ut.encIndir = implementsInterface(ut.user, gobEncoderCheck) - ut.isGobDecoder, ut.decIndir = implementsInterface(ut.user, gobDecoderCheck) - userTypeCache[rt] = ut - return -} - -const ( - gobEncodeMethodName = "GobEncode" - gobDecodeMethodName = "GobDecode" -) - -// implements returns whether the type implements the interface, as encoded -// in the check function. -func implements(typ reflect.Type, check func(typ reflect.Type) bool) bool { - if typ.NumMethod() == 0 { // avoid allocations etc. unless there's some chance - return false - } - return check(typ) -} - -// gobEncoderCheck makes the type assertion a boolean function. -func gobEncoderCheck(typ reflect.Type) bool { - _, ok := reflect.MakeZero(typ).Interface().(GobEncoder) - return ok -} - -// gobDecoderCheck makes the type assertion a boolean function. -func gobDecoderCheck(typ reflect.Type) bool { - _, ok := reflect.MakeZero(typ).Interface().(GobDecoder) - return ok -} - -// implementsInterface reports whether the type implements the -// interface. (The actual check is done through the provided function.) -// It also returns the number of indirections required to get to the -// implementation. -func implementsInterface(typ reflect.Type, check func(typ reflect.Type) bool) (success bool, indir int8) { - if typ == nil { - return - } - rt := typ - // The type might be a pointer and we need to keep - // dereferencing to the base type until we find an implementation. - for { - if implements(rt, check) { - return true, indir - } - if p, ok := rt.(*reflect.PtrType); ok { - indir++ - if indir > 100 { // insane number of indirections - return false, 0 - } - rt = p.Elem() - continue - } - break - } - // No luck yet, but if this is a base type (non-pointer), the pointer might satisfy. - if _, ok := typ.(*reflect.PtrType); !ok { - // Not a pointer, but does the pointer work? - if implements(reflect.PtrTo(typ), check) { - return true, -1 - } - } - return false, 0 -} - -// userType returns, and saves, the information associated with user-provided type rt. -// If the user type is not valid, it calls error. -func userType(rt reflect.Type) *userTypeInfo { - ut, err := validUserType(rt) - if err != nil { - error(err) - } - return ut -} - -// A typeId represents a gob Type as an integer that can be passed on the wire. -// Internally, typeIds are used as keys to a map to recover the underlying type info. -type typeId int32 - -var nextId typeId // incremented for each new type we build -var typeLock sync.Mutex // set while building a type -const firstUserId = 64 // lowest id number granted to user - -type gobType interface { - id() typeId - setId(id typeId) - name() string - string() string // not public; only for debugging - safeString(seen map[typeId]bool) string -} - -var types = make(map[reflect.Type]gobType) -var idToType = make(map[typeId]gobType) -var builtinIdToType map[typeId]gobType // set in init() after builtins are established - -func setTypeId(typ gobType) { - nextId++ - typ.setId(nextId) - idToType[nextId] = typ -} - -func (t typeId) gobType() gobType { - if t == 0 { - return nil - } - return idToType[t] -} - -// string returns the string representation of the type associated with the typeId. -func (t typeId) string() string { - if t.gobType() == nil { - return "<nil>" - } - return t.gobType().string() -} - -// Name returns the name of the type associated with the typeId. -func (t typeId) name() string { - if t.gobType() == nil { - return "<nil>" - } - return t.gobType().name() -} - -// Common elements of all types. -type CommonType struct { - Name string - Id typeId -} - -func (t *CommonType) id() typeId { return t.Id } - -func (t *CommonType) setId(id typeId) { t.Id = id } - -func (t *CommonType) string() string { return t.Name } - -func (t *CommonType) safeString(seen map[typeId]bool) string { - return t.Name -} - -func (t *CommonType) name() string { return t.Name } - -// Create and check predefined types -// The string for tBytes is "bytes" not "[]byte" to signify its specialness. - -var ( - // Primordial types, needed during initialization. - // Always passed as pointers so the interface{} type - // goes through without losing its interfaceness. - tBool = bootstrapType("bool", (*bool)(nil), 1) - tInt = bootstrapType("int", (*int)(nil), 2) - tUint = bootstrapType("uint", (*uint)(nil), 3) - tFloat = bootstrapType("float", (*float64)(nil), 4) - tBytes = bootstrapType("bytes", (*[]byte)(nil), 5) - tString = bootstrapType("string", (*string)(nil), 6) - tComplex = bootstrapType("complex", (*complex128)(nil), 7) - tInterface = bootstrapType("interface", (*interface{})(nil), 8) - // Reserve some Ids for compatible expansion - tReserved7 = bootstrapType("_reserved1", (*struct{ r7 int })(nil), 9) - tReserved6 = bootstrapType("_reserved1", (*struct{ r6 int })(nil), 10) - tReserved5 = bootstrapType("_reserved1", (*struct{ r5 int })(nil), 11) - tReserved4 = bootstrapType("_reserved1", (*struct{ r4 int })(nil), 12) - tReserved3 = bootstrapType("_reserved1", (*struct{ r3 int })(nil), 13) - tReserved2 = bootstrapType("_reserved1", (*struct{ r2 int })(nil), 14) - tReserved1 = bootstrapType("_reserved1", (*struct{ r1 int })(nil), 15) -) - -// Predefined because it's needed by the Decoder -var tWireType = mustGetTypeInfo(reflect.Typeof(wireType{})).id -var wireTypeUserInfo *userTypeInfo // userTypeInfo of (*wireType) - -func init() { - // Some magic numbers to make sure there are no surprises. - checkId(16, tWireType) - checkId(17, mustGetTypeInfo(reflect.Typeof(arrayType{})).id) - checkId(18, mustGetTypeInfo(reflect.Typeof(CommonType{})).id) - checkId(19, mustGetTypeInfo(reflect.Typeof(sliceType{})).id) - checkId(20, mustGetTypeInfo(reflect.Typeof(structType{})).id) - checkId(21, mustGetTypeInfo(reflect.Typeof(fieldType{})).id) - checkId(23, mustGetTypeInfo(reflect.Typeof(mapType{})).id) - - builtinIdToType = make(map[typeId]gobType) - for k, v := range idToType { - builtinIdToType[k] = v - } - - // Move the id space upwards to allow for growth in the predefined world - // without breaking existing files. - if nextId > firstUserId { - panic(fmt.Sprintln("nextId too large:", nextId)) - } - nextId = firstUserId - registerBasics() - wireTypeUserInfo = userType(reflect.Typeof((*wireType)(nil))) -} - -// Array type -type arrayType struct { - CommonType - Elem typeId - Len int -} - -func newArrayType(name string) *arrayType { - a := &arrayType{CommonType{Name: name}, 0, 0} - return a -} - -func (a *arrayType) init(elem gobType, len int) { - // Set our type id before evaluating the element's, in case it's our own. - setTypeId(a) - a.Elem = elem.id() - a.Len = len -} - -func (a *arrayType) safeString(seen map[typeId]bool) string { - if seen[a.Id] { - return a.Name - } - seen[a.Id] = true - return fmt.Sprintf("[%d]%s", a.Len, a.Elem.gobType().safeString(seen)) -} - -func (a *arrayType) string() string { return a.safeString(make(map[typeId]bool)) } - -// GobEncoder type (something that implements the GobEncoder interface) -type gobEncoderType struct { - CommonType -} - -func newGobEncoderType(name string) *gobEncoderType { - g := &gobEncoderType{CommonType{Name: name}} - setTypeId(g) - return g -} - -func (g *gobEncoderType) safeString(seen map[typeId]bool) string { - return g.Name -} - -func (g *gobEncoderType) string() string { return g.Name } - -// Map type -type mapType struct { - CommonType - Key typeId - Elem typeId -} - -func newMapType(name string) *mapType { - m := &mapType{CommonType{Name: name}, 0, 0} - return m -} - -func (m *mapType) init(key, elem gobType) { - // Set our type id before evaluating the element's, in case it's our own. - setTypeId(m) - m.Key = key.id() - m.Elem = elem.id() -} - -func (m *mapType) safeString(seen map[typeId]bool) string { - if seen[m.Id] { - return m.Name - } - seen[m.Id] = true - key := m.Key.gobType().safeString(seen) - elem := m.Elem.gobType().safeString(seen) - return fmt.Sprintf("map[%s]%s", key, elem) -} - -func (m *mapType) string() string { return m.safeString(make(map[typeId]bool)) } - -// Slice type -type sliceType struct { - CommonType - Elem typeId -} - -func newSliceType(name string) *sliceType { - s := &sliceType{CommonType{Name: name}, 0} - return s -} - -func (s *sliceType) init(elem gobType) { - // Set our type id before evaluating the element's, in case it's our own. - setTypeId(s) - s.Elem = elem.id() -} - -func (s *sliceType) safeString(seen map[typeId]bool) string { - if seen[s.Id] { - return s.Name - } - seen[s.Id] = true - return fmt.Sprintf("[]%s", s.Elem.gobType().safeString(seen)) -} - -func (s *sliceType) string() string { return s.safeString(make(map[typeId]bool)) } - -// Struct type -type fieldType struct { - Name string - Id typeId -} - -type structType struct { - CommonType - Field []*fieldType -} - -func (s *structType) safeString(seen map[typeId]bool) string { - if s == nil { - return "<nil>" - } - if _, ok := seen[s.Id]; ok { - return s.Name - } - seen[s.Id] = true - str := s.Name + " = struct { " - for _, f := range s.Field { - str += fmt.Sprintf("%s %s; ", f.Name, f.Id.gobType().safeString(seen)) - } - str += "}" - return str -} - -func (s *structType) string() string { return s.safeString(make(map[typeId]bool)) } - -func newStructType(name string) *structType { - s := &structType{CommonType{Name: name}, nil} - // For historical reasons we set the id here rather than init. - // See the comment in newTypeObject for details. - setTypeId(s) - return s -} - -// newTypeObject allocates a gobType for the reflection type rt. -// Unless ut represents a GobEncoder, rt should be the base type -// of ut. -// This is only called from the encoding side. The decoding side -// works through typeIds and userTypeInfos alone. -func newTypeObject(name string, ut *userTypeInfo, rt reflect.Type) (gobType, os.Error) { - // Does this type implement GobEncoder? - if ut.isGobEncoder { - return newGobEncoderType(name), nil - } - var err os.Error - var type0, type1 gobType - defer func() { - if err != nil { - types[rt] = nil, false - } - }() - // Install the top-level type before the subtypes (e.g. struct before - // fields) so recursive types can be constructed safely. - switch t := rt.(type) { - // All basic types are easy: they are predefined. - case *reflect.BoolType: - return tBool.gobType(), nil - - case *reflect.IntType: - return tInt.gobType(), nil - - case *reflect.UintType: - return tUint.gobType(), nil - - case *reflect.FloatType: - return tFloat.gobType(), nil - - case *reflect.ComplexType: - return tComplex.gobType(), nil - - case *reflect.StringType: - return tString.gobType(), nil - - case *reflect.InterfaceType: - return tInterface.gobType(), nil - - case *reflect.ArrayType: - at := newArrayType(name) - types[rt] = at - type0, err = getBaseType("", t.Elem()) - if err != nil { - return nil, err - } - // Historical aside: - // For arrays, maps, and slices, we set the type id after the elements - // are constructed. This is to retain the order of type id allocation after - // a fix made to handle recursive types, which changed the order in - // which types are built. Delaying the setting in this way preserves - // type ids while allowing recursive types to be described. Structs, - // done below, were already handling recursion correctly so they - // assign the top-level id before those of the field. - at.init(type0, t.Len()) - return at, nil - - case *reflect.MapType: - mt := newMapType(name) - types[rt] = mt - type0, err = getBaseType("", t.Key()) - if err != nil { - return nil, err - } - type1, err = getBaseType("", t.Elem()) - if err != nil { - return nil, err - } - mt.init(type0, type1) - return mt, nil - - case *reflect.SliceType: - // []byte == []uint8 is a special case - if t.Elem().Kind() == reflect.Uint8 { - return tBytes.gobType(), nil - } - st := newSliceType(name) - types[rt] = st - type0, err = getBaseType(t.Elem().Name(), t.Elem()) - if err != nil { - return nil, err - } - st.init(type0) - return st, nil - - case *reflect.StructType: - st := newStructType(name) - types[rt] = st - idToType[st.id()] = st - for i := 0; i < t.NumField(); i++ { - f := t.Field(i) - if !isExported(f.Name) { - continue - } - typ := userType(f.Type).base - tname := typ.Name() - if tname == "" { - t := userType(f.Type).base - tname = t.String() - } - gt, err := getBaseType(tname, f.Type) - if err != nil { - return nil, err - } - st.Field = append(st.Field, &fieldType{f.Name, gt.id()}) - } - return st, nil - - default: - return nil, os.NewError("gob NewTypeObject can't handle type: " + rt.String()) - } - return nil, nil -} - -// isExported reports whether this is an exported - upper case - name. -func isExported(name string) bool { - rune, _ := utf8.DecodeRuneInString(name) - return unicode.IsUpper(rune) -} - -// getBaseType returns the Gob type describing the given reflect.Type's base type. -// typeLock must be held. -func getBaseType(name string, rt reflect.Type) (gobType, os.Error) { - ut := userType(rt) - return getType(name, ut, ut.base) -} - -// getType returns the Gob type describing the given reflect.Type. -// Should be called only when handling GobEncoders/Decoders, -// which may be pointers. All other types are handled through the -// base type, never a pointer. -// typeLock must be held. -func getType(name string, ut *userTypeInfo, rt reflect.Type) (gobType, os.Error) { - typ, present := types[rt] - if present { - return typ, nil - } - typ, err := newTypeObject(name, ut, rt) - if err == nil { - types[rt] = typ - } - return typ, err -} - -func checkId(want, got typeId) { - if want != got { - fmt.Fprintf(os.Stderr, "checkId: %d should be %d\n", int(got), int(want)) - panic("bootstrap type wrong id: " + got.name() + " " + got.string() + " not " + want.string()) - } -} - -// used for building the basic types; called only from init(). the incoming -// interface always refers to a pointer. -func bootstrapType(name string, e interface{}, expect typeId) typeId { - rt := reflect.Typeof(e).(*reflect.PtrType).Elem() - _, present := types[rt] - if present { - panic("bootstrap type already present: " + name + ", " + rt.String()) - } - typ := &CommonType{Name: name} - types[rt] = typ - setTypeId(typ) - checkId(expect, nextId) - userType(rt) // might as well cache it now - return nextId -} - -// Representation of the information we send and receive about this type. -// Each value we send is preceded by its type definition: an encoded int. -// However, the very first time we send the value, we first send the pair -// (-id, wireType). -// For bootstrapping purposes, we assume that the recipient knows how -// to decode a wireType; it is exactly the wireType struct here, interpreted -// using the gob rules for sending a structure, except that we assume the -// ids for wireType and structType etc. are known. The relevant pieces -// are built in encode.go's init() function. -// To maintain binary compatibility, if you extend this type, always put -// the new fields last. -type wireType struct { - ArrayT *arrayType - SliceT *sliceType - StructT *structType - MapT *mapType - GobEncoderT *gobEncoderType -} - -func (w *wireType) string() string { - const unknown = "unknown type" - if w == nil { - return unknown - } - switch { - case w.ArrayT != nil: - return w.ArrayT.Name - case w.SliceT != nil: - return w.SliceT.Name - case w.StructT != nil: - return w.StructT.Name - case w.MapT != nil: - return w.MapT.Name - case w.GobEncoderT != nil: - return w.GobEncoderT.Name - } - return unknown -} - -type typeInfo struct { - id typeId - encoder *encEngine - wire *wireType -} - -var typeInfoMap = make(map[reflect.Type]*typeInfo) // protected by typeLock - -// typeLock must be held. -func getTypeInfo(ut *userTypeInfo) (*typeInfo, os.Error) { - rt := ut.base - if ut.isGobEncoder { - // We want the user type, not the base type. - rt = ut.user - } - info, ok := typeInfoMap[rt] - if ok { - return info, nil - } - info = new(typeInfo) - gt, err := getBaseType(rt.Name(), rt) - if err != nil { - return nil, err - } - info.id = gt.id() - - if ut.isGobEncoder { - userType, err := getType(rt.Name(), ut, rt) - if err != nil { - return nil, err - } - info.wire = &wireType{GobEncoderT: userType.id().gobType().(*gobEncoderType)} - typeInfoMap[ut.user] = info - return info, nil - } - - t := info.id.gobType() - switch typ := rt.(type) { - case *reflect.ArrayType: - info.wire = &wireType{ArrayT: t.(*arrayType)} - case *reflect.MapType: - info.wire = &wireType{MapT: t.(*mapType)} - case *reflect.SliceType: - // []byte == []uint8 is a special case handled separately - if typ.Elem().Kind() != reflect.Uint8 { - info.wire = &wireType{SliceT: t.(*sliceType)} - } - case *reflect.StructType: - info.wire = &wireType{StructT: t.(*structType)} - } - typeInfoMap[rt] = info - return info, nil -} - -// Called only when a panic is acceptable and unexpected. -func mustGetTypeInfo(rt reflect.Type) *typeInfo { - t, err := getTypeInfo(userType(rt)) - if err != nil { - panic("getTypeInfo: " + err.String()) - } - return t -} - -// GobEncoder is the interface describing data that provides its own -// representation for encoding values for transmission to a GobDecoder. -// A type that implements GobEncoder and GobDecoder has complete -// control over the representation of its data and may therefore -// contain things such as private fields, channels, and functions, -// which are not usually transmissable in gob streams. -// -// Note: Since gobs can be stored permanently, It is good design -// to guarantee the encoding used by a GobEncoder is stable as the -// software evolves. For instance, it might make sense for GobEncode -// to include a version number in the encoding. -type GobEncoder interface { - // GobEncode returns a byte slice representing the encoding of the - // receiver for transmission to a GobDecoder, usually of the same - // concrete type. - GobEncode() ([]byte, os.Error) -} - -// GobDecoder is the interface describing data that provides its own -// routine for decoding transmitted values sent by a GobEncoder. -type GobDecoder interface { - // GobDecode overwrites the receiver, which must be a pointer, - // with the value represented by the byte slice, which was written - // by GobEncode, usually for the same concrete type. - GobDecode([]byte) os.Error -} - -var ( - nameToConcreteType = make(map[string]reflect.Type) - concreteTypeToName = make(map[reflect.Type]string) -) - -// RegisterName is like Register but uses the provided name rather than the -// type's default. -func RegisterName(name string, value interface{}) { - if name == "" { - // reserved for nil - panic("attempt to register empty name") - } - base := userType(reflect.Typeof(value)).base - // Check for incompatible duplicates. - if t, ok := nameToConcreteType[name]; ok && t != base { - panic("gob: registering duplicate types for " + name) - } - if n, ok := concreteTypeToName[base]; ok && n != name { - panic("gob: registering duplicate names for " + base.String()) - } - // Store the name and type provided by the user.... - nameToConcreteType[name] = reflect.Typeof(value) - // but the flattened type in the type table, since that's what decode needs. - concreteTypeToName[base] = name -} - -// Register records a type, identified by a value for that type, under its -// internal type name. That name will identify the concrete type of a value -// sent or received as an interface variable. Only types that will be -// transferred as implementations of interface values need to be registered. -// Expecting to be used only during initialization, it panics if the mapping -// between types and names is not a bijection. -func Register(value interface{}) { - // Default to printed representation for unnamed types - rt := reflect.Typeof(value) - name := rt.String() - - // But for named types (or pointers to them), qualify with import path. - // Dereference one pointer looking for a named type. - star := "" - if rt.Name() == "" { - if pt, ok := rt.(*reflect.PtrType); ok { - star = "*" - rt = pt - } - } - if rt.Name() != "" { - if rt.PkgPath() == "" { - name = star + rt.Name() - } else { - name = star + rt.PkgPath() + "." + rt.Name() - } - } - - RegisterName(name, value) -} - -func registerBasics() { - Register(int(0)) - Register(int8(0)) - Register(int16(0)) - Register(int32(0)) - Register(int64(0)) - Register(uint(0)) - Register(uint8(0)) - Register(uint16(0)) - Register(uint32(0)) - Register(uint64(0)) - Register(float32(0)) - Register(float64(0)) - Register(complex64(0i)) - Register(complex128(0i)) - Register(false) - Register("") - Register([]byte(nil)) -} diff --git a/src/cmd/fix/testdata/reflect.type.go.out b/src/cmd/fix/testdata/reflect.type.go.out deleted file mode 100644 index d729ea471..000000000 --- a/src/cmd/fix/testdata/reflect.type.go.out +++ /dev/null @@ -1,790 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package gob - -import ( - "fmt" - "os" - "reflect" - "sync" - "unicode" - "utf8" -) - -// userTypeInfo stores the information associated with a type the user has handed -// to the package. It's computed once and stored in a map keyed by reflection -// type. -type userTypeInfo struct { - user reflect.Type // the type the user handed us - base reflect.Type // the base type after all indirections - indir int // number of indirections to reach the base type - isGobEncoder bool // does the type implement GobEncoder? - isGobDecoder bool // does the type implement GobDecoder? - encIndir int8 // number of indirections to reach the receiver type; may be negative - decIndir int8 // number of indirections to reach the receiver type; may be negative -} - -var ( - // Protected by an RWMutex because we read it a lot and write - // it only when we see a new type, typically when compiling. - userTypeLock sync.RWMutex - userTypeCache = make(map[reflect.Type]*userTypeInfo) -) - -// validType returns, and saves, the information associated with user-provided type rt. -// If the user type is not valid, err will be non-nil. To be used when the error handler -// is not set up. -func validUserType(rt reflect.Type) (ut *userTypeInfo, err os.Error) { - userTypeLock.RLock() - ut = userTypeCache[rt] - userTypeLock.RUnlock() - if ut != nil { - return - } - // Now set the value under the write lock. - userTypeLock.Lock() - defer userTypeLock.Unlock() - if ut = userTypeCache[rt]; ut != nil { - // Lost the race; not a problem. - return - } - ut = new(userTypeInfo) - ut.base = rt - ut.user = rt - // A type that is just a cycle of pointers (such as type T *T) cannot - // be represented in gobs, which need some concrete data. We use a - // cycle detection algorithm from Knuth, Vol 2, Section 3.1, Ex 6, - // pp 539-540. As we step through indirections, run another type at - // half speed. If they meet up, there's a cycle. - slowpoke := ut.base // walks half as fast as ut.base - for { - pt := ut.base - if pt.Kind() != reflect.Ptr { - break - } - ut.base = pt.Elem() - if ut.base == slowpoke { // ut.base lapped slowpoke - // recursive pointer type. - return nil, os.NewError("can't represent recursive pointer type " + ut.base.String()) - } - if ut.indir%2 == 0 { - slowpoke = slowpoke.Elem() - } - ut.indir++ - } - ut.isGobEncoder, ut.encIndir = implementsInterface(ut.user, gobEncoderCheck) - ut.isGobDecoder, ut.decIndir = implementsInterface(ut.user, gobDecoderCheck) - userTypeCache[rt] = ut - return -} - -const ( - gobEncodeMethodName = "GobEncode" - gobDecodeMethodName = "GobDecode" -) - -// implements returns whether the type implements the interface, as encoded -// in the check function. -func implements(typ reflect.Type, check func(typ reflect.Type) bool) bool { - if typ.NumMethod() == 0 { // avoid allocations etc. unless there's some chance - return false - } - return check(typ) -} - -// gobEncoderCheck makes the type assertion a boolean function. -func gobEncoderCheck(typ reflect.Type) bool { - _, ok := reflect.Zero(typ).Interface().(GobEncoder) - return ok -} - -// gobDecoderCheck makes the type assertion a boolean function. -func gobDecoderCheck(typ reflect.Type) bool { - _, ok := reflect.Zero(typ).Interface().(GobDecoder) - return ok -} - -// implementsInterface reports whether the type implements the -// interface. (The actual check is done through the provided function.) -// It also returns the number of indirections required to get to the -// implementation. -func implementsInterface(typ reflect.Type, check func(typ reflect.Type) bool) (success bool, indir int8) { - if typ == nil { - return - } - rt := typ - // The type might be a pointer and we need to keep - // dereferencing to the base type until we find an implementation. - for { - if implements(rt, check) { - return true, indir - } - if p := rt; p.Kind() == reflect.Ptr { - indir++ - if indir > 100 { // insane number of indirections - return false, 0 - } - rt = p.Elem() - continue - } - break - } - // No luck yet, but if this is a base type (non-pointer), the pointer might satisfy. - if typ.Kind() != reflect.Ptr { - // Not a pointer, but does the pointer work? - if implements(reflect.PtrTo(typ), check) { - return true, -1 - } - } - return false, 0 -} - -// userType returns, and saves, the information associated with user-provided type rt. -// If the user type is not valid, it calls error. -func userType(rt reflect.Type) *userTypeInfo { - ut, err := validUserType(rt) - if err != nil { - error(err) - } - return ut -} - -// A typeId represents a gob Type as an integer that can be passed on the wire. -// Internally, typeIds are used as keys to a map to recover the underlying type info. -type typeId int32 - -var nextId typeId // incremented for each new type we build -var typeLock sync.Mutex // set while building a type -const firstUserId = 64 // lowest id number granted to user - -type gobType interface { - id() typeId - setId(id typeId) - name() string - string() string // not public; only for debugging - safeString(seen map[typeId]bool) string -} - -var types = make(map[reflect.Type]gobType) -var idToType = make(map[typeId]gobType) -var builtinIdToType map[typeId]gobType // set in init() after builtins are established - -func setTypeId(typ gobType) { - nextId++ - typ.setId(nextId) - idToType[nextId] = typ -} - -func (t typeId) gobType() gobType { - if t == 0 { - return nil - } - return idToType[t] -} - -// string returns the string representation of the type associated with the typeId. -func (t typeId) string() string { - if t.gobType() == nil { - return "<nil>" - } - return t.gobType().string() -} - -// Name returns the name of the type associated with the typeId. -func (t typeId) name() string { - if t.gobType() == nil { - return "<nil>" - } - return t.gobType().name() -} - -// Common elements of all types. -type CommonType struct { - Name string - Id typeId -} - -func (t *CommonType) id() typeId { return t.Id } - -func (t *CommonType) setId(id typeId) { t.Id = id } - -func (t *CommonType) string() string { return t.Name } - -func (t *CommonType) safeString(seen map[typeId]bool) string { - return t.Name -} - -func (t *CommonType) name() string { return t.Name } - -// Create and check predefined types -// The string for tBytes is "bytes" not "[]byte" to signify its specialness. - -var ( - // Primordial types, needed during initialization. - // Always passed as pointers so the interface{} type - // goes through without losing its interfaceness. - tBool = bootstrapType("bool", (*bool)(nil), 1) - tInt = bootstrapType("int", (*int)(nil), 2) - tUint = bootstrapType("uint", (*uint)(nil), 3) - tFloat = bootstrapType("float", (*float64)(nil), 4) - tBytes = bootstrapType("bytes", (*[]byte)(nil), 5) - tString = bootstrapType("string", (*string)(nil), 6) - tComplex = bootstrapType("complex", (*complex128)(nil), 7) - tInterface = bootstrapType("interface", (*interface{})(nil), 8) - // Reserve some Ids for compatible expansion - tReserved7 = bootstrapType("_reserved1", (*struct{ r7 int })(nil), 9) - tReserved6 = bootstrapType("_reserved1", (*struct{ r6 int })(nil), 10) - tReserved5 = bootstrapType("_reserved1", (*struct{ r5 int })(nil), 11) - tReserved4 = bootstrapType("_reserved1", (*struct{ r4 int })(nil), 12) - tReserved3 = bootstrapType("_reserved1", (*struct{ r3 int })(nil), 13) - tReserved2 = bootstrapType("_reserved1", (*struct{ r2 int })(nil), 14) - tReserved1 = bootstrapType("_reserved1", (*struct{ r1 int })(nil), 15) -) - -// Predefined because it's needed by the Decoder -var tWireType = mustGetTypeInfo(reflect.TypeOf(wireType{})).id -var wireTypeUserInfo *userTypeInfo // userTypeInfo of (*wireType) - -func init() { - // Some magic numbers to make sure there are no surprises. - checkId(16, tWireType) - checkId(17, mustGetTypeInfo(reflect.TypeOf(arrayType{})).id) - checkId(18, mustGetTypeInfo(reflect.TypeOf(CommonType{})).id) - checkId(19, mustGetTypeInfo(reflect.TypeOf(sliceType{})).id) - checkId(20, mustGetTypeInfo(reflect.TypeOf(structType{})).id) - checkId(21, mustGetTypeInfo(reflect.TypeOf(fieldType{})).id) - checkId(23, mustGetTypeInfo(reflect.TypeOf(mapType{})).id) - - builtinIdToType = make(map[typeId]gobType) - for k, v := range idToType { - builtinIdToType[k] = v - } - - // Move the id space upwards to allow for growth in the predefined world - // without breaking existing files. - if nextId > firstUserId { - panic(fmt.Sprintln("nextId too large:", nextId)) - } - nextId = firstUserId - registerBasics() - wireTypeUserInfo = userType(reflect.TypeOf((*wireType)(nil))) -} - -// Array type -type arrayType struct { - CommonType - Elem typeId - Len int -} - -func newArrayType(name string) *arrayType { - a := &arrayType{CommonType{Name: name}, 0, 0} - return a -} - -func (a *arrayType) init(elem gobType, len int) { - // Set our type id before evaluating the element's, in case it's our own. - setTypeId(a) - a.Elem = elem.id() - a.Len = len -} - -func (a *arrayType) safeString(seen map[typeId]bool) string { - if seen[a.Id] { - return a.Name - } - seen[a.Id] = true - return fmt.Sprintf("[%d]%s", a.Len, a.Elem.gobType().safeString(seen)) -} - -func (a *arrayType) string() string { return a.safeString(make(map[typeId]bool)) } - -// GobEncoder type (something that implements the GobEncoder interface) -type gobEncoderType struct { - CommonType -} - -func newGobEncoderType(name string) *gobEncoderType { - g := &gobEncoderType{CommonType{Name: name}} - setTypeId(g) - return g -} - -func (g *gobEncoderType) safeString(seen map[typeId]bool) string { - return g.Name -} - -func (g *gobEncoderType) string() string { return g.Name } - -// Map type -type mapType struct { - CommonType - Key typeId - Elem typeId -} - -func newMapType(name string) *mapType { - m := &mapType{CommonType{Name: name}, 0, 0} - return m -} - -func (m *mapType) init(key, elem gobType) { - // Set our type id before evaluating the element's, in case it's our own. - setTypeId(m) - m.Key = key.id() - m.Elem = elem.id() -} - -func (m *mapType) safeString(seen map[typeId]bool) string { - if seen[m.Id] { - return m.Name - } - seen[m.Id] = true - key := m.Key.gobType().safeString(seen) - elem := m.Elem.gobType().safeString(seen) - return fmt.Sprintf("map[%s]%s", key, elem) -} - -func (m *mapType) string() string { return m.safeString(make(map[typeId]bool)) } - -// Slice type -type sliceType struct { - CommonType - Elem typeId -} - -func newSliceType(name string) *sliceType { - s := &sliceType{CommonType{Name: name}, 0} - return s -} - -func (s *sliceType) init(elem gobType) { - // Set our type id before evaluating the element's, in case it's our own. - setTypeId(s) - s.Elem = elem.id() -} - -func (s *sliceType) safeString(seen map[typeId]bool) string { - if seen[s.Id] { - return s.Name - } - seen[s.Id] = true - return fmt.Sprintf("[]%s", s.Elem.gobType().safeString(seen)) -} - -func (s *sliceType) string() string { return s.safeString(make(map[typeId]bool)) } - -// Struct type -type fieldType struct { - Name string - Id typeId -} - -type structType struct { - CommonType - Field []*fieldType -} - -func (s *structType) safeString(seen map[typeId]bool) string { - if s == nil { - return "<nil>" - } - if _, ok := seen[s.Id]; ok { - return s.Name - } - seen[s.Id] = true - str := s.Name + " = struct { " - for _, f := range s.Field { - str += fmt.Sprintf("%s %s; ", f.Name, f.Id.gobType().safeString(seen)) - } - str += "}" - return str -} - -func (s *structType) string() string { return s.safeString(make(map[typeId]bool)) } - -func newStructType(name string) *structType { - s := &structType{CommonType{Name: name}, nil} - // For historical reasons we set the id here rather than init. - // See the comment in newTypeObject for details. - setTypeId(s) - return s -} - -// newTypeObject allocates a gobType for the reflection type rt. -// Unless ut represents a GobEncoder, rt should be the base type -// of ut. -// This is only called from the encoding side. The decoding side -// works through typeIds and userTypeInfos alone. -func newTypeObject(name string, ut *userTypeInfo, rt reflect.Type) (gobType, os.Error) { - // Does this type implement GobEncoder? - if ut.isGobEncoder { - return newGobEncoderType(name), nil - } - var err os.Error - var type0, type1 gobType - defer func() { - if err != nil { - types[rt] = nil, false - } - }() - // Install the top-level type before the subtypes (e.g. struct before - // fields) so recursive types can be constructed safely. - switch t := rt; t.Kind() { - // All basic types are easy: they are predefined. - case reflect.Bool: - return tBool.gobType(), nil - - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return tInt.gobType(), nil - - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return tUint.gobType(), nil - - case reflect.Float32, reflect.Float64: - return tFloat.gobType(), nil - - case reflect.Complex64, reflect.Complex128: - return tComplex.gobType(), nil - - case reflect.String: - return tString.gobType(), nil - - case reflect.Interface: - return tInterface.gobType(), nil - - case reflect.Array: - at := newArrayType(name) - types[rt] = at - type0, err = getBaseType("", t.Elem()) - if err != nil { - return nil, err - } - // Historical aside: - // For arrays, maps, and slices, we set the type id after the elements - // are constructed. This is to retain the order of type id allocation after - // a fix made to handle recursive types, which changed the order in - // which types are built. Delaying the setting in this way preserves - // type ids while allowing recursive types to be described. Structs, - // done below, were already handling recursion correctly so they - // assign the top-level id before those of the field. - at.init(type0, t.Len()) - return at, nil - - case reflect.Map: - mt := newMapType(name) - types[rt] = mt - type0, err = getBaseType("", t.Key()) - if err != nil { - return nil, err - } - type1, err = getBaseType("", t.Elem()) - if err != nil { - return nil, err - } - mt.init(type0, type1) - return mt, nil - - case reflect.Slice: - // []byte == []uint8 is a special case - if t.Elem().Kind() == reflect.Uint8 { - return tBytes.gobType(), nil - } - st := newSliceType(name) - types[rt] = st - type0, err = getBaseType(t.Elem().Name(), t.Elem()) - if err != nil { - return nil, err - } - st.init(type0) - return st, nil - - case reflect.Struct: - st := newStructType(name) - types[rt] = st - idToType[st.id()] = st - for i := 0; i < t.NumField(); i++ { - f := t.Field(i) - if !isExported(f.Name) { - continue - } - typ := userType(f.Type).base - tname := typ.Name() - if tname == "" { - t := userType(f.Type).base - tname = t.String() - } - gt, err := getBaseType(tname, f.Type) - if err != nil { - return nil, err - } - st.Field = append(st.Field, &fieldType{f.Name, gt.id()}) - } - return st, nil - - default: - return nil, os.NewError("gob NewTypeObject can't handle type: " + rt.String()) - } - return nil, nil -} - -// isExported reports whether this is an exported - upper case - name. -func isExported(name string) bool { - rune, _ := utf8.DecodeRuneInString(name) - return unicode.IsUpper(rune) -} - -// getBaseType returns the Gob type describing the given reflect.Type's base type. -// typeLock must be held. -func getBaseType(name string, rt reflect.Type) (gobType, os.Error) { - ut := userType(rt) - return getType(name, ut, ut.base) -} - -// getType returns the Gob type describing the given reflect.Type. -// Should be called only when handling GobEncoders/Decoders, -// which may be pointers. All other types are handled through the -// base type, never a pointer. -// typeLock must be held. -func getType(name string, ut *userTypeInfo, rt reflect.Type) (gobType, os.Error) { - typ, present := types[rt] - if present { - return typ, nil - } - typ, err := newTypeObject(name, ut, rt) - if err == nil { - types[rt] = typ - } - return typ, err -} - -func checkId(want, got typeId) { - if want != got { - fmt.Fprintf(os.Stderr, "checkId: %d should be %d\n", int(got), int(want)) - panic("bootstrap type wrong id: " + got.name() + " " + got.string() + " not " + want.string()) - } -} - -// used for building the basic types; called only from init(). the incoming -// interface always refers to a pointer. -func bootstrapType(name string, e interface{}, expect typeId) typeId { - rt := reflect.TypeOf(e).Elem() - _, present := types[rt] - if present { - panic("bootstrap type already present: " + name + ", " + rt.String()) - } - typ := &CommonType{Name: name} - types[rt] = typ - setTypeId(typ) - checkId(expect, nextId) - userType(rt) // might as well cache it now - return nextId -} - -// Representation of the information we send and receive about this type. -// Each value we send is preceded by its type definition: an encoded int. -// However, the very first time we send the value, we first send the pair -// (-id, wireType). -// For bootstrapping purposes, we assume that the recipient knows how -// to decode a wireType; it is exactly the wireType struct here, interpreted -// using the gob rules for sending a structure, except that we assume the -// ids for wireType and structType etc. are known. The relevant pieces -// are built in encode.go's init() function. -// To maintain binary compatibility, if you extend this type, always put -// the new fields last. -type wireType struct { - ArrayT *arrayType - SliceT *sliceType - StructT *structType - MapT *mapType - GobEncoderT *gobEncoderType -} - -func (w *wireType) string() string { - const unknown = "unknown type" - if w == nil { - return unknown - } - switch { - case w.ArrayT != nil: - return w.ArrayT.Name - case w.SliceT != nil: - return w.SliceT.Name - case w.StructT != nil: - return w.StructT.Name - case w.MapT != nil: - return w.MapT.Name - case w.GobEncoderT != nil: - return w.GobEncoderT.Name - } - return unknown -} - -type typeInfo struct { - id typeId - encoder *encEngine - wire *wireType -} - -var typeInfoMap = make(map[reflect.Type]*typeInfo) // protected by typeLock - -// typeLock must be held. -func getTypeInfo(ut *userTypeInfo) (*typeInfo, os.Error) { - rt := ut.base - if ut.isGobEncoder { - // We want the user type, not the base type. - rt = ut.user - } - info, ok := typeInfoMap[rt] - if ok { - return info, nil - } - info = new(typeInfo) - gt, err := getBaseType(rt.Name(), rt) - if err != nil { - return nil, err - } - info.id = gt.id() - - if ut.isGobEncoder { - userType, err := getType(rt.Name(), ut, rt) - if err != nil { - return nil, err - } - info.wire = &wireType{GobEncoderT: userType.id().gobType().(*gobEncoderType)} - typeInfoMap[ut.user] = info - return info, nil - } - - t := info.id.gobType() - switch typ := rt; typ.Kind() { - case reflect.Array: - info.wire = &wireType{ArrayT: t.(*arrayType)} - case reflect.Map: - info.wire = &wireType{MapT: t.(*mapType)} - case reflect.Slice: - // []byte == []uint8 is a special case handled separately - if typ.Elem().Kind() != reflect.Uint8 { - info.wire = &wireType{SliceT: t.(*sliceType)} - } - case reflect.Struct: - info.wire = &wireType{StructT: t.(*structType)} - } - typeInfoMap[rt] = info - return info, nil -} - -// Called only when a panic is acceptable and unexpected. -func mustGetTypeInfo(rt reflect.Type) *typeInfo { - t, err := getTypeInfo(userType(rt)) - if err != nil { - panic("getTypeInfo: " + err.String()) - } - return t -} - -// GobEncoder is the interface describing data that provides its own -// representation for encoding values for transmission to a GobDecoder. -// A type that implements GobEncoder and GobDecoder has complete -// control over the representation of its data and may therefore -// contain things such as private fields, channels, and functions, -// which are not usually transmissable in gob streams. -// -// Note: Since gobs can be stored permanently, It is good design -// to guarantee the encoding used by a GobEncoder is stable as the -// software evolves. For instance, it might make sense for GobEncode -// to include a version number in the encoding. -type GobEncoder interface { - // GobEncode returns a byte slice representing the encoding of the - // receiver for transmission to a GobDecoder, usually of the same - // concrete type. - GobEncode() ([]byte, os.Error) -} - -// GobDecoder is the interface describing data that provides its own -// routine for decoding transmitted values sent by a GobEncoder. -type GobDecoder interface { - // GobDecode overwrites the receiver, which must be a pointer, - // with the value represented by the byte slice, which was written - // by GobEncode, usually for the same concrete type. - GobDecode([]byte) os.Error -} - -var ( - nameToConcreteType = make(map[string]reflect.Type) - concreteTypeToName = make(map[reflect.Type]string) -) - -// RegisterName is like Register but uses the provided name rather than the -// type's default. -func RegisterName(name string, value interface{}) { - if name == "" { - // reserved for nil - panic("attempt to register empty name") - } - base := userType(reflect.TypeOf(value)).base - // Check for incompatible duplicates. - if t, ok := nameToConcreteType[name]; ok && t != base { - panic("gob: registering duplicate types for " + name) - } - if n, ok := concreteTypeToName[base]; ok && n != name { - panic("gob: registering duplicate names for " + base.String()) - } - // Store the name and type provided by the user.... - nameToConcreteType[name] = reflect.TypeOf(value) - // but the flattened type in the type table, since that's what decode needs. - concreteTypeToName[base] = name -} - -// Register records a type, identified by a value for that type, under its -// internal type name. That name will identify the concrete type of a value -// sent or received as an interface variable. Only types that will be -// transferred as implementations of interface values need to be registered. -// Expecting to be used only during initialization, it panics if the mapping -// between types and names is not a bijection. -func Register(value interface{}) { - // Default to printed representation for unnamed types - rt := reflect.TypeOf(value) - name := rt.String() - - // But for named types (or pointers to them), qualify with import path. - // Dereference one pointer looking for a named type. - star := "" - if rt.Name() == "" { - if pt := rt; pt.Kind() == reflect.Ptr { - star = "*" - rt = pt - } - } - if rt.Name() != "" { - if rt.PkgPath() == "" { - name = star + rt.Name() - } else { - name = star + rt.PkgPath() + "." + rt.Name() - } - } - - RegisterName(name, value) -} - -func registerBasics() { - Register(int(0)) - Register(int8(0)) - Register(int16(0)) - Register(int32(0)) - Register(int64(0)) - Register(uint(0)) - Register(uint8(0)) - Register(uint16(0)) - Register(uint32(0)) - Register(uint64(0)) - Register(float32(0)) - Register(float64(0)) - Register(complex64(0i)) - Register(complex128(0i)) - Register(false) - Register("") - Register([]byte(nil)) -} diff --git a/src/cmd/gc/align.c b/src/cmd/gc/align.c index 80c65387b..8e9677e75 100644 --- a/src/cmd/gc/align.c +++ b/src/cmd/gc/align.c @@ -119,7 +119,8 @@ dowidth(Type *t) if(t->width == -2) { lno = lineno; lineno = t->lineno; - yyerror("invalid recursive type %T", t); + if(!t->broke) + yyerror("invalid recursive type %T", t); t->width = 0; lineno = lno; return; @@ -219,7 +220,8 @@ dowidth(Type *t) checkwidth(t->down); break; case TFORW: // should have been filled in - yyerror("invalid recursive type %T", t); + if(!t->broke) + yyerror("invalid recursive type %T", t); w = 1; // anything will do break; case TANY: diff --git a/src/cmd/gc/bisonerrors b/src/cmd/gc/bisonerrors index 8886a8e52..1f97fc8ce 100755 --- a/src/cmd/gc/bisonerrors +++ b/src/cmd/gc/bisonerrors @@ -35,6 +35,9 @@ grammar && NF>0 { } rulelhs[$1] = r rulesize[$1] = NF-2 + if(rulesize[$1] == 1 && $3 == "%empty") { + rulesize[$1] = 0 + } if(rulesize[$1] == 3 && $3 $4 $5 == "/*empty*/") { rulesize[$1] = 0 } diff --git a/src/cmd/gc/builtin.c b/src/cmd/gc/builtin.c index 9053dfe10..309dc1ea0 100644 --- a/src/cmd/gc/builtin.c +++ b/src/cmd/gc/builtin.c @@ -24,9 +24,6 @@ char *runtimeimport = "func @\"\".printsp ()\n" "func @\"\".goprintf ()\n" "func @\"\".concatstring ()\n" - "func @\"\".append ()\n" - "func @\"\".appendslice (@\"\".typ·2 *byte, @\"\".x·3 any, @\"\".y·4 []any) (? any)\n" - "func @\"\".appendstr (@\"\".typ·2 *byte, @\"\".x·3 []byte, @\"\".y·4 string) (? []byte)\n" "func @\"\".cmpstring (? string, ? string) (? int)\n" "func @\"\".eqstring (? string, ? string) (? bool)\n" "func @\"\".intstring (? int64) (? string)\n" @@ -94,6 +91,7 @@ char *runtimeimport = "func @\"\".block ()\n" "func @\"\".makeslice (@\"\".typ·2 *byte, @\"\".nel·3 int64, @\"\".cap·4 int64) (@\"\".ary·1 []any)\n" "func @\"\".growslice (@\"\".typ·2 *byte, @\"\".old·3 []any, @\"\".n·4 int64) (@\"\".ary·1 []any)\n" + "func @\"\".memmove (@\"\".to·1 *any, @\"\".frm·2 *any, @\"\".length·3 uintptr)\n" "func @\"\".memequal (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n" "func @\"\".memequal8 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n" "func @\"\".memequal16 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n" @@ -113,6 +111,8 @@ char *runtimeimport = "func @\"\".racefuncexit ()\n" "func @\"\".raceread (? uintptr)\n" "func @\"\".racewrite (? uintptr)\n" + "func @\"\".racereadrange (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\n" + "func @\"\".racewriterange (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\n" "\n" "$$\n"; char *unsafeimport = diff --git a/src/cmd/gc/bv.c b/src/cmd/gc/bv.c new file mode 100644 index 000000000..92834a97b --- /dev/null +++ b/src/cmd/gc/bv.c @@ -0,0 +1,80 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include <u.h> +#include <libc.h> +#include "go.h" + +enum { + WORDSIZE = sizeof(uint32), + WORDBITS = 32, +}; + +uintptr +bvsize(uintptr n) +{ + return ((n + WORDBITS - 1) / WORDBITS) * WORDSIZE; +} + +Bvec* +bvalloc(int32 n) +{ + Bvec *bv; + uintptr nbytes; + + if(n < 0) + fatal("bvalloc: initial size is negative\n"); + nbytes = sizeof(Bvec) + bvsize(n); + bv = malloc(nbytes); + if(bv == nil) + fatal("bvalloc: malloc failed\n"); + memset(bv, 0, nbytes); + bv->n = n; + return bv; +} + +void +bvset(Bvec *bv, int32 i) +{ + uint32 mask; + + if(i < 0 || i >= bv->n) + fatal("bvset: index %d is out of bounds with length %d\n", i, bv->n); + mask = 1U << (i % WORDBITS); + bv->b[i / WORDBITS] |= mask; +} + +void +bvres(Bvec *bv, int32 i) +{ + uint32 mask; + + if(i < 0 || i >= bv->n) + fatal("bvres: index %d is out of bounds with length %d\n", i, bv->n); + mask = ~(1 << (i % WORDBITS)); + bv->b[i / WORDBITS] &= mask; +} + +int +bvget(Bvec *bv, int32 i) +{ + uint32 mask, word; + + if(i < 0 || i >= bv->n) + fatal("bvget: index %d is out of bounds with length %d\n", i, bv->n); + mask = 1 << (i % WORDBITS); + word = bv->b[i / WORDBITS] & mask; + return word ? 1 : 0; +} + +int +bvisempty(Bvec *bv) +{ + int32 i; + + for(i = 0; i < bv->n; i += WORDBITS) + if(bv->b[i / WORDBITS] != 0) + return 0; + return 1; +} diff --git a/src/cmd/gc/closure.c b/src/cmd/gc/closure.c index 996504a11..5a84dfb1b 100644 --- a/src/cmd/gc/closure.c +++ b/src/cmd/gc/closure.c @@ -285,6 +285,8 @@ makepartialcall(Node *fn, Type *t0, Node *meth) NodeList *body, *l, *callargs, *retargs; char *p; Sym *sym; + Pkg *spkg; + static Pkg* gopkg; int i, ddd; // TODO: names are not right @@ -296,10 +298,18 @@ makepartialcall(Node *fn, Type *t0, Node *meth) basetype = rcvrtype; if(isptr[rcvrtype->etype]) basetype = basetype->type; - if(basetype->sym == S) + if(basetype->etype != TINTER && basetype->sym == S) fatal("missing base type for %T", rcvrtype); - sym = pkglookup(p, basetype->sym->pkg); + spkg = nil; + if(basetype->sym != S) + spkg = basetype->sym->pkg; + if(spkg == nil) { + if(gopkg == nil) + gopkg = mkpkg(strlit("go")); + spkg = gopkg; + } + sym = pkglookup(p, spkg); free(p); if(sym->flags & SymUniq) return sym->def; @@ -407,8 +417,10 @@ walkpartialcall(Node *n, NodeList **init) // Like walkclosure above. if(isinter(n->left->type)) { + // Trigger panic for method on nil interface now. + // Otherwise it happens in the wrapper and is confusing. n->left = cheapexpr(n->left, init); - checknotnil(n->left, init); + checknil(n->left, init); } typ = nod(OTSTRUCT, N, N); diff --git a/src/cmd/gc/const.c b/src/cmd/gc/const.c index e9d99df18..cfb1f0ade 100644 --- a/src/cmd/gc/const.c +++ b/src/cmd/gc/const.c @@ -1056,7 +1056,7 @@ nodcplxlit(Val r, Val i) } // idealkind returns a constant kind like consttype -// but for an arbitrary "ideal" expression. +// but for an arbitrary "ideal" (untyped constant) expression. static int idealkind(Node *n) { diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c index d3759efde..c7d13ef06 100644 --- a/src/cmd/gc/dcl.c +++ b/src/cmd/gc/dcl.c @@ -141,6 +141,8 @@ testdclstack(void) for(d=dclstack; d!=S; d=d->link) { if(d->name == nil) { + if(nerrors != 0) + errorexit(); yyerror("mark left on the stack"); continue; } @@ -287,7 +289,7 @@ variter(NodeList *vl, Node *t, NodeList *el) for(; vl; vl=vl->next) { if(doexpr) { if(el == nil) { - yyerror("missing expr in var dcl"); + yyerror("missing expression in var declaration"); break; } e = el->n; @@ -310,7 +312,7 @@ variter(NodeList *vl, Node *t, NodeList *el) } } if(el != nil) - yyerror("extra expr in var dcl"); + yyerror("extra expression in var declaration"); return init; } @@ -327,7 +329,7 @@ constiter(NodeList *vl, Node *t, NodeList *cl) vv = nil; if(cl == nil) { if(t != N) - yyerror("constdcl cannot have type without expr"); + yyerror("const declaration cannot have type without expression"); cl = lastconst; t = lasttype; } else { @@ -338,7 +340,7 @@ constiter(NodeList *vl, Node *t, NodeList *cl) for(; vl; vl=vl->next) { if(cl == nil) { - yyerror("missing expr in const dcl"); + yyerror("missing value in const declaration"); break; } c = cl->n; @@ -354,7 +356,7 @@ constiter(NodeList *vl, Node *t, NodeList *cl) vv = list(vv, nod(ODCLCONST, v, N)); } if(cl != nil) - yyerror("extra expr in const dcl"); + yyerror("extra expression in const declaration"); iota += 1; return vv; } @@ -1019,7 +1021,7 @@ tointerface(NodeList *l) } Node* -embedded(Sym *s) +embedded(Sym *s, Pkg *pkg) { Node *n; char *name; @@ -1036,9 +1038,9 @@ embedded(Sym *s) if(exportname(name)) n = newname(lookup(name)); - else if(s->pkg == builtinpkg && importpkg != nil) - // The name of embedded builtins during imports belongs to importpkg. - n = newname(pkglookup(name, importpkg)); + else if(s->pkg == builtinpkg) + // The name of embedded builtins belongs to pkg. + n = newname(pkglookup(name, pkg)); else n = newname(pkglookup(name, s->pkg)); n = nod(ODCLFIELD, n, oldname(s)); diff --git a/src/cmd/gc/esc.c b/src/cmd/gc/esc.c index df273e392..b84b66ef1 100644 --- a/src/cmd/gc/esc.c +++ b/src/cmd/gc/esc.c @@ -144,7 +144,7 @@ visitcode(Node *n, uint32 min) fn = n->left; if(n->op == OCALLMETH) fn = n->left->right->sym->def; - if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn && fn->defn->nbody) + if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn) if((m = visit(fn->defn)) < min) min = m; } @@ -749,6 +749,8 @@ escassign(EscState *e, Node *dst, Node *src) case ODOTTYPE2: case OSLICE: case OSLICEARR: + case OSLICE3: + case OSLICE3ARR: // Conversions, field access, slice all preserve the input value. escassign(e, dst, src->left); break; diff --git a/src/cmd/gc/export.c b/src/cmd/gc/export.c index caac330d5..31bcdf8e7 100644 --- a/src/cmd/gc/export.c +++ b/src/cmd/gc/export.c @@ -44,7 +44,7 @@ initname(char *s) return strcmp(s, "init") == 0; } -// exportedsym returns whether a symbol will be visible +// exportedsym reports whether a symbol will be visible // to files that import our package. static int exportedsym(Sym *sym) @@ -481,9 +481,10 @@ importvar(Sym *s, Type *t) if(s->def != N && s->def->op == ONAME) { if(eqtype(t, s->def->type)) return; - yyerror("inconsistent definition for var %S during import\n\t%T\n\t%T", s, s->def->type, t); + yyerror("inconsistent definition for var %S during import\n\t%T (in \"%Z\")\n\t%T (in \"%Z\")", s, s->def->type, s->importdef->path, t, importpkg->path); } n = newname(s); + s->importdef = importpkg; n->type = t; declare(n, PEXTERN); @@ -509,11 +510,12 @@ importtype(Type *pt, Type *t) n = pt->nod; copytype(pt->nod, t); pt->nod = n; // unzero nod + pt->sym->importdef = importpkg; pt->sym->lastlineno = parserline(); declare(n, PEXTERN); checkwidth(pt); } else if(!eqtype(pt->orig, t)) - yyerror("inconsistent definition for type %S during import\n\t%lT\n\t%lT", pt->sym, pt, t); + yyerror("inconsistent definition for type %S during import\n\t%lT (in \"%Z\")\n\t%lT (in \"%Z\")", pt->sym, pt, pt->sym->importdef->path, t, importpkg->path); if(debug['E']) print("import type %T %lT\n", pt, t); diff --git a/src/cmd/gc/fmt.c b/src/cmd/gc/fmt.c index b5f6a9b8f..9cd344870 100644 --- a/src/cmd/gc/fmt.c +++ b/src/cmd/gc/fmt.c @@ -415,6 +415,8 @@ Zconv(Fmt *fp) s = sp->s; se = s + sp->len; + + // NOTE: Keep in sync with ../ld/go.c:/^Zconv. while(s < se) { n = chartorune(&r, s); s += n; @@ -575,7 +577,7 @@ basicnames[] = [TANY] = "any", [TSTRING] = "string", [TNIL] = "nil", - [TIDEAL] = "ideal", + [TIDEAL] = "untyped number", [TBLANK] = "blank", }; @@ -602,8 +604,11 @@ typefmt(Fmt *fp, Type *t) if(!(fp->flags&FmtLong) && t->sym && t->etype != TFIELD && t != types[t->etype]) { switch(fmtmode) { case FTypeId: - if(fp->flags&FmtShort) + if(fp->flags&FmtShort) { + if(t->vargen) + return fmtprint(fp, "%hS·%d", t->sym, t->vargen); return fmtprint(fp, "%hS", t->sym); + } if(fp->flags&FmtUnsigned) return fmtprint(fp, "%uS", t->sym); // fallthrough @@ -617,7 +622,7 @@ typefmt(Fmt *fp, Type *t) if(t->etype < nelem(basicnames) && basicnames[t->etype] != nil) { if(fmtmode == FErr && (t == idealbool || t == idealstring)) - fmtstrcpy(fp, "ideal "); + fmtstrcpy(fp, "untyped "); return fmtstrcpy(fp, basicnames[t->etype]); } @@ -695,6 +700,13 @@ typefmt(Fmt *fp, Type *t) return 0; case TSTRUCT: + // Format the bucket struct for map[x]y as map.bucket[x]y. + // This avoids a recursive print that generates very long names. + if(t->hmap != T) { + t = t->hmap; + return fmtprint(fp, "map.bucket[%T]%T", t->down, t->type); + } + if(t->funarg) { fmtstrcpy(fp, "("); if(fmtmode == FTypeId || fmtmode == FErr) { // no argument names on function signature, and no "noescape" tags @@ -749,6 +761,9 @@ typefmt(Fmt *fp, Type *t) //if(t->funarg) // fmtstrcpy(fp, "_ "); //else + if(t->embedded && s->pkg != nil && s->pkg->path->len > 0) + fmtprint(fp, "@\"%Z\".? ", s->pkg->path); + else fmtstrcpy(fp, "? "); } } @@ -870,6 +885,10 @@ stmtfmt(Fmt *f, Node *n) fmtprint(f, "return %,H", n->list); break; + case ORETJMP: + fmtprint(f, "retjmp %S", n->sym); + break; + case OPROC: fmtprint(f, "go %N", n->left); break; @@ -1018,6 +1037,8 @@ static int opprec[] = { [OSLICE] = 8, [OSLICESTR] = 8, [OSLICEARR] = 8, + [OSLICE3] = 8, + [OSLICE3ARR] = 8, [ODOTINTER] = 8, [ODOTMETH] = 8, [ODOTPTR] = 8, @@ -1291,6 +1312,8 @@ exprfmt(Fmt *f, Node *n, int prec) case OSLICE: case OSLICESTR: case OSLICEARR: + case OSLICE3: + case OSLICE3ARR: exprfmt(f, n->left, nprec); return fmtprint(f, "[%N]", n->right); diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c index 955ec2c5b..ada16eacc 100644 --- a/src/cmd/gc/gen.c +++ b/src/cmd/gc/gen.c @@ -489,11 +489,12 @@ gen(Node *n) break; case ORETURN: + case ORETJMP: cgen_ret(n); break; - case OCHECKNOTNIL: - checkref(n->left, 1); + case OCHECKNIL: + cgen_checknil(n->left); } ret: @@ -628,6 +629,10 @@ cgen_discard(Node *nr) case OPLUS: cgen_discard(nr->left); break; + + case OIND: + cgen_checknil(nr->left); + break; // special enough to just evaluate default: @@ -776,6 +781,8 @@ cgen_eface(Node *n, Node *res) * n->left is s * n->list is (cap(s)-lo(TUINT), hi-lo(TUINT)[, lo*width(TUINTPTR)]) * caller (cgen) guarantees res is an addable ONAME. + * + * called for OSLICE, OSLICE3, OSLICEARR, OSLICE3ARR, OSLICESTR. */ void cgen_slice(Node *n, Node *res) @@ -807,21 +814,26 @@ cgen_slice(Node *n, Node *res) dst.xoffset += Array_array; dst.type = types[TUINTPTR]; - if(n->op == OSLICEARR) { - if(!isptr[n->left->type->etype]) - fatal("slicearr is supposed to work on pointer: %+N\n", n); - checkref(n->left, 0); - } - if(isnil(n->left)) { tempname(&src, n->left->type); cgen(n->left, &src); } else src = *n->left; - src.xoffset += Array_array; + if(n->op == OSLICE || n->op == OSLICE3 || n->op == OSLICESTR) + src.xoffset += Array_array; src.type = types[TUINTPTR]; - if(offs == N) { + if(n->op == OSLICEARR || n->op == OSLICE3ARR) { + if(!isptr[n->left->type->etype]) + fatal("slicearr is supposed to work on pointer: %+N\n", n); + cgen(&src, &dst); + cgen_checknil(&dst); + if(offs != N) { + add = nod(OADD, &dst, offs); + typecheck(&add, Erv); + cgen(add, &dst); + } + } else if(offs == N) { cgen(&src, &dst); } else { add = nod(OADD, &src, offs); diff --git a/src/cmd/gc/go.errors b/src/cmd/gc/go.errors index 68a5e5af3..f90d61990 100644 --- a/src/cmd/gc/go.errors +++ b/src/cmd/gc/go.errors @@ -17,17 +17,20 @@ static struct { % loadsys package LIMPORT '(' LLITERAL import_package import_there ',' "unexpected comma during import block", + % loadsys package LIMPORT LNAME ';' + "missing import path; require quoted string", + % loadsys package imports LFUNC LNAME '(' ')' '{' LIF if_header ';' - "unexpected semicolon or newline before {", + "missing { after if clause", % loadsys package imports LFUNC LNAME '(' ')' '{' LSWITCH if_header ';' - "unexpected semicolon or newline before {", + "missing { after switch clause", % loadsys package imports LFUNC LNAME '(' ')' '{' LFOR for_header ';' - "unexpected semicolon or newline before {", + "missing { after for clause", % loadsys package imports LFUNC LNAME '(' ')' '{' LFOR ';' LBODY - "unexpected semicolon or newline before {", + "missing { after for clause", % loadsys package imports LFUNC LNAME '(' ')' ';' '{' "unexpected semicolon or newline before {", diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index e94eb90ee..562f16890 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -63,6 +63,8 @@ enum ACPLX128, BADWIDTH = -1000000000, + + MaxStackVarSize = 10*1024*1024, }; extern vlong MAXWIDTH; @@ -76,7 +78,7 @@ typedef struct Strlit Strlit; struct Strlit { int32 len; - char s[3]; // variable + char s[1]; // variable }; enum @@ -127,6 +129,7 @@ struct Val } u; }; +typedef struct Bvec Bvec; typedef struct Pkg Pkg; typedef struct Sym Sym; typedef struct Node Node; @@ -138,6 +141,7 @@ struct Type { uchar etype; uchar nointerface; + uchar noalg; uchar chan; uchar trecur; // to detect loops uchar printed; @@ -150,6 +154,7 @@ struct Type uchar broke; // broken type definition. uchar isddd; // TFIELD is ... argument uchar align; + uchar haspointers; // 0 unknown, 1 no, 2 yes Node* nod; // canonical OTYPE node Type* orig; // original type (type literal or predefined type) @@ -182,14 +187,17 @@ struct Type // TARRAY vlong bound; // negative is dynamic array + // TMAP + Type* bucket; // internal type representing a hash bucket + Type* hmap; // internal type representing a Hmap (map header object) + int32 maplineno; // first use of TFORW as map key int32 embedlineno; // first use of TFORW as embedded type // for TFORW, where to copy the eventual value to NodeList *copyto; - // for usefield - Node *lastfn; + Node *lastfn; // for usefield }; #define T ((Type*)0) @@ -265,8 +273,10 @@ struct Node uchar implicit; uchar addrtaken; // address taken, even if not moved to heap uchar dupok; // duplicate definitions ok (for func) + uchar wrapper; // is method wrapper (for func) schar likely; // likeliness of if statement uchar hasbreak; // has break statement + uchar needzero; // if it contains pointers, needs to be zeroed on function entry uint esc; // EscXXX int funcdepth; @@ -327,6 +337,7 @@ struct Node int32 iota; uint32 walkgen; int32 esclevel; + void* opt; // for optimization passes }; #define N ((Node*)0) @@ -371,6 +382,7 @@ struct Sym Sym* link; int32 npkg; // number of imported packages with this name uint32 uniqgen; + Pkg* importdef; // where imported definition was found // saved and restored by dcopy Pkg* pkg; @@ -519,6 +531,8 @@ enum OSLICE, // v[1:2], typechecking may convert to a more specfic OSLICEXXX. OSLICEARR, // a[1:2] OSLICESTR, // s[1:2] + OSLICE3, // v[1:2:3], typechecking may convert to OSLICE3ARR. + OSLICE3ARR, // a[1:2:3] ORECOVER, // recover ORECV, // <-c ORUNESTR, // string(i) @@ -565,9 +579,10 @@ enum OINLCALL, // intermediary representation of an inlined call. OEFACE, // itable and data words of an empty-interface value. OITAB, // itable word of an interface value. + OSPTR, // base pointer of a slice or string. OCLOSUREVAR, // variable reference at beginning of closure function OCFUNC, // reference to c function pointer (not go func value) - OCHECKNOTNIL, // emit code to ensure pointer/interface not nil + OCHECKNIL, // emit code to ensure pointer/interface not nil // arch-specific registers OREGISTER, // a register, such as AX. @@ -581,6 +596,7 @@ enum OHMUL, // high mul: AMUL/AIMUL for unsigned/signed (OMUL uses AIMUL for both). OLROT, // left rotate: AROL. ORROTC, // right rotate-carry: ARCR. + ORETJMP, // return to other function OEND, }; @@ -697,6 +713,12 @@ struct Bits EXTERN Bits zbits; +struct Bvec +{ + int32 n; // number of bits + uint32 b[]; +}; + typedef struct Var Var; struct Var { @@ -741,6 +763,7 @@ struct Io int32 ilineno; int nlsemi; int eofnl; + int last; int peekc; int peekc1; // second peekc for ... char* cp; // used for content when bin==nil @@ -851,6 +874,8 @@ EXTERN char namebuf[NSYMB]; EXTERN char lexbuf[NSYMB]; EXTERN char litbuf[NSYMB]; EXTERN int debug[256]; +EXTERN char* debugstr; +EXTERN int debug_checknil; EXTERN Sym* hash[NHASH]; EXTERN Sym* importmyname; // my name for package EXTERN Pkg* localpkg; // package being compiled @@ -924,6 +949,8 @@ EXTERN NodeList* lastconst; EXTERN Node* lasttype; EXTERN vlong maxarg; EXTERN vlong stksize; // stack size for current frame +EXTERN vlong stkptrsize; // prefix of stack containing pointers +EXTERN vlong stkzerosize; // prefix of stack that must be zeroed on entry EXTERN int32 blockgen; // max block number EXTERN int32 block; // current block number EXTERN int hasdefer; // flag that curfn has defer statetment @@ -949,12 +976,14 @@ EXTERN int typecheckok; EXTERN int compiling_runtime; EXTERN int compiling_wrappers; EXTERN int pure_go; +EXTERN char* flag_installsuffix; EXTERN int flag_race; EXTERN int flag_largemodel; EXTERN int noescape; EXTERN int nointerface; EXTERN int fieldtrack_enabled; +EXTERN int precisestack_enabled; /* * y.tab.c @@ -987,6 +1016,16 @@ Bits bor(Bits a, Bits b); int bset(Bits a, uint n); /* + * bv.c + */ +Bvec* bvalloc(int32 n); +void bvset(Bvec *bv, int32 i); +void bvres(Bvec *bv, int32 i); +int bvget(Bvec *bv, int32 i); +int bvisempty(Bvec *bv); +int bvcmp(Bvec *bv1, Bvec *bv2); + +/* * closure.c */ Node* closurebody(NodeList *body); @@ -1042,7 +1081,7 @@ NodeList* constiter(NodeList *vl, Node *t, NodeList *cl); Node* dclname(Sym *s); void declare(Node *n, int ctxt); void dumpdcl(char *st); -Node* embedded(Sym *s); +Node* embedded(Sym *s, Pkg *pkg); Node* fakethis(void); void funcbody(Node *n); void funccompile(Node *n, int isclosure); @@ -1420,20 +1459,22 @@ EXTERN Prog* firstpc; EXTERN Prog* retpc; EXTERN Node* nodfp; +EXTERN int disable_checknil; int anyregalloc(void); void betypeinit(void); void bgen(Node *n, int true, int likely, Prog *to); -void checkref(Node *n, int force); -void checknotnil(Node*, NodeList**); +void checknil(Node*, NodeList**); +void expandchecks(Prog*); void cgen(Node*, Node*); void cgen_asop(Node *n); void cgen_call(Node *n, int proc); void cgen_callinter(Node *n, Node *res, int proc); +void cgen_checknil(Node*); void cgen_ret(Node *n); void clearfat(Node *n); void compile(Node*); -void defframe(Prog*); +void defframe(Prog*, Bvec*); int dgostringptr(Sym*, int off, char *str); int dgostrlitptr(Sym*, int off, Strlit*); int dstringptr(Sym *s, int off, char *str); @@ -1445,11 +1486,11 @@ void fixautoused(Prog*); void gdata(Node*, Node*, int); void gdatacomplex(Node*, Mpcplx*); void gdatastring(Node*, Strlit*); -void genembedtramp(Type*, Type*, Sym*, int iface); void ggloblnod(Node *nam); void ggloblsym(Sym *s, int32 width, int dupok, int rodata); Prog* gjmp(Prog*); void gused(Node*); +void movelarge(NodeList*); int isfat(Type*); void markautoused(Prog*); Plist* newplist(void); diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y index 29cd37008..5432eca85 100644 --- a/src/cmd/gc/go.y +++ b/src/cmd/gc/go.y @@ -134,8 +134,7 @@ package: { prevlineno = lineno; yyerror("package statement must be first"); - flusherrors(); - mkpackage("main"); + errorexit(); } | LPACKAGE sym ';' { @@ -195,6 +194,10 @@ import_stmt: importdot(ipkg, pack); break; } + if(strcmp(my->name, "init") == 0) { + yyerror("cannot import package as init - init must be a func"); + break; + } if(my->name[0] == '_' && my->name[1] == '\0') break; if(my->def) { @@ -950,6 +953,14 @@ pexpr_no_paren: { $$ = nod(OSLICE, $1, nod(OKEY, $3, $5)); } +| pexpr '[' oexpr ':' oexpr ':' oexpr ']' + { + if($5 == N) + yyerror("middle index required in 3-index slice"); + if($7 == N) + yyerror("final index required in 3-index slice"); + $$ = nod(OSLICE3, $1, nod(OKEY, $3, nod(OKEY, $5, $7))); + } | pseudocall | convtype '(' expr ocomma ')' { @@ -1115,6 +1126,19 @@ hidden_importsym: } $$ = pkglookup($4->name, p); } +| '@' LLITERAL '.' '?' + { + Pkg *p; + + if($2.u.sval->len == 0) + p = importpkg; + else { + if(isbadimport($2.u.sval)) + errorexit(); + p = mkpkg($2.u.sval); + } + $$ = pkglookup("?", p); + } name: sym %prec NotParen @@ -1520,12 +1544,12 @@ structdcl: Node *n; l = $1; - if(l != nil && l->next == nil && l->n == nil) { - // ? symbol, during import + if(l == nil) { + // ? symbol, during import (list1(N) == nil) n = $2; if(n->op == OIND) n = n->left; - n = embedded(n->sym); + n = embedded(n->sym, importpkg); n->right = $2; n->val = $3; $$ = list1(n); @@ -1596,7 +1620,7 @@ packname: embed: packname { - $$ = embedded($1); + $$ = embedded($1, localpkg); } interfacedcl: @@ -2050,15 +2074,19 @@ hidden_structdcl: sym hidden_type oliteral { Sym *s; + Pkg *p; - if($1 != S) { + if($1 != S && strcmp($1->name, "?") != 0) { $$ = nod(ODCLFIELD, newname($1), typenod($2)); $$->val = $3; } else { s = $2->sym; if(s == S && isptr[$2->etype]) s = $2->type->sym; - $$ = embedded(s); + p = importpkg; + if($1 != S) + p = $1->pkg; + $$ = embedded(s, p); $$->right = typenod($2); $$->val = $3; } diff --git a/src/cmd/gc/inl.c b/src/cmd/gc/inl.c index 08b462e13..6800884a0 100644 --- a/src/cmd/gc/inl.c +++ b/src/cmd/gc/inl.c @@ -198,6 +198,7 @@ ishairy(Node *n, int *budget) case ODEFER: case ODCLTYPE: // can't print yet case ODCLCONST: // can't print yet + case ORETJMP: return 1; break; diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c index b7f71d553..8c739391a 100644 --- a/src/cmd/gc/lex.c +++ b/src/cmd/gc/lex.c @@ -41,6 +41,19 @@ static struct { } exper[] = { // {"rune32", &rune32}, {"fieldtrack", &fieldtrack_enabled}, + {"precisestack", &precisestack_enabled}, + {nil, nil}, +}; + +// Debug arguments. +// These can be specified with the -d flag, as in "-d checknil" +// to set the debug_checknil variable. In general the list passed +// to -d can be comma-separated. +static struct { + char *name; + int *val; +} debugtab[] = { + {"nil", &debug_checknil}, {nil, nil}, }; @@ -238,12 +251,13 @@ main(int argc, char *argv[]) flagfn0("V", "print compiler version", doversion); flagcount("W", "debug parse tree after type checking", &debug['W']); flagcount("complete", "compiling complete package (no C or assembly)", &pure_go); - flagcount("d", "debug declarations", &debug['d']); + flagstr("d", "list: print debug information about items in list", &debugstr); flagcount("e", "no limit on number of errors reported", &debug['e']); flagcount("f", "debug stack frames", &debug['f']); flagcount("g", "debug code generation", &debug['g']); flagcount("h", "halt on error", &debug['h']); flagcount("i", "debug line number stack", &debug['i']); + flagstr("installsuffix", "pkg directory suffix", &flag_installsuffix); flagcount("j", "debug runtime-initialized variables", &debug['j']); flagcount("l", "disable inlining", &debug['l']); flagcount("m", "print optimization decisions", &debug['m']); @@ -269,6 +283,24 @@ main(int argc, char *argv[]) racepkg = mkpkg(strlit("runtime/race")); racepkg->name = "race"; } + + // parse -d argument + if(debugstr) { + char *f[100]; + int i, j, nf; + + nf = getfields(debugstr, f, nelem(f), 1, ","); + for(i=0; i<nf; i++) { + for(j=0; debugtab[j].name != nil; j++) { + if(strcmp(debugtab[j].name, f[i]) == 0) { + *debugtab[j].val = 1; + break; + } + } + if(j == nelem(debugtab)) + fatal("unknown debug information -d '%s'\n", f[i]); + } + } // enable inlining. for now: // default: inlining on. (debug['l'] == 1) @@ -329,6 +361,8 @@ main(int argc, char *argv[]) curio.peekc = 0; curio.peekc1 = 0; curio.nlsemi = 0; + curio.eofnl = 0; + curio.last = 0; // Skip initial BOM if present. if(Bgetrune(curio.bin) != BOM) @@ -416,6 +450,10 @@ main(int argc, char *argv[]) // Phase 5: Escape analysis. if(!debug['N']) escapes(xtop); + + // Escape analysis moved escaped values off stack. + // Move large values off stack too. + movelarge(xtop); // Phase 6: Compile top level functions. for(l=xtop; l; l=l->next) @@ -540,7 +578,7 @@ static int findpkg(Strlit *name) { Idir *p; - char *q, *race; + char *q, *suffix, *suffixsep; if(islocalname(name)) { if(safemode) @@ -578,13 +616,19 @@ findpkg(Strlit *name) return 1; } if(goroot != nil) { - race = ""; - if(flag_race) - race = "_race"; - snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s%s/%Z.a", goroot, goos, goarch, race, name); + suffix = ""; + suffixsep = ""; + if(flag_installsuffix != nil) { + suffixsep = "_"; + suffix = flag_installsuffix; + } else if(flag_race) { + suffixsep = "_"; + suffix = "race"; + } + snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s%s%s/%Z.a", goroot, goos, goarch, suffixsep, suffix, name); if(access(namebuf, 0) >= 0) return 1; - snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s%s/%Z.%c", goroot, goos, goarch, race, name, thechar); + snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s%s%s/%Z.%c", goroot, goos, goarch, suffixsep, suffix, name, thechar); if(access(namebuf, 0) >= 0) return 1; } @@ -1580,10 +1624,10 @@ getc(void) curio.cp++; } else { loop: - c = Bgetc(curio.bin); + c = BGETC(curio.bin); if(c == 0xef) { - c1 = Bgetc(curio.bin); - c2 = Bgetc(curio.bin); + c1 = BGETC(curio.bin); + c2 = BGETC(curio.bin); if(c1 == 0xbb && c2 == 0xbf) { yyerrorl(lexlineno, "Unicode (UTF-8) BOM in middle of file"); goto loop; @@ -1602,7 +1646,7 @@ check: } case EOF: // insert \n at EOF - if(curio.eofnl) + if(curio.eofnl || curio.last == '\n') return EOF; curio.eofnl = 1; c = '\n'; @@ -1611,6 +1655,7 @@ check: lexlineno++; break; } + curio.last = c; return c; } @@ -1971,27 +2016,27 @@ lexinit1(void) // error type s = lookup("error"); s->lexical = LNAME; - errortype = t; - errortype->sym = s; s1 = pkglookup("error", builtinpkg); + errortype = t; + errortype->sym = s1; s1->lexical = LNAME; s1->def = typenod(errortype); // byte alias s = lookup("byte"); s->lexical = LNAME; - bytetype = typ(TUINT8); - bytetype->sym = s; s1 = pkglookup("byte", builtinpkg); + bytetype = typ(TUINT8); + bytetype->sym = s1; s1->lexical = LNAME; s1->def = typenod(bytetype); // rune alias s = lookup("rune"); s->lexical = LNAME; - runetype = typ(TINT32); - runetype->sym = s; s1 = pkglookup("rune", builtinpkg); + runetype = typ(TINT32); + runetype->sym = s1; s1->lexical = LNAME; s1->def = typenod(runetype); } @@ -2242,6 +2287,28 @@ yytinit(void) } } +static void +pkgnotused(int lineno, Strlit *path, char *name) +{ + char *elem; + + // If the package was imported with a name other than the final + // import path element, show it explicitly in the error message. + // Note that this handles both renamed imports and imports of + // packages containing unconventional package declarations. + // Note that this uses / always, even on Windows, because Go import + // paths always use forward slashes. + elem = strrchr(path->s, '/'); + if(elem != nil) + elem++; + else + elem = path->s; + if(name == nil || strcmp(elem, name) == 0) + yyerrorl(lineno, "imported and not used: \"%Z\"", path); + else + yyerrorl(lineno, "imported and not used: \"%Z\" as %s", path, name); +} + void mkpackage(char* pkgname) { @@ -2267,7 +2334,7 @@ mkpackage(char* pkgname) // errors if a conflicting top-level name is // introduced by a different file. if(!s->def->used && !nsyntaxerrors) - yyerrorl(s->def->lineno, "imported and not used: \"%Z\"", s->def->pkg->path); + pkgnotused(s->def->lineno, s->def->pkg->path, s->name); s->def = N; continue; } @@ -2275,7 +2342,7 @@ mkpackage(char* pkgname) // throw away top-level name left over // from previous import . "x" if(s->def->pack != N && !s->def->pack->used && !nsyntaxerrors) { - yyerrorl(s->def->pack->lineno, "imported and not used: \"%Z\"", s->def->pack->pkg->path); + pkgnotused(s->def->pack->lineno, s->def->pack->pkg->path, nil); s->def->pack->used = 1; } s->def = N; diff --git a/src/cmd/gc/md5.c b/src/cmd/gc/md5.c index 5856aab51..bbd4e298f 100644 --- a/src/cmd/gc/md5.c +++ b/src/cmd/gc/md5.c @@ -196,7 +196,7 @@ md5block(MD5 *dig, uchar *p, int nn) for(i=0; i<16; i++) { j = i*4; - X[i] = p[j] | (p[j+1]<<8) | (p[j+2]<<16) | (p[j+3]<<24); + X[i] = p[j] | (p[j+1]<<8) | (p[j+2]<<16) | ((uint32)p[j+3]<<24); } // Round 1. diff --git a/src/cmd/gc/mkbuiltin1.c b/src/cmd/gc/mkbuiltin1.c index f8f61c278..69027fdf5 100644 --- a/src/cmd/gc/mkbuiltin1.c +++ b/src/cmd/gc/mkbuiltin1.c @@ -62,7 +62,7 @@ begin: // sys.go claims to be in package PACKAGE to avoid // conflicts during "6g sys.go". rename PACKAGE to $2. printf("\t\""); - while(q = strstr(p, "PACKAGE")) { + while((q = strstr(p, "PACKAGE")) != NULL) { *q = 0; esc(p); // up to the substitution printf("%s", name); // the sub name diff --git a/src/cmd/gc/mparith2.c b/src/cmd/gc/mparith2.c index 8e52ff216..9b2f664f7 100644 --- a/src/cmd/gc/mparith2.c +++ b/src/cmd/gc/mparith2.c @@ -565,11 +565,11 @@ mpgetfix(Mpint *a) return 0; } - v = (vlong)a->a[0]; - v |= (vlong)a->a[1] << Mpscale; - v |= (vlong)a->a[2] << (Mpscale+Mpscale); + v = (uvlong)a->a[0]; + v |= (uvlong)a->a[1] << Mpscale; + v |= (uvlong)a->a[2] << (Mpscale+Mpscale); if(a->neg) - v = -v; + v = -(uvlong)v; return v; } @@ -586,7 +586,7 @@ mpmovecfix(Mpint *a, vlong c) x = c; if(x < 0) { a->neg = 1; - x = -x; + x = -(uvlong)x; } a1 = &a->a[0]; diff --git a/src/cmd/gc/obj.c b/src/cmd/gc/obj.c index e4bcd1170..4cad08924 100644 --- a/src/cmd/gc/obj.c +++ b/src/cmd/gc/obj.c @@ -87,7 +87,7 @@ void Bputname(Biobuf *b, Sym *s) { Bprint(b, "%s", s->pkg->prefix); - Bputc(b, '.'); + BPUTC(b, '.'); Bwrite(b, s->name, strlen(s->name)+1); } @@ -124,7 +124,7 @@ outwinname(Biobuf *b, Hist *h, char *ds, char *p) outzfile(b, p+1); } else { // relative name - if(h->offset == 0 && pathname && pathname[1] == ':') { + if(h->offset >= 0 && pathname && pathname[1] == ':') { if(tolowerrune(ds[0]) == tolowerrune(pathname[0])) { // using current drive zfile(b, pathname, 3); // leading "c:/" @@ -235,7 +235,7 @@ ieeedtod(uint64 *ieee, double native) return; } fr = frexp(native, &exp); - f = 2097152L; /* shouldnt use fp constants here */ + f = 2097152L; /* shouldn't use fp constants here */ fr = modf(fr*f, &ho); h = ho; h &= 0xfffffL; diff --git a/src/cmd/gc/order.c b/src/cmd/gc/order.c index 499a4e746..7552510e9 100644 --- a/src/cmd/gc/order.c +++ b/src/cmd/gc/order.c @@ -218,6 +218,7 @@ orderstmt(Node *n, NodeList **out) case_OFALL: case OGOTO: case OLABEL: + case ORETJMP: // Special: n->left is not an expression; save as is. *out = list(*out, n); break; @@ -263,7 +264,7 @@ orderstmt(Node *n, NodeList **out) ordercallargs(&n->list, out); *out = list(*out, n); break; - + case OSELECT: for(l=n->list; l; l=l->next) { if(l->n->op != OXCASE) diff --git a/src/cmd/gc/pgen.c b/src/cmd/gc/pgen.c index 82d8186b0..2850af6bb 100644 --- a/src/cmd/gc/pgen.c +++ b/src/cmd/gc/pgen.c @@ -2,24 +2,36 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// "Portable" code generation. +// Compiled separately for 5g, 6g, and 8g, so allowed to use gg.h, opt.h. +// Must code to the intersection of the three back ends. + #include <u.h> #include <libc.h> #include "gg.h" #include "opt.h" +#include "../../pkg/runtime/funcdata.h" + +enum { BitsPerPointer = 2 }; static void allocauto(Prog* p); +static void dumpgcargs(Node*, Sym*); +static Bvec* dumpgclocals(Node*, Sym*); void compile(Node *fn) { + Bvec *bv; Plist *pl; - Node nod1, *n; - Prog *plocals, *ptxt, *p, *p1; + Node nod1, *n, *gcargsnod, *gclocalsnod; + Prog *ptxt, *p, *p1; int32 lno; Type *t; Iter save; vlong oldstksize; NodeList *l; + Sym *gcargssym, *gclocalssym; + static int ngcargs, ngclocals; if(newproc == N) { newproc = sysfunc("newproc"); @@ -83,12 +95,37 @@ compile(Node *fn) nodconst(&nod1, types[TINT32], 0); ptxt = gins(ATEXT, isblank(curfn->nname) ? N : curfn->nname, &nod1); if(fn->dupok) - ptxt->TEXTFLAG = DUPOK; + ptxt->TEXTFLAG |= DUPOK; + if(fn->wrapper) + ptxt->TEXTFLAG |= WRAPPER; + + // Clumsy but important. + // See test/recover.go for test cases and src/pkg/reflect/value.go + // for the actual functions being considered. + if(myimportpath != nil && strcmp(myimportpath, "reflect") == 0) { + if(strcmp(curfn->nname->sym->name, "callReflect") == 0 || strcmp(curfn->nname->sym->name, "callMethod") == 0) + ptxt->TEXTFLAG |= WRAPPER; + } + afunclit(&ptxt->from, curfn->nname); ginit(); - plocals = gins(ALOCALS, N, N); + snprint(namebuf, sizeof namebuf, "gcargs·%d", ngcargs++); + gcargssym = lookup(namebuf); + gcargsnod = newname(gcargssym); + gcargsnod->class = PEXTERN; + + nodconst(&nod1, types[TINT32], FUNCDATA_GCArgs); + gins(AFUNCDATA, &nod1, gcargsnod); + + snprint(namebuf, sizeof(namebuf), "gclocals·%d", ngclocals++); + gclocalssym = lookup(namebuf); + gclocalsnod = newname(gclocalssym); + gclocalsnod->class = PEXTERN; + + nodconst(&nod1, types[TINT32], FUNCDATA_GCLocals); + gins(AFUNCDATA, &nod1, gclocalsnod); for(t=curfn->paramfld; t; t=t->down) gtrack(tracksym(t->type)); @@ -144,22 +181,28 @@ compile(Node *fn) if(!debug['N'] || debug['R'] || debug['P']) { regopt(ptxt); + nilopt(ptxt); } + expandchecks(ptxt); oldstksize = stksize; allocauto(ptxt); - plocals->to.type = D_CONST; - plocals->to.offset = stksize; - if(0) print("allocauto: %lld to %lld\n", oldstksize, (vlong)stksize); setlineno(curfn); - if((int64)stksize+maxarg > (1ULL<<31)) + if((int64)stksize+maxarg > (1ULL<<31)) { yyerror("stack frame too large (>2GB)"); + goto ret; + } + + // Emit garbage collection symbols. + dumpgcargs(fn, gcargssym); + bv = dumpgclocals(curfn, gclocalssym); - defframe(ptxt); + defframe(ptxt, bv); + free(bv); if(0) frame(0); @@ -168,26 +211,211 @@ ret: lineno = lno; } +static void +walktype1(Type *t, vlong *xoffset, Bvec *bv) +{ + vlong fieldoffset, i, o; + Type *t1; + + if(t->align > 0 && (*xoffset % t->align) != 0) + fatal("walktype1: invalid initial alignment, %T", t); + + switch(t->etype) { + case TINT8: + case TUINT8: + case TINT16: + case TUINT16: + case TINT32: + case TUINT32: + case TINT64: + case TUINT64: + case TINT: + case TUINT: + case TUINTPTR: + case TBOOL: + case TFLOAT32: + case TFLOAT64: + case TCOMPLEX64: + case TCOMPLEX128: + *xoffset += t->width; + break; + + case TPTR32: + case TPTR64: + case TUNSAFEPTR: + case TFUNC: + case TCHAN: + case TMAP: + if(*xoffset % widthptr != 0) + fatal("walktype1: invalid alignment, %T", t); + bvset(bv, (*xoffset / widthptr) * BitsPerPointer); + *xoffset += t->width; + break; + + case TSTRING: + // struct { byte *str; intgo len; } + if(*xoffset % widthptr != 0) + fatal("walktype1: invalid alignment, %T", t); + bvset(bv, (*xoffset / widthptr) * BitsPerPointer); + *xoffset += t->width; + break; + + case TINTER: + // struct { Itab* tab; union { void* ptr, uintptr val } data; } + // or, when isnilinter(t)==true: + // struct { Type* type; union { void* ptr, uintptr val } data; } + if(*xoffset % widthptr != 0) + fatal("walktype1: invalid alignment, %T", t); + bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 1); + if(isnilinter(t)) + bvset(bv, ((*xoffset / widthptr) * BitsPerPointer)); + *xoffset += t->width; + break; + + case TARRAY: + // The value of t->bound is -1 for slices types and >0 for + // for fixed array types. All other values are invalid. + if(t->bound < -1) + fatal("walktype1: invalid bound, %T", t); + if(isslice(t)) { + // struct { byte* array; uintgo len; uintgo cap; } + if(*xoffset % widthptr != 0) + fatal("walktype1: invalid TARRAY alignment, %T", t); + bvset(bv, (*xoffset / widthptr) * BitsPerPointer); + *xoffset += t->width; + } else if(!haspointers(t->type)) + *xoffset += t->width; + else + for(i = 0; i < t->bound; ++i) + walktype1(t->type, xoffset, bv); + break; + + case TSTRUCT: + o = 0; + for(t1 = t->type; t1 != T; t1 = t1->down) { + fieldoffset = t1->width; + *xoffset += fieldoffset - o; + walktype1(t1->type, xoffset, bv); + o = fieldoffset + t1->type->width; + } + *xoffset += t->width - o; + break; + + default: + fatal("walktype1: unexpected type, %T", t); + } +} + +static void +walktype(Type *type, Bvec *bv) +{ + vlong xoffset; + + // Start the walk at offset 0. The correct offset will be + // filled in by the first type encountered during the walk. + xoffset = 0; + walktype1(type, &xoffset, bv); +} + +// Compute a bit vector to describe the pointer-containing locations +// in the in and out argument list and dump the bitvector length and +// data to the provided symbol. +static void +dumpgcargs(Node *fn, Sym *sym) +{ + Type *thistype, *inargtype, *outargtype; + Bvec *bv; + int32 i; + int off; + + thistype = getthisx(fn->type); + inargtype = getinargx(fn->type); + outargtype = getoutargx(fn->type); + bv = bvalloc((fn->type->argwid / widthptr) * BitsPerPointer); + if(thistype != nil) + walktype(thistype, bv); + if(inargtype != nil) + walktype(inargtype, bv); + if(outargtype != nil) + walktype(outargtype, bv); + off = duint32(sym, 0, bv->n); + for(i = 0; i < bv->n; i += 32) + off = duint32(sym, off, bv->b[i/32]); + free(bv); + ggloblsym(sym, off, 0, 1); +} -// Sort the list of stack variables. autos after anything else, -// within autos, unused after used, and within used on reverse alignment. -// non-autos sort on offset. +// Compute a bit vector to describe the pointer-containing locations +// in local variables and dump the bitvector length and data out to +// the provided symbol. Return the vector for use and freeing by caller. +static Bvec* +dumpgclocals(Node* fn, Sym *sym) +{ + Bvec *bv; + NodeList *ll; + Node *node; + vlong xoffset; + int32 i; + int off; + + bv = bvalloc((stkptrsize / widthptr) * BitsPerPointer); + for(ll = fn->dcl; ll != nil; ll = ll->next) { + node = ll->n; + if(node->class == PAUTO && node->op == ONAME) { + if(haspointers(node->type)) { + xoffset = node->xoffset + stkptrsize; + walktype1(node->type, &xoffset, bv); + } + } + } + off = duint32(sym, 0, bv->n); + for(i = 0; i < bv->n; i += 32) { + off = duint32(sym, off, bv->b[i/32]); + } + ggloblsym(sym, off, 0, 1); + return bv; +} + +// Sort the list of stack variables. Autos after anything else, +// within autos, unused after used, within used, things with +// pointers first, zeroed things first, and then decreasing size. +// Because autos are laid out in decreasing addresses +// on the stack, pointers first, zeroed things first and decreasing size +// really means, in memory, things with pointers needing zeroing at +// the top of the stack and increasing in size. +// Non-autos sort on offset. static int cmpstackvar(Node *a, Node *b) { + int ap, bp; + if (a->class != b->class) - return (a->class == PAUTO) ? 1 : -1; + return (a->class == PAUTO) ? +1 : -1; if (a->class != PAUTO) { if (a->xoffset < b->xoffset) return -1; if (a->xoffset > b->xoffset) - return 1; + return +1; return 0; } if ((a->used == 0) != (b->used == 0)) return b->used - a->used; - return b->type->align - a->type->align; + ap = haspointers(a->type); + bp = haspointers(b->type); + if(ap != bp) + return bp - ap; + + ap = a->needzero; + bp = b->needzero; + if(ap != bp) + return bp - ap; + + if(a->type->width < b->type->width) + return +1; + if(a->type->width > b->type->width) + return -1; + return 0; } // TODO(lvd) find out where the PAUTO/OLITERAL nodes come from. @@ -198,6 +426,10 @@ allocauto(Prog* ptxt) Node* n; vlong w; + stksize = 0; + stkptrsize = 0; + stkzerosize = 0; + if(curfn->dcl == nil) return; @@ -208,6 +440,13 @@ allocauto(Prog* ptxt) markautoused(ptxt); + if(precisestack_enabled) { + // TODO: Remove when liveness analysis sets needzero instead. + for(ll=curfn->dcl; ll != nil; ll=ll->next) + if(ll->n->class == PAUTO) + ll->n->needzero = 1; // ll->n->addrtaken; + } + listsort(&curfn->dcl, cmpstackvar); // Unused autos are at the end, chop 'em off. @@ -216,7 +455,6 @@ allocauto(Prog* ptxt) if (n->class == PAUTO && n->op == ONAME && !n->used) { // No locals used at all curfn->dcl = nil; - stksize = 0; fixautoused(ptxt); return; } @@ -231,7 +469,6 @@ allocauto(Prog* ptxt) } // Reassign stack offsets of the locals that are still there. - stksize = 0; for(ll = curfn->dcl; ll != nil; ll=ll->next) { n = ll->n; if (n->class != PAUTO || n->op != ONAME) @@ -243,6 +480,11 @@ allocauto(Prog* ptxt) fatal("bad width"); stksize += w; stksize = rnd(stksize, n->type->align); + if(haspointers(n->type)) { + stkptrsize = stksize; + if(n->needzero) + stkzerosize = stksize; + } if(thechar == '5') stksize = rnd(stksize, widthptr); if(stksize >= (1ULL<<31)) { @@ -251,14 +493,62 @@ allocauto(Prog* ptxt) } n->stkdelta = -stksize - n->xoffset; } + stksize = rnd(stksize, widthptr); + stkptrsize = rnd(stkptrsize, widthptr); + stkzerosize = rnd(stkzerosize, widthptr); fixautoused(ptxt); // The debug information needs accurate offsets on the symbols. - for(ll = curfn->dcl ;ll != nil; ll=ll->next) { + for(ll = curfn->dcl; ll != nil; ll=ll->next) { if (ll->n->class != PAUTO || ll->n->op != ONAME) continue; ll->n->xoffset += ll->n->stkdelta; ll->n->stkdelta = 0; } } + +static void movelargefn(Node*); + +void +movelarge(NodeList *l) +{ + for(; l; l=l->next) + if(l->n->op == ODCLFUNC) + movelargefn(l->n); +} + +static void +movelargefn(Node *fn) +{ + NodeList *l; + Node *n; + + for(l=fn->dcl; l != nil; l=l->next) { + n = l->n; + if(n->class == PAUTO && n->type != T && n->type->width > MaxStackVarSize) + addrescapes(n); + } +} + +void +cgen_checknil(Node *n) +{ + Node reg; + + if(disable_checknil) + return; + // Ideally we wouldn't see any TUINTPTR here, but we do. + if(n->type == T || (!isptr[n->type->etype] && n->type->etype != TUINTPTR && n->type->etype != TUNSAFEPTR)) { + dump("checknil", n); + fatal("bad checknil"); + } + if((thechar == '5' && n->op != OREGISTER) || !n->addable) { + regalloc(®, types[tptr], n); + cgen(n, ®); + gins(ACHECKNIL, ®, N); + regfree(®); + return; + } + gins(ACHECKNIL, n, N); +} diff --git a/src/cmd/gc/popt.c b/src/cmd/gc/popt.c new file mode 100644 index 000000000..8d7afa011 --- /dev/null +++ b/src/cmd/gc/popt.c @@ -0,0 +1,948 @@ +// Derived from Inferno utils/6c/reg.c +// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// "Portable" optimizations. +// Compiled separately for 5g, 6g, and 8g, so allowed to use gg.h, opt.h. +// Must code to the intersection of the three back ends. + +#include <u.h> +#include <libc.h> +#include "gg.h" +#include "opt.h" + +// p is a call instruction. Does the call fail to return? +int +noreturn(Prog *p) +{ + Sym *s; + int i; + static Sym* symlist[10]; + + if(symlist[0] == S) { + symlist[0] = pkglookup("panicindex", runtimepkg); + symlist[1] = pkglookup("panicslice", runtimepkg); + symlist[2] = pkglookup("throwinit", runtimepkg); + symlist[3] = pkglookup("panic", runtimepkg); + symlist[4] = pkglookup("panicwrap", runtimepkg); + } + + s = p->to.sym; + if(s == S) + return 0; + for(i=0; symlist[i]!=S; i++) + if(s == symlist[i]) + return 1; + return 0; +} + +// JMP chasing and removal. +// +// The code generator depends on being able to write out jump +// instructions that it can jump to now but fill in later. +// the linker will resolve them nicely, but they make the code +// longer and more difficult to follow during debugging. +// Remove them. + +/* what instruction does a JMP to p eventually land on? */ +static Prog* +chasejmp(Prog *p, int *jmploop) +{ + int n; + + n = 0; + while(p != P && p->as == AJMP && p->to.type == D_BRANCH) { + if(++n > 10) { + *jmploop = 1; + break; + } + p = p->to.u.branch; + } + return p; +} + +/* + * reuse reg pointer for mark/sweep state. + * leave reg==nil at end because alive==nil. + */ +#define alive ((void*)0) +#define dead ((void*)1) + +/* mark all code reachable from firstp as alive */ +static void +mark(Prog *firstp) +{ + Prog *p; + + for(p=firstp; p; p=p->link) { + if(p->opt != dead) + break; + p->opt = alive; + if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch) + mark(p->to.u.branch); + if(p->as == AJMP || p->as == ARET || p->as == AUNDEF) + break; + } +} + +void +fixjmp(Prog *firstp) +{ + int jmploop; + Prog *p, *last; + + if(debug['R'] && debug['v']) + print("\nfixjmp\n"); + + // pass 1: resolve jump to jump, mark all code as dead. + jmploop = 0; + for(p=firstp; p; p=p->link) { + if(debug['R'] && debug['v']) + print("%P\n", p); + if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch && p->to.u.branch->as == AJMP) { + p->to.u.branch = chasejmp(p->to.u.branch, &jmploop); + if(debug['R'] && debug['v']) + print("->%P\n", p); + } + p->opt = dead; + } + if(debug['R'] && debug['v']) + print("\n"); + + // pass 2: mark all reachable code alive + mark(firstp); + + // pass 3: delete dead code (mostly JMPs). + last = nil; + for(p=firstp; p; p=p->link) { + if(p->opt == dead) { + if(p->link == P && p->as == ARET && last && last->as != ARET) { + // This is the final ARET, and the code so far doesn't have one. + // Let it stay. + } else { + if(debug['R'] && debug['v']) + print("del %P\n", p); + continue; + } + } + if(last) + last->link = p; + last = p; + } + last->link = P; + + // pass 4: elide JMP to next instruction. + // only safe if there are no jumps to JMPs anymore. + if(!jmploop) { + last = nil; + for(p=firstp; p; p=p->link) { + if(p->as == AJMP && p->to.type == D_BRANCH && p->to.u.branch == p->link) { + if(debug['R'] && debug['v']) + print("del %P\n", p); + continue; + } + if(last) + last->link = p; + last = p; + } + last->link = P; + } + + if(debug['R'] && debug['v']) { + print("\n"); + for(p=firstp; p; p=p->link) + print("%P\n", p); + print("\n"); + } +} + +#undef alive +#undef dead + +// Control flow analysis. The Flow structures hold predecessor and successor +// information as well as basic loop analysis. +// +// graph = flowstart(firstp, sizeof(Flow)); +// ... use flow graph ... +// flowend(graph); // free graph +// +// Typical uses of the flow graph are to iterate over all the flow-relevant instructions: +// +// for(f = graph->start; f != nil; f = f->link) +// +// or, given an instruction f, to iterate over all the predecessors, which is +// f->p1 and this list: +// +// for(f2 = f->p2; f2 != nil; f2 = f2->p2link) +// +// Often the Flow struct is embedded as the first field inside a larger struct S. +// In that case casts are needed to convert Flow* to S* in many places but the +// idea is the same. Pass sizeof(S) instead of sizeof(Flow) to flowstart. + +Graph* +flowstart(Prog *firstp, int size) +{ + int nf; + Flow *f, *f1, *start, *last; + Graph *graph; + Prog *p; + ProgInfo info; + + // Count and mark instructions to annotate. + nf = 0; + for(p = firstp; p != P; p = p->link) { + p->opt = nil; // should be already, but just in case + proginfo(&info, p); + if(info.flags & Skip) + continue; + p->opt = (void*)1; + nf++; + } + + if(nf == 0) + return nil; + + if(nf >= 20000) { + // fatal("%S is too big (%d instructions)", curfn->nname->sym, nf); + return nil; + } + + // Allocate annotations and assign to instructions. + graph = calloc(sizeof *graph + size*nf, 1); + if(graph == nil) + fatal("out of memory"); + start = (Flow*)(graph+1); + last = nil; + f = start; + for(p = firstp; p != P; p = p->link) { + if(p->opt == nil) + continue; + p->opt = f; + f->prog = p; + if(last) + last->link = f; + last = f; + + f = (Flow*)((uchar*)f + size); + } + + // Fill in pred/succ information. + for(f = start; f != nil; f = f->link) { + p = f->prog; + proginfo(&info, p); + if(!(info.flags & Break)) { + f1 = f->link; + f->s1 = f1; + f1->p1 = f; + } + if(p->to.type == D_BRANCH) { + if(p->to.u.branch == P) + fatal("pnil %P", p); + f1 = p->to.u.branch->opt; + if(f1 == nil) + fatal("fnil %P / %P", p, p->to.u.branch); + if(f1 == f) { + //fatal("self loop %P", p); + continue; + } + f->s2 = f1; + f->p2link = f1->p2; + f1->p2 = f; + } + } + + graph->start = start; + graph->num = nf; + return graph; +} + +void +flowend(Graph *graph) +{ + Flow *f; + + for(f = graph->start; f != nil; f = f->link) + f->prog->opt = nil; + free(graph); +} + +/* + * find looping structure + * + * 1) find reverse postordering + * 2) find approximate dominators, + * the actual dominators if the flow graph is reducible + * otherwise, dominators plus some other non-dominators. + * See Matthew S. Hecht and Jeffrey D. Ullman, + * "Analysis of a Simple Algorithm for Global Data Flow Problems", + * Conf. Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts, + * Oct. 1-3, 1973, pp. 207-217. + * 3) find all nodes with a predecessor dominated by the current node. + * such a node is a loop head. + * recursively, all preds with a greater rpo number are in the loop + */ +static int32 +postorder(Flow *r, Flow **rpo2r, int32 n) +{ + Flow *r1; + + r->rpo = 1; + r1 = r->s1; + if(r1 && !r1->rpo) + n = postorder(r1, rpo2r, n); + r1 = r->s2; + if(r1 && !r1->rpo) + n = postorder(r1, rpo2r, n); + rpo2r[n] = r; + n++; + return n; +} + +static int32 +rpolca(int32 *idom, int32 rpo1, int32 rpo2) +{ + int32 t; + + if(rpo1 == -1) + return rpo2; + while(rpo1 != rpo2){ + if(rpo1 > rpo2){ + t = rpo2; + rpo2 = rpo1; + rpo1 = t; + } + while(rpo1 < rpo2){ + t = idom[rpo2]; + if(t >= rpo2) + fatal("bad idom"); + rpo2 = t; + } + } + return rpo1; +} + +static int +doms(int32 *idom, int32 r, int32 s) +{ + while(s > r) + s = idom[s]; + return s == r; +} + +static int +loophead(int32 *idom, Flow *r) +{ + int32 src; + + src = r->rpo; + if(r->p1 != nil && doms(idom, src, r->p1->rpo)) + return 1; + for(r = r->p2; r != nil; r = r->p2link) + if(doms(idom, src, r->rpo)) + return 1; + return 0; +} + +static void +loopmark(Flow **rpo2r, int32 head, Flow *r) +{ + if(r->rpo < head || r->active == head) + return; + r->active = head; + r->loop += LOOP; + if(r->p1 != nil) + loopmark(rpo2r, head, r->p1); + for(r = r->p2; r != nil; r = r->p2link) + loopmark(rpo2r, head, r); +} + +void +flowrpo(Graph *g) +{ + Flow *r1; + int32 i, d, me, nr, *idom; + Flow **rpo2r; + + free(g->rpo); + g->rpo = calloc(g->num*sizeof g->rpo[0], 1); + idom = calloc(g->num*sizeof idom[0], 1); + if(g->rpo == nil || idom == nil) + fatal("out of memory"); + + for(r1 = g->start; r1 != nil; r1 = r1->link) + r1->active = 0; + + rpo2r = g->rpo; + d = postorder(g->start, rpo2r, 0); + nr = g->num; + if(d > nr) + fatal("too many reg nodes %d %d", d, nr); + nr = d; + for(i = 0; i < nr / 2; i++) { + r1 = rpo2r[i]; + rpo2r[i] = rpo2r[nr - 1 - i]; + rpo2r[nr - 1 - i] = r1; + } + for(i = 0; i < nr; i++) + rpo2r[i]->rpo = i; + + idom[0] = 0; + for(i = 0; i < nr; i++) { + r1 = rpo2r[i]; + me = r1->rpo; + d = -1; + // rpo2r[r->rpo] == r protects against considering dead code, + // which has r->rpo == 0. + if(r1->p1 != nil && rpo2r[r1->p1->rpo] == r1->p1 && r1->p1->rpo < me) + d = r1->p1->rpo; + for(r1 = r1->p2; r1 != nil; r1 = r1->p2link) + if(rpo2r[r1->rpo] == r1 && r1->rpo < me) + d = rpolca(idom, d, r1->rpo); + idom[i] = d; + } + + for(i = 0; i < nr; i++) { + r1 = rpo2r[i]; + r1->loop++; + if(r1->p2 != nil && loophead(idom, r1)) + loopmark(rpo2r, i, r1); + } + free(idom); + + for(r1 = g->start; r1 != nil; r1 = r1->link) + r1->active = 0; +} + +Flow* +uniqp(Flow *r) +{ + Flow *r1; + + r1 = r->p1; + if(r1 == nil) { + r1 = r->p2; + if(r1 == nil || r1->p2link != nil) + return nil; + } else + if(r->p2 != nil) + return nil; + return r1; +} + +Flow* +uniqs(Flow *r) +{ + Flow *r1; + + r1 = r->s1; + if(r1 == nil) { + r1 = r->s2; + if(r1 == nil) + return nil; + } else + if(r->s2 != nil) + return nil; + return r1; +} + +// The compilers assume they can generate temporary variables +// as needed to preserve the right semantics or simplify code +// generation and the back end will still generate good code. +// This results in a large number of ephemeral temporary variables. +// Merge temps with non-overlapping lifetimes and equal types using the +// greedy algorithm in Poletto and Sarkar, "Linear Scan Register Allocation", +// ACM TOPLAS 1999. + +typedef struct TempVar TempVar; +typedef struct TempFlow TempFlow; + +struct TempVar +{ + Node *node; + TempFlow *def; // definition of temp var + TempFlow *use; // use list, chained through TempFlow.uselink + TempVar *freelink; // next free temp in Type.opt list + TempVar *merge; // merge var with this one + uint32 start; // smallest Prog.loc in live range + uint32 end; // largest Prog.loc in live range + uchar addr; // address taken - no accurate end + uchar removed; // removed from program +}; + +struct TempFlow +{ + Flow f; + TempFlow *uselink; +}; + +static int +startcmp(const void *va, const void *vb) +{ + TempVar *a, *b; + + a = *(TempVar**)va; + b = *(TempVar**)vb; + + if(a->start < b->start) + return -1; + if(a->start > b->start) + return +1; + return 0; +} + +// Is n available for merging? +static int +canmerge(Node *n) +{ + return n->class == PAUTO && !n->addrtaken && strncmp(n->sym->name, "autotmp", 7) == 0; +} + +static void mergewalk(TempVar*, TempFlow*, uint32); + +void +mergetemp(Prog *firstp) +{ + int i, j, nvar, ninuse, nfree, nkill; + TempVar *var, *v, *v1, **bystart, **inuse; + TempFlow *r; + NodeList *l, **lp; + Node *n; + Prog *p, *p1; + Type *t; + ProgInfo info, info1; + int32 gen; + Graph *g; + + enum { Debug = 0 }; + + g = flowstart(firstp, sizeof(TempFlow)); + if(g == nil) + return; + + // Build list of all mergeable variables. + nvar = 0; + for(l = curfn->dcl; l != nil; l = l->next) + if(canmerge(l->n)) + nvar++; + + var = calloc(nvar*sizeof var[0], 1); + nvar = 0; + for(l = curfn->dcl; l != nil; l = l->next) { + n = l->n; + if(canmerge(n)) { + v = &var[nvar++]; + n->opt = v; + v->node = n; + } + } + + // Build list of uses. + // We assume that the earliest reference to a temporary is its definition. + // This is not true of variables in general but our temporaries are all + // single-use (that's why we have so many!). + for(r = (TempFlow*)g->start; r != nil; r = (TempFlow*)r->f.link) { + p = r->f.prog; + proginfo(&info, p); + + if(p->from.node != N && p->from.node->opt && p->to.node != N && p->to.node->opt) + fatal("double node %P", p); + if((n = p->from.node) != N && (v = n->opt) != nil || + (n = p->to.node) != N && (v = n->opt) != nil) { + if(v->def == nil) + v->def = r; + r->uselink = v->use; + v->use = r; + if(n == p->from.node && (info.flags & LeftAddr)) + v->addr = 1; + } + } + + if(Debug > 1) + dumpit("before", g->start, 0); + + nkill = 0; + + // Special case. + for(v = var; v < var+nvar; v++) { + if(v->addr) + continue; + // Used in only one instruction, which had better be a write. + if((r = v->use) != nil && r->uselink == nil) { + p = r->f.prog; + proginfo(&info, p); + if(p->to.node == v->node && (info.flags & RightWrite) && !(info.flags & RightRead)) { + p->as = ANOP; + p->to = zprog.to; + v->removed = 1; + if(Debug) + print("drop write-only %S\n", v->node->sym); + } else + fatal("temp used and not set: %P", p); + nkill++; + continue; + } + + // Written in one instruction, read in the next, otherwise unused, + // no jumps to the next instruction. Happens mainly in 386 compiler. + if((r = v->use) != nil && r->f.link == &r->uselink->f && r->uselink->uselink == nil && uniqp(r->f.link) == &r->f) { + p = r->f.prog; + proginfo(&info, p); + p1 = r->f.link->prog; + proginfo(&info1, p1); + enum { + SizeAny = SizeB | SizeW | SizeL | SizeQ | SizeF | SizeD, + }; + if(p->from.node == v->node && p1->to.node == v->node && (info.flags & Move) && + !((info.flags|info1.flags) & (LeftAddr|RightAddr)) && + (info.flags & SizeAny) == (info1.flags & SizeAny)) { + p1->from = p->from; + excise(&r->f); + v->removed = 1; + if(Debug) + print("drop immediate-use %S\n", v->node->sym); + } + nkill++; + continue; + } + } + + // Traverse live range of each variable to set start, end. + // Each flood uses a new value of gen so that we don't have + // to clear all the r->f.active words after each variable. + gen = 0; + for(v = var; v < var+nvar; v++) { + gen++; + for(r = v->use; r != nil; r = r->uselink) + mergewalk(v, r, gen); + } + + // Sort variables by start. + bystart = malloc(nvar*sizeof bystart[0]); + for(i=0; i<nvar; i++) + bystart[i] = &var[i]; + qsort(bystart, nvar, sizeof bystart[0], startcmp); + + // List of in-use variables, sorted by end, so that the ones that + // will last the longest are the earliest ones in the array. + // The tail inuse[nfree:] holds no-longer-used variables. + // In theory we should use a sorted tree so that insertions are + // guaranteed O(log n) and then the loop is guaranteed O(n log n). + // In practice, it doesn't really matter. + inuse = malloc(nvar*sizeof inuse[0]); + ninuse = 0; + nfree = nvar; + for(i=0; i<nvar; i++) { + v = bystart[i]; + if(v->addr || v->removed) + continue; + + // Expire no longer in use. + while(ninuse > 0 && inuse[ninuse-1]->end < v->start) { + v1 = inuse[--ninuse]; + inuse[--nfree] = v1; + } + + // Find old temp to reuse if possible. + t = v->node->type; + for(j=nfree; j<nvar; j++) { + v1 = inuse[j]; + if(eqtype(t, v1->node->type)) { + inuse[j] = inuse[nfree++]; + if(v1->merge) + v->merge = v1->merge; + else + v->merge = v1; + nkill++; + break; + } + } + + // Sort v into inuse. + j = ninuse++; + while(j > 0 && inuse[j-1]->end < v->end) { + inuse[j] = inuse[j-1]; + j--; + } + inuse[j] = v; + } + + if(Debug) { + print("%S [%d - %d]\n", curfn->nname->sym, nvar, nkill); + for(v=var; v<var+nvar; v++) { + print("var %#N %T %d-%d", v->node, v->node->type, v->start, v->end); + if(v->addr) + print(" addr=1"); + if(v->removed) + print(" dead=1"); + if(v->merge) + print(" merge %#N", v->merge->node); + if(v->start == v->end) + print(" %P", v->def->f.prog); + print("\n"); + } + + if(Debug > 1) + dumpit("after", g->start, 0); + } + + // Update node references to use merged temporaries. + for(r = (TempFlow*)g->start; r != nil; r = (TempFlow*)r->f.link) { + p = r->f.prog; + if((n = p->from.node) != N && (v = n->opt) != nil && v->merge != nil) + p->from.node = v->merge->node; + if((n = p->to.node) != N && (v = n->opt) != nil && v->merge != nil) + p->to.node = v->merge->node; + } + + // Delete merged nodes from declaration list. + for(lp = &curfn->dcl; (l = *lp); ) { + curfn->dcl->end = l; + n = l->n; + v = n->opt; + if(v && (v->merge || v->removed)) { + *lp = l->next; + continue; + } + lp = &l->next; + } + + // Clear aux structures. + for(v=var; v<var+nvar; v++) + v->node->opt = nil; + free(var); + free(bystart); + free(inuse); + flowend(g); +} + +static void +mergewalk(TempVar *v, TempFlow *r0, uint32 gen) +{ + Prog *p; + TempFlow *r1, *r, *r2; + + for(r1 = r0; r1 != nil; r1 = (TempFlow*)r1->f.p1) { + if(r1->f.active == gen) + break; + r1->f.active = gen; + p = r1->f.prog; + if(v->end < p->loc) + v->end = p->loc; + if(r1 == v->def) { + v->start = p->loc; + break; + } + } + + for(r = r0; r != r1; r = (TempFlow*)r->f.p1) + for(r2 = (TempFlow*)r->f.p2; r2 != nil; r2 = (TempFlow*)r2->f.p2link) + mergewalk(v, r2, gen); +} + +// Eliminate redundant nil pointer checks. +// +// The code generation pass emits a CHECKNIL for every possibly nil pointer. +// This pass removes a CHECKNIL if every predecessor path has already +// checked this value for nil. +// +// Simple backwards flood from check to definition. +// Run prog loop backward from end of program to beginning to avoid quadratic +// behavior removing a run of checks. +// +// Assume that stack variables with address not taken can be loaded multiple times +// from memory without being rechecked. Other variables need to be checked on +// each load. + +typedef struct NilVar NilVar; +typedef struct NilFlow NilFlow; + +struct NilFlow { + Flow f; + int kill; +}; + +static void nilwalkback(NilFlow *rcheck); +static void nilwalkfwd(NilFlow *rcheck); + +void +nilopt(Prog *firstp) +{ + NilFlow *r; + Prog *p; + Graph *g; + int ncheck, nkill; + + g = flowstart(firstp, sizeof(NilFlow)); + if(g == nil) + return; + + if(debug_checknil > 1 /* || strcmp(curfn->nname->sym->name, "f1") == 0 */) + dumpit("nilopt", g->start, 0); + + ncheck = 0; + nkill = 0; + for(r = (NilFlow*)g->start; r != nil; r = (NilFlow*)r->f.link) { + p = r->f.prog; + if(p->as != ACHECKNIL || !regtyp(&p->from)) + continue; + ncheck++; + if(stackaddr(&p->from)) { + if(debug_checknil && p->lineno > 1) + warnl(p->lineno, "removed nil check of SP address"); + r->kill = 1; + continue; + } + nilwalkfwd(r); + if(r->kill) { + if(debug_checknil && p->lineno > 1) + warnl(p->lineno, "removed nil check before indirect"); + continue; + } + nilwalkback(r); + if(r->kill) { + if(debug_checknil && p->lineno > 1) + warnl(p->lineno, "removed repeated nil check"); + continue; + } + } + + for(r = (NilFlow*)g->start; r != nil; r = (NilFlow*)r->f.link) { + if(r->kill) { + nkill++; + excise(&r->f); + } + } + + flowend(g); + + if(debug_checknil > 1) + print("%S: removed %d of %d nil checks\n", curfn->nname->sym, nkill, ncheck); +} + +static void +nilwalkback(NilFlow *rcheck) +{ + Prog *p; + ProgInfo info; + NilFlow *r; + + for(r = rcheck; r != nil; r = (NilFlow*)uniqp(&r->f)) { + p = r->f.prog; + proginfo(&info, p); + if((info.flags & RightWrite) && sameaddr(&p->to, &rcheck->f.prog->from)) { + // Found initialization of value we're checking for nil. + // without first finding the check, so this one is unchecked. + return; + } + if(r != rcheck && p->as == ACHECKNIL && sameaddr(&p->from, &rcheck->f.prog->from)) { + rcheck->kill = 1; + return; + } + } + + // Here is a more complex version that scans backward across branches. + // It assumes rcheck->kill = 1 has been set on entry, and its job is to find a reason + // to keep the check (setting rcheck->kill = 0). + // It doesn't handle copying of aggregates as well as I would like, + // nor variables with their address taken, + // and it's too subtle to turn on this late in Go 1.2. Perhaps for Go 1.3. + /* + for(r1 = r0; r1 != nil; r1 = (NilFlow*)r1->f.p1) { + if(r1->f.active == gen) + break; + r1->f.active = gen; + p = r1->f.prog; + + // If same check, stop this loop but still check + // alternate predecessors up to this point. + if(r1 != rcheck && p->as == ACHECKNIL && sameaddr(&p->from, &rcheck->f.prog->from)) + break; + + proginfo(&info, p); + if((info.flags & RightWrite) && sameaddr(&p->to, &rcheck->f.prog->from)) { + // Found initialization of value we're checking for nil. + // without first finding the check, so this one is unchecked. + rcheck->kill = 0; + return; + } + + if(r1->f.p1 == nil && r1->f.p2 == nil) { + print("lost pred for %P\n", rcheck->f.prog); + for(r1=r0; r1!=nil; r1=(NilFlow*)r1->f.p1) { + proginfo(&info, r1->f.prog); + print("\t%P %d %d %D %D\n", r1->f.prog, info.flags&RightWrite, sameaddr(&r1->f.prog->to, &rcheck->f.prog->from), &r1->f.prog->to, &rcheck->f.prog->from); + } + fatal("lost pred trail"); + } + } + + for(r = r0; r != r1; r = (NilFlow*)r->f.p1) + for(r2 = (NilFlow*)r->f.p2; r2 != nil; r2 = (NilFlow*)r2->f.p2link) + nilwalkback(rcheck, r2, gen); + */ +} + +static void +nilwalkfwd(NilFlow *rcheck) +{ + NilFlow *r; + Prog *p; + ProgInfo info; + + // If the path down from rcheck dereferences the address + // (possibly with a small offset) before writing to memory + // and before any subsequent checks, it's okay to wait for + // that implicit check. Only consider this basic block to + // avoid problems like: + // _ = *x // should panic + // for {} // no writes but infinite loop may be considered visible + for(r = (NilFlow*)uniqs(&rcheck->f); r != nil; r = (NilFlow*)uniqs(&r->f)) { + p = r->f.prog; + proginfo(&info, p); + + if((info.flags & LeftRead) && smallindir(&p->from, &rcheck->f.prog->from)) { + rcheck->kill = 1; + return; + } + if((info.flags & (RightRead|RightWrite)) && smallindir(&p->to, &rcheck->f.prog->from)) { + rcheck->kill = 1; + return; + } + + // Stop if another nil check happens. + if(p->as == ACHECKNIL) + return; + // Stop if value is lost. + if((info.flags & RightWrite) && sameaddr(&p->to, &rcheck->f.prog->from)) + return; + // Stop if memory write. + if((info.flags & RightWrite) && !regtyp(&p->to)) + return; + } +} diff --git a/src/cmd/gc/popt.h b/src/cmd/gc/popt.h new file mode 100644 index 000000000..8d5dfff1a --- /dev/null +++ b/src/cmd/gc/popt.h @@ -0,0 +1,46 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +typedef struct Flow Flow; +typedef struct Graph Graph; + +struct Flow { + Prog* prog; // actual instruction + Flow* p1; // predecessors of this instruction: p1, + Flow* p2; // and then p2 linked though p2link. + Flow* p2link; + Flow* s1; // successors of this instruction (at most two: s1 and s2). + Flow* s2; + Flow* link; // next instruction in function code + + int32 active; // usable by client + + int32 rpo; // reverse post ordering + uint16 loop; // x5 for every loop + uchar refset; // diagnostic generated +}; + +struct Graph +{ + Flow* start; + int num; + + // After calling flowrpo, rpo lists the flow nodes in reverse postorder, + // and each non-dead Flow node f has g->rpo[f->rpo] == f. + Flow** rpo; +}; + +void fixjmp(Prog*); +Graph* flowstart(Prog*, int); +void flowrpo(Graph*); +void flowend(Graph*); +void mergetemp(Prog*); +void nilopt(Prog*); +int noreturn(Prog*); +int regtyp(Addr*); +int sameaddr(Addr*, Addr*); +int smallindir(Addr*, Addr*); +int stackaddr(Addr*); +Flow* uniqp(Flow*); +Flow* uniqs(Flow*); diff --git a/src/cmd/gc/racewalk.c b/src/cmd/gc/racewalk.c index 5d4f62e76..d6a5b3cce 100644 --- a/src/cmd/gc/racewalk.c +++ b/src/cmd/gc/racewalk.c @@ -23,6 +23,7 @@ static void racewalklist(NodeList *l, NodeList **init); static void racewalknode(Node **np, NodeList **init, int wr, int skip); static int callinstr(Node **n, NodeList **init, int wr, int skip); static Node* uintptraddr(Node *n); +static void makeaddable(Node *n); static Node* basenod(Node *n); static void foreach(Node *n, void(*f)(Node*, void*), void *c); static void hascallspred(Node *n, void *c); @@ -50,6 +51,18 @@ ispkgin(const char **pkgs, int n) return 0; } +static int +isforkfunc(Node *fn) +{ + // Special case for syscall.forkAndExecInChild. + // In the child, this function must not acquire any locks, because + // they might have been locked at the time of the fork. This means + // no rescheduling, no malloc calls, and no new stack segments. + // Race instrumentation does all of the above. + return myimportpath != nil && strcmp(myimportpath, "syscall") == 0 && + strcmp(fn->nname->sym->name, "forkAndExecInChild") == 0; +} + void racewalk(Node *fn) { @@ -57,7 +70,7 @@ racewalk(Node *fn) Node *nodpc; char s[1024]; - if(ispkgin(omit_pkgs, nelem(omit_pkgs))) + if(ispkgin(omit_pkgs, nelem(omit_pkgs)) || isforkfunc(fn)) return; if(!ispkgin(noinst_pkgs, nelem(noinst_pkgs))) { @@ -121,8 +134,20 @@ racewalknode(Node **np, NodeList **init, int wr, int skip) if(debug['w'] > 1) dump("racewalk-before", n); setlineno(n); - if(init == nil || init == &n->ninit) + if(init == nil) fatal("racewalk: bad init list"); + if(init == &n->ninit) { + // If init == &n->ninit and n->ninit is non-nil, + // racewalknode might append it to itself. + // nil it out and handle it separately before putting it back. + l = n->ninit; + n->ninit = nil; + racewalklist(l, nil); + racewalknode(&n, &l, wr, skip); // recurse with nil n->ninit + appendinit(&n, l); + *np = n; + return; + } racewalklist(n->ninit, nil); @@ -213,6 +238,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip) callinstr(&n, init, wr, skip); goto ret; + case OSPTR: case OLEN: case OCAP: racewalknode(&n->left, init, 0, 0); @@ -254,9 +280,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip) // side effects are safe. // n->right may not be executed, // so instrumentation goes to n->right->ninit, not init. - l = nil; - racewalknode(&n->right, &l, wr, 0); - appendinit(&n->right, l); + racewalknode(&n->right, &n->right->ninit, wr, 0); goto ret; case ONAME: @@ -293,6 +317,8 @@ racewalknode(Node **np, NodeList **init, int wr, int skip) case OSLICE: case OSLICEARR: + case OSLICE3: + case OSLICE3ARR: // Seems to only lead to double instrumentation. //racewalknode(&n->left, init, 0, 0); goto ret; @@ -310,10 +336,6 @@ racewalknode(Node **np, NodeList **init, int wr, int skip) racewalknode(&n->left, init, 0, 0); goto ret; - case OTYPESW: - racewalknode(&n->right, init, 0, 0); - goto ret; - // should not appear in AST by now case OSEND: case ORECV: @@ -363,6 +385,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip) case OIF: case OCALLMETH: case ORETURN: + case ORETJMP: case OSWITCH: case OSELECT: case OEMPTY: @@ -376,7 +399,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip) // does not require instrumentation case OPRINT: // don't bother instrumenting it case OPRINTN: // don't bother instrumenting it - case OCHECKNOTNIL: // always followed by a read. + case OCHECKNIL: // always followed by a read. case OPARAM: // it appears only in fn->exit to copy heap params back case OCLOSUREVAR:// immutable pointer to captured variable case ODOTMETH: // either part of CALLMETH or CALLPART (lowered to PTRLIT) @@ -388,18 +411,15 @@ racewalknode(Node **np, NodeList **init, int wr, int skip) case ONONAME: case OLITERAL: case OSLICESTR: // always preceded by bounds checking, avoid double instrumentation. + case OTYPESW: // ignored by code generation, do not instrument. goto ret; } ret: if(n->op != OBLOCK) // OBLOCK is handled above in a special way. racewalklist(n->list, init); - l = nil; - racewalknode(&n->ntest, &l, 0, 0); - n->ninit = concat(n->ninit, l); - l = nil; - racewalknode(&n->nincr, &l, 0, 0); - n->ninit = concat(n->ninit, l); + racewalknode(&n->ntest, &n->ntest->ninit, 0, 0); + racewalknode(&n->nincr, &n->nincr->ninit, 0, 0); racewalklist(n->nbody, nil); racewalklist(n->nelse, nil); racewalklist(n->rlist, nil); @@ -431,8 +451,8 @@ static int callinstr(Node **np, NodeList **init, int wr, int skip) { Node *f, *b, *n; - Type *t, *t1; - int class, res, hascalls; + Type *t; + int class, hascalls; n = *np; //print("callinstr for %+N [ %O ] etype=%E class=%d\n", @@ -443,33 +463,6 @@ callinstr(Node **np, NodeList **init, int wr, int skip) t = n->type; if(isartificial(n)) return 0; - if(t->etype == TSTRUCT) { - // TODO: instrument arrays similarly. - // PARAMs w/o PHEAP are not interesting. - if(n->class == PPARAM || n->class == PPARAMOUT) - return 0; - res = 0; - hascalls = 0; - foreach(n, hascallspred, &hascalls); - if(hascalls) { - n = detachexpr(n, init); - *np = n; - } - for(t1=t->type; t1; t1=t1->down) { - if(t1->sym && strcmp(t1->sym->name, "_")) { - n = treecopy(n); - f = nod(OXDOT, n, newname(t1->sym)); - f->type = t1; - if(f->type->etype == TFIELD) - f->type = f->type->type; - if(callinstr(&f, init, wr, 0)) { - typecheck(&f, Erv); - res = 1; - } - } - } - return res; - } b = basenod(n); // it skips e.g. stores to ... parameter array @@ -489,19 +482,56 @@ callinstr(Node **np, NodeList **init, int wr, int skip) *np = n; } n = treecopy(n); - f = mkcall(wr ? "racewrite" : "raceread", T, init, uintptraddr(n)); + makeaddable(n); + if(t->etype == TSTRUCT || isfixedarray(t)) { + f = mkcall(wr ? "racewriterange" : "racereadrange", T, init, uintptraddr(n), + nodintconst(t->width)); + } else + f = mkcall(wr ? "racewrite" : "raceread", T, init, uintptraddr(n)); *init = list(*init, f); return 1; } return 0; } +// makeaddable returns a node whose memory location is the +// same as n, but which is addressable in the Go language +// sense. +// This is different from functions like cheapexpr that may make +// a copy of their argument. +static void +makeaddable(Node *n) +{ + // The arguments to uintptraddr technically have an address but + // may not be addressable in the Go sense: for example, in the case + // of T(v).Field where T is a struct type and v is + // an addressable value. + switch(n->op) { + case OINDEX: + if(isfixedarray(n->left->type)) + makeaddable(n->left); + break; + case ODOT: + case OXDOT: + // Turn T(v).Field into v.Field + if(n->left->op == OCONVNOP) + n->left = n->left->left; + makeaddable(n->left); + break; + case ODOTPTR: + default: + // nothing to do + break; + } +} + static Node* uintptraddr(Node *n) { Node *r; r = nod(OADDR, n, N); + r->bounded = 1; r = conv(r, types[TUNSAFEPTR]); r = conv(r, types[TUINTPTR]); return r; diff --git a/src/cmd/gc/range.c b/src/cmd/gc/range.c index 8af45b9d2..bd271da38 100644 --- a/src/cmd/gc/range.c +++ b/src/cmd/gc/range.c @@ -129,6 +129,9 @@ walkrange(Node *n) v2 = N; if(n->list->next) v2 = n->list->next->n; + // n->list has no meaning anymore, clear it + // to avoid erroneous processing by racewalk. + n->list = nil; hv2 = N; if(v2 == N && t->etype == TARRAY) { diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c index fc182b03e..0a8aa8d7a 100644 --- a/src/cmd/gc/reflect.c +++ b/src/cmd/gc/reflect.c @@ -101,6 +101,138 @@ lsort(Sig *l, int(*f)(Sig*, Sig*)) return l; } +// Builds a type respresenting a Bucket structure for +// the given map type. This type is not visible to users - +// we include only enough information to generate a correct GC +// program for it. +// Make sure this stays in sync with ../../pkg/runtime/hashmap.c! +enum { + BUCKETSIZE = 8, + MAXKEYSIZE = 128, + MAXVALSIZE = 128, +}; + +static Type* +mapbucket(Type *t) +{ + Type *keytype, *valtype; + Type *bucket; + Type *overflowfield, *keysfield, *valuesfield; + int32 offset; + + if(t->bucket != T) + return t->bucket; + + keytype = t->down; + valtype = t->type; + if(keytype->width > MAXKEYSIZE) + keytype = ptrto(keytype); + if(valtype->width > MAXVALSIZE) + valtype = ptrto(valtype); + + bucket = typ(TSTRUCT); + bucket->noalg = 1; + + // The first field is: uint8 topbits[BUCKETSIZE]. + // We don't need to encode it as GC doesn't care about it. + offset = BUCKETSIZE * 1; + + overflowfield = typ(TFIELD); + overflowfield->type = ptrto(bucket); + overflowfield->width = offset; // "width" is offset in structure + overflowfield->sym = mal(sizeof(Sym)); // not important but needs to be set to give this type a name + overflowfield->sym->name = "overflow"; + offset += widthptr; + + keysfield = typ(TFIELD); + keysfield->type = typ(TARRAY); + keysfield->type->type = keytype; + keysfield->type->bound = BUCKETSIZE; + keysfield->type->width = BUCKETSIZE * keytype->width; + keysfield->width = offset; + keysfield->sym = mal(sizeof(Sym)); + keysfield->sym->name = "keys"; + offset += BUCKETSIZE * keytype->width; + + valuesfield = typ(TFIELD); + valuesfield->type = typ(TARRAY); + valuesfield->type->type = valtype; + valuesfield->type->bound = BUCKETSIZE; + valuesfield->type->width = BUCKETSIZE * valtype->width; + valuesfield->width = offset; + valuesfield->sym = mal(sizeof(Sym)); + valuesfield->sym->name = "values"; + offset += BUCKETSIZE * valtype->width; + + // link up fields + bucket->type = overflowfield; + overflowfield->down = keysfield; + keysfield->down = valuesfield; + valuesfield->down = T; + + bucket->width = offset; + bucket->local = t->local; + t->bucket = bucket; + return bucket; +} + +// Builds a type respresenting a Hmap structure for +// the given map type. This type is not visible to users - +// we include only enough information to generate a correct GC +// program for it. +// Make sure this stays in sync with ../../pkg/runtime/hashmap.c! +static Type* +hmap(Type *t) +{ + Type *h, *bucket; + Type *bucketsfield, *oldbucketsfield; + int32 offset; + + if(t->hmap != T) + return t->hmap; + + bucket = mapbucket(t); + h = typ(TSTRUCT); + h->noalg = 1; + + offset = widthint; // count + offset += 4; // flags + offset += 4; // hash0 + offset += 1; // B + offset += 1; // keysize + offset += 1; // valuesize + offset = (offset + 1) / 2 * 2; + offset += 2; // bucketsize + offset = (offset + widthptr - 1) / widthptr * widthptr; + + bucketsfield = typ(TFIELD); + bucketsfield->type = ptrto(bucket); + bucketsfield->width = offset; + bucketsfield->sym = mal(sizeof(Sym)); + bucketsfield->sym->name = "buckets"; + offset += widthptr; + + oldbucketsfield = typ(TFIELD); + oldbucketsfield->type = ptrto(bucket); + oldbucketsfield->width = offset; + oldbucketsfield->sym = mal(sizeof(Sym)); + oldbucketsfield->sym->name = "oldbuckets"; + offset += widthptr; + + offset += widthptr; // nevacuate (last field in Hmap) + + // link up fields + h->type = bucketsfield; + bucketsfield->down = oldbucketsfield; + oldbucketsfield->down = T; + + h->width = offset; + h->local = t->local; + t->hmap = h; + h->hmap = t; + return h; +} + /* * f is method type, with receiver. * return function type, receiver as first argument (or not). @@ -208,16 +340,8 @@ methods(Type *t) if(!(a->isym->flags & SymSiggen)) { a->isym->flags |= SymSiggen; if(!eqtype(this, it) || this->width < types[tptr]->width) { - // Is okay to call genwrapper here always, - // but we can generate more efficient code - // using genembedtramp if all that is necessary - // is a pointer adjustment and a JMP. compiling_wrappers = 1; - if(isptr[it->etype] && isptr[this->etype] - && f->embedded && !isifacemethod(f->type)) - genembedtramp(it, f, a->isym, 1); - else - genwrapper(it, f, a->isym, 1); + genwrapper(it, f, a->isym, 1); compiling_wrappers = 0; } } @@ -226,11 +350,7 @@ methods(Type *t) a->tsym->flags |= SymSiggen; if(!eqtype(this, t)) { compiling_wrappers = 1; - if(isptr[t->etype] && isptr[this->etype] - && f->embedded && !isifacemethod(f->type)) - genembedtramp(t, f, a->tsym, 0); - else - genwrapper(t, f, a->tsym, 0); + genwrapper(t, f, a->tsym, 0); compiling_wrappers = 0; } } @@ -277,8 +397,8 @@ imethods(Type *t) last = a; // Compiler can only refer to wrappers for - // named interface types. - if(t->sym == S) + // named interface types and non-blank methods. + if(t->sym == S || isblanksym(method)) continue; // NOTE(rsc): Perhaps an oversight that @@ -471,6 +591,10 @@ int haspointers(Type *t) { Type *t1; + int ret; + + if(t->haspointers != 0) + return t->haspointers - 1; switch(t->etype) { case TINT: @@ -486,17 +610,27 @@ haspointers(Type *t) case TUINTPTR: case TFLOAT32: case TFLOAT64: + case TCOMPLEX64: + case TCOMPLEX128: case TBOOL: - return 0; + ret = 0; + break; case TARRAY: - if(t->bound < 0) // slice - return 1; - return haspointers(t->type); + if(t->bound < 0) { // slice + ret = 1; + break; + } + ret = haspointers(t->type); + break; case TSTRUCT: - for(t1=t->type; t1!=T; t1=t1->down) - if(haspointers(t1->type)) - return 1; - return 0; + ret = 0; + for(t1=t->type; t1!=T; t1=t1->down) { + if(haspointers(t1->type)) { + ret = 1; + break; + } + } + break; case TSTRING: case TPTR32: case TPTR64: @@ -506,8 +640,12 @@ haspointers(Type *t) case TMAP: case TFUNC: default: - return 1; + ret = 1; + break; } + + t->haspointers = 1+ret; + return ret; } /* @@ -709,7 +847,7 @@ static Sym* dtypesym(Type *t) { int ot, xt, n, isddd, dupok; - Sym *s, *s1, *s2, *slink; + Sym *s, *s1, *s2, *s3, *s4, *slink; Sig *a, *m; Type *t1, *tbase, *t2; @@ -849,10 +987,14 @@ ok: // ../../pkg/runtime/type.go:/MapType s1 = dtypesym(t->down); s2 = dtypesym(t->type); + s3 = dtypesym(mapbucket(t)); + s4 = dtypesym(hmap(t)); ot = dcommontype(s, ot, t); xt = ot - 2*widthptr; ot = dsymptr(s, ot, s1, 0); ot = dsymptr(s, ot, s2, 0); + ot = dsymptr(s, ot, s3, 0); + ot = dsymptr(s, ot, s4, 0); break; case TPTR32: @@ -1026,7 +1168,8 @@ dalgsym(Type *t) } static int -gcinline(Type *t) { +gcinline(Type *t) +{ switch(t->etype) { case TARRAY: if(t->bound == 1) @@ -1111,9 +1254,9 @@ dgcsym1(Sym *s, int ot, Type *t, vlong *off, int stack_size) // NOTE: Any changes here need to be made to reflect.MapOf as well. if(*off % widthptr != 0) fatal("dgcsym1: invalid alignment, %T", t); - ot = duintptr(s, ot, GC_MAP_PTR); + ot = duintptr(s, ot, GC_PTR); ot = duintptr(s, ot, *off); - ot = dsymptr(s, ot, dtypesym(t), 0); + ot = dsymptr(s, ot, dgcsym(hmap(t)), 0); *off += t->width; break; diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go index 2139a95d9..c8d57ab33 100644 --- a/src/cmd/gc/runtime.go +++ b/src/cmd/gc/runtime.go @@ -39,11 +39,6 @@ func goprintf() // filled in by compiler: int n, string, string, ... func concatstring() -// filled in by compiler: Type*, int n, Slice, ... -func append() -func appendslice(typ *byte, x any, y []any) any -func appendstr(typ *byte, x []byte, y string) []byte - func cmpstring(string, string) int func eqstring(string, string) bool func intstring(int64) string @@ -124,6 +119,7 @@ func block() func makeslice(typ *byte, nel int64, cap int64) (ary []any) func growslice(typ *byte, old []any, n int64) (ary []any) +func memmove(to *any, frm *any, length uintptr) func memequal(eq *bool, size uintptr, x, y *any) func memequal8(eq *bool, size uintptr, x, y *any) @@ -149,3 +145,5 @@ func racefuncenter(uintptr) func racefuncexit() func raceread(uintptr) func racewrite(uintptr) +func racereadrange(addr, size uintptr) +func racewriterange(addr, size uintptr) diff --git a/src/cmd/gc/sinit.c b/src/cmd/gc/sinit.c index 51c5f7022..446b1110a 100644 --- a/src/cmd/gc/sinit.c +++ b/src/cmd/gc/sinit.c @@ -25,10 +25,13 @@ static void init2list(NodeList*, NodeList**); static int staticinit(Node*, NodeList**); static Node *staticname(Type*, int); +// init1 walks the AST starting at n, and accumulates in out +// the list of definitions needing init code in dependency order. static void init1(Node *n, NodeList **out) { NodeList *l; + Node *nv; if(n == N) return; @@ -61,9 +64,29 @@ init1(Node *n, NodeList **out) if(n->initorder == InitDone) return; if(n->initorder == InitPending) { - if(n->class == PFUNC) - return; + // Since mutually recursive sets of functions are allowed, + // we don't necessarily raise an error if n depends on a node + // which is already waiting for its dependencies to be visited. + // + // initlist contains a cycle of identifiers referring to each other. + // If this cycle contains a variable, then this variable refers to itself. + // Conversely, if there exists an initialization cycle involving + // a variable in the program, the tree walk will reach a cycle + // involving that variable. + if(n->class != PFUNC) { + nv = n; + goto foundinitloop; + } + for(l=initlist; l->n!=n; l=l->next) { + if(l->n->class != PFUNC) { + nv = l->n; + goto foundinitloop; + } + } + // The loop involves only functions, ok. + return; + foundinitloop: // if there have already been errors printed, // those errors probably confused us and // there might not be a loop. let the user @@ -72,17 +95,26 @@ init1(Node *n, NodeList **out) if(nerrors > 0) errorexit(); - print("%L: initialization loop:\n", n->lineno); - for(l=initlist;; l=l->next) { - if(l->next == nil) - break; - l->next->end = l; - } + // There is a loop involving nv. We know about + // n and initlist = n1 <- ... <- nv <- ... <- n <- ... + print("%L: initialization loop:\n", nv->lineno); + // Build back pointers in initlist. + for(l=initlist; l; l=l->next) + if(l->next != nil) + l->next->end = l; + // Print nv -> ... -> n1 -> n. + for(l=initlist; l->n!=nv; l=l->next); for(; l; l=l->end) print("\t%L %S refers to\n", l->n->lineno, l->n->sym); - print("\t%L %S\n", n->lineno, n->sym); + // Print n -> ... -> nv. + for(l=initlist; l->n!=n; l=l->next); + for(; l->n != nv; l=l->end) + print("\t%L %S refers to\n", l->n->lineno, l->n->sym); + print("\t%L %S\n", nv->lineno, nv->sym); errorexit(); } + + // reached a new unvisited node. n->initorder = InitPending; l = malloc(sizeof *l); if(l == nil) { @@ -116,31 +148,16 @@ init1(Node *n, NodeList **out) break; } - /* - n->defn->dodata = 1; - init1(n->defn->right, out); + init2(n->defn->right, out); if(debug['j']) print("%S\n", n->sym); - *out = list(*out, n->defn); - break; - */ - if(1) { - init2(n->defn->right, out); - if(debug['j']) - print("%S\n", n->sym); - if(isblank(n) || !staticinit(n, out)) { - if(debug['%']) dump("nonstatic", n->defn); - *out = list(*out, n->defn); - } - } else if(0) { - n->defn->dodata = 1; - init1(n->defn->right, out); - if(debug['j']) - print("%S\n", n->sym); + if(isblank(n) || !staticinit(n, out)) { + if(debug['%']) + dump("nonstatic", n->defn); *out = list(*out, n->defn); } break; - + case OAS2FUNC: case OAS2MAPR: case OAS2DOTTYPE: @@ -220,6 +237,9 @@ initreorder(NodeList *l, NodeList **out) } } +// initfix computes initialization order for a list l of top-level +// declarations and outputs the corresponding list of statements +// to include in the init() function body. NodeList* initfix(NodeList *l) { @@ -686,6 +706,7 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init) t->bound = mpgetfix(n->right->val.u.xval); t->width = 0; t->sym = nil; + t->haspointers = 0; dowidth(t); if(ctxt != 0) { diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index 20a15bc71..bea90b87b 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -322,7 +322,7 @@ setlineno(Node *n) uint32 stringhash(char *p) { - int32 h; + uint32 h; int c; h = 0; @@ -333,9 +333,9 @@ stringhash(char *p) h = h*PRIME1 + c; } - if(h < 0) { + if((int32)h < 0) { h = -h; - if(h < 0) + if((int32)h < 0) h = 0; } return h; @@ -525,7 +525,7 @@ saveorignode(Node *n) n->orig = norig; } -// ispaddedfield returns whether the given field +// ispaddedfield reports whether the given field // is followed by padding. For the case where t is // the last field, total gives the size of the enclosing struct. static int @@ -547,6 +547,9 @@ algtype1(Type *t, Type **bad) if(bad) *bad = T; + if(t->noalg) + return ANOEQ; + switch(t->etype) { case TANY: case TFORW: @@ -615,23 +618,23 @@ algtype1(Type *t, Type **bad) return -1; // needs special compare case TSTRUCT: - if(t->type != T && t->type->down == T) { + if(t->type != T && t->type->down == T && !isblanksym(t->type->sym)) { // One-field struct is same as that one field alone. return algtype1(t->type->type, bad); } ret = AMEM; for(t1=t->type; t1!=T; t1=t1->down) { - // Blank fields and padding must be ignored, - // so need special compare. - if(isblanksym(t1->sym) || ispaddedfield(t1, t->width)) { + // All fields must be comparable. + a = algtype1(t1->type, bad); + if(a == ANOEQ) + return ANOEQ; + + // Blank fields, padded fields, fields with non-memory + // equality need special compare. + if(a != AMEM || isblanksym(t1->sym) || ispaddedfield(t1, t->width)) { ret = -1; continue; } - a = algtype1(t1->type, bad); - if(a == ANOEQ) - return ANOEQ; // not comparable - if(a != AMEM) - ret = -1; // needs special compare } return ret; } @@ -1260,7 +1263,7 @@ assignop(Type *src, Type *dst, char **why) "\t\thave %S%hhT\n\t\twant %S%hhT", src, dst, missing->sym, have->sym, have->type, missing->sym, missing->type); else if(ptr) - *why = smprint(":\n\t%T does not implement %T (%S method requires pointer receiver)", + *why = smprint(":\n\t%T does not implement %T (%S method has pointer receiver)", src, dst, missing->sym); else if(have) *why = smprint(":\n\t%T does not implement %T (missing %S method)\n" @@ -1411,6 +1414,9 @@ assignconv(Node *n, Type *t, char *context) if(n == N || n->type == T || n->type->broke) return n; + if(t->etype == TBLANK && n->type->etype == TNIL) + yyerror("use of untyped nil"); + old = n; old->diag++; // silence errors about n; we'll issue one below defaultlit(&n, t); @@ -2177,7 +2183,7 @@ lookdot0(Sym *s, Type *t, Type **save, int ignorecase) c = 0; if(u->etype == TSTRUCT || u->etype == TINTER) { for(f=u->type; f!=T; f=f->down) - if(f->sym == s || (ignorecase && ucistrcmp(f->sym->name, s->name) == 0)) { + if(f->sym == s || (ignorecase && f->type->etype == TFUNC && f->type->thistuple > 0 && ucistrcmp(f->sym->name, s->name) == 0)) { if(save) *save = f; c++; @@ -2495,13 +2501,13 @@ structargs(Type **tl, int mustname) void genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface) { - Node *this, *fn, *call, *n, *t, *pad; + Node *this, *fn, *call, *n, *t, *pad, *dot, *as; NodeList *l, *args, *in, *out; - Type *tpad; + Type *tpad, *methodrcvr; int isddd; Val v; - if(debug['r']) + if(0 && debug['r']) print("genwrapper rcvrtype=%T method=%T newnam=%S\n", rcvr, method, newnam); @@ -2547,8 +2553,10 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface) isddd = l->n->left->isddd; } + methodrcvr = getthisx(method->type)->type->type; + // generate nil pointer check for better error - if(isptr[rcvr->etype] && rcvr->type == getthisx(method->type)->type->type) { + if(isptr[rcvr->etype] && rcvr->type == methodrcvr) { // generating wrapper from *T to T. n = nod(OIF, N, N); n->ntest = nod(OEQ, this->left, nodnil()); @@ -2567,17 +2575,33 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface) n->nbody = list1(call); fn->nbody = list(fn->nbody, n); } - + + dot = adddot(nod(OXDOT, this->left, newname(method->sym))); + // generate call - call = nod(OCALL, adddot(nod(OXDOT, this->left, newname(method->sym))), N); - call->list = args; - call->isddd = isddd; - if(method->type->outtuple > 0) { - n = nod(ORETURN, N, N); - n->list = list1(call); - call = n; + if(!flag_race && isptr[rcvr->etype] && isptr[methodrcvr->etype] && method->embedded && !isifacemethod(method->type)) { + // generate tail call: adjust pointer receiver and jump to embedded method. + dot = dot->left; // skip final .M + if(!isptr[dotlist[0].field->type->etype]) + dot = nod(OADDR, dot, N); + as = nod(OAS, this->left, nod(OCONVNOP, dot, N)); + as->right->type = rcvr; + fn->nbody = list(fn->nbody, as); + n = nod(ORETJMP, N, N); + n->left = newname(methodsym(method->sym, methodrcvr, 0)); + fn->nbody = list(fn->nbody, n); + } else { + fn->wrapper = 1; // ignore frame for panic+recover matching + call = nod(OCALL, dot, N); + call->list = args; + call->isddd = isddd; + if(method->type->outtuple > 0) { + n = nod(ORETURN, N, N); + n->list = list1(call); + call = n; + } + fn->nbody = list(fn->nbody, call); } - fn->nbody = list(fn->nbody, call); if(0 && debug['r']) dumplist("genwrapper body", fn->nbody); @@ -3752,7 +3776,7 @@ isbadimport(Strlit *path) } void -checknotnil(Node *x, NodeList **init) +checknil(Node *x, NodeList **init) { Node *n; @@ -3760,7 +3784,7 @@ checknotnil(Node *x, NodeList **init) x = nod(OITAB, x, N); typecheck(&x, Erv); } - n = nod(OCHECKNOTNIL, x, N); + n = nod(OCHECKNIL, x, N); n->typecheck = 1; *init = list(*init, n); } diff --git a/src/cmd/gc/swt.c b/src/cmd/gc/swt.c index 3ad5f02a5..d6aa021a9 100644 --- a/src/cmd/gc/swt.c +++ b/src/cmd/gc/swt.c @@ -315,7 +315,7 @@ casebody(Node *sw, Node *typeswvar) } stat = concat(stat, n->nbody); - // botch - shouldnt fall thru declaration + // botch - shouldn't fall thru declaration last = stat->end->n; if(last->op == OXFALL) { if(typeswvar) { @@ -415,7 +415,7 @@ mkcaselist(Node *sw, int arg) break; if(!eqtype(c1->node->left->type, c2->node->left->type)) continue; - yyerrorl(c2->node->lineno, "duplicate case in switch\n\tprevious case at %L", c1->node->lineno); + yyerrorl(c2->node->lineno, "duplicate case %T in type switch\n\tprevious case at %L", c2->node->left->type, c1->node->lineno); } } break; @@ -427,7 +427,7 @@ mkcaselist(Node *sw, int arg) if(exprcmp(c1, c1->link) != 0) continue; setlineno(c1->link->node); - yyerror("duplicate case in switch\n\tprevious case at %L", c1->node->lineno); + yyerror("duplicate case %N in switch\n\tprevious case at %L", c1->node->left, c1->node->lineno); } break; } @@ -820,6 +820,9 @@ walkswitch(Node *sw) return; } exprswitch(sw); + // Discard old AST elements after a walk. They can confuse racewealk. + sw->ntest = nil; + sw->list = nil; } /* diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index 40eecd337..31a2f2c5c 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -33,6 +33,8 @@ static void stringtoarraylit(Node**); static Node* resolve(Node*); static void checkdefergo(Node*); static int checkmake(Type*, char*, Node*); +static int checksliceindex(Node*, Type*); +static int checksliceconst(Node*, Node*); static NodeList* typecheckdefstack; @@ -88,7 +90,7 @@ static char* _typekind[] = { [TARRAY] = "array", [TFUNC] = "func", [TNIL] = "nil", - [TIDEAL] = "ideal number", + [TIDEAL] = "untyped number", }; static char* @@ -303,7 +305,7 @@ static void typecheck1(Node **np, int top) { int et, aop, op, ptr; - Node *n, *l, *r; + Node *n, *l, *r, *lo, *mid, *hi; NodeList *args; int ok, ntop; Type *t, *tp, *missing, *have, *badtype; @@ -420,12 +422,12 @@ reswitch: goto error; } t->bound = mpgetfix(v.u.xval); - if(t->bound < 0) { - yyerror("array bound must be non-negative"); - goto error; - } else if(doesoverflow(v, types[TINT])) { + if(doesoverflow(v, types[TINT])) { yyerror("array bound is too large"); goto error; + } else if(t->bound < 0) { + yyerror("array bound must be non-negative"); + goto error; } } typecheck(&r, Etype); @@ -837,7 +839,7 @@ reswitch: "\t\thave %S%hhT\n\t\twant %S%hhT", n->type, t, missing->sym, have->sym, have->type, missing->sym, missing->type); else if(ptr) - yyerror("impossible type assertion:\n\t%T does not implement %T (%S method requires pointer receiver)", + yyerror("impossible type assertion:\n\t%T does not implement %T (%S method has pointer receiver)", n->type, t, missing->sym); else if(have) yyerror("impossible type assertion:\n\t%T does not implement %T (missing %S method)\n" @@ -926,11 +928,7 @@ reswitch: goto ret; case OSEND: - if(top & Erv) { - yyerror("send statement %N used as value; use select for non-blocking send", n); - goto error; - } - ok |= Etop | Erv; + ok |= Etop; l = typecheck(&n->left, Erv); typecheck(&n->right, Erv); defaultlit(&n->left, T); @@ -993,54 +991,63 @@ reswitch: yyerror("cannot slice %N (type %T)", l, t); goto error; } - if(n->right->left != N) { - if((t = n->right->left->type) == T) - goto error; - if(!isint[t->etype]) { - yyerror("invalid slice index %N (type %T)", n->right->left, t); + if((lo = n->right->left) != N && checksliceindex(lo, tp) < 0) + goto error; + if((hi = n->right->right) != N && checksliceindex(hi, tp) < 0) + goto error; + if(checksliceconst(lo, hi) < 0) + goto error; + goto ret; + + case OSLICE3: + ok |= Erv; + typecheck(&n->left, top); + typecheck(&n->right->left, Erv); + typecheck(&n->right->right->left, Erv); + typecheck(&n->right->right->right, Erv); + defaultlit(&n->left, T); + indexlit(&n->right->left); + indexlit(&n->right->right->left); + indexlit(&n->right->right->right); + l = n->left; + if(isfixedarray(l->type)) { + if(!islvalue(n->left)) { + yyerror("invalid operation %N (slice of unaddressable value)", n); goto error; } - if(n->right->left->op == OLITERAL) { - if(mpgetfix(n->right->left->val.u.xval) < 0) { - yyerror("invalid slice index %N (index must be non-negative)", n->right->left); - goto error; - } else if(tp != nil && tp->bound > 0 && mpgetfix(n->right->left->val.u.xval) > tp->bound) { - yyerror("invalid slice index %N (out of bounds for %d-element array)", n->right->left, tp->bound); - goto error; - } else if(mpcmpfixfix(n->right->left->val.u.xval, maxintval[TINT]) > 0) { - yyerror("invalid slice index %N (index too large)", n->right->left); - goto error; - } - } + n->left = nod(OADDR, n->left, N); + n->left->implicit = 1; + typecheck(&n->left, Erv); + l = n->left; } - if(n->right->right != N) { - if((t = n->right->right->type) == T) - goto error; - if(!isint[t->etype]) { - yyerror("invalid slice index %N (type %T)", n->right->right, t); - goto error; - } - if(n->right->right->op == OLITERAL) { - if(mpgetfix(n->right->right->val.u.xval) < 0) { - yyerror("invalid slice index %N (index must be non-negative)", n->right->right); - goto error; - } else if(tp != nil && tp->bound > 0 && mpgetfix(n->right->right->val.u.xval) > tp->bound) { - yyerror("invalid slice index %N (out of bounds for %d-element array)", n->right->right, tp->bound); - goto error; - } else if(mpcmpfixfix(n->right->right->val.u.xval, maxintval[TINT]) > 0) { - yyerror("invalid slice index %N (index too large)", n->right->right); - goto error; - } - } + if((t = l->type) == T) + goto error; + tp = nil; + if(istype(t, TSTRING)) { + yyerror("invalid operation %N (3-index slice of string)", n); + goto error; } - if(n->right->left != N - && n->right->right != N - && n->right->left->op == OLITERAL - && n->right->right->op == OLITERAL - && mpcmpfixfix(n->right->left->val.u.xval, n->right->right->val.u.xval) > 0) { - yyerror("inverted slice index %N > %N", n->right->left, n->right->right); + if(isptr[t->etype] && isfixedarray(t->type)) { + tp = t->type; + n->type = typ(TARRAY); + n->type->type = tp->type; + n->type->bound = -1; + dowidth(n->type); + n->op = OSLICE3ARR; + } else if(isslice(t)) { + n->type = t; + } else { + yyerror("cannot slice %N (type %T)", l, t); goto error; } + if((lo = n->right->left) != N && checksliceindex(lo, tp) < 0) + goto error; + if((mid = n->right->right->left) != N && checksliceindex(mid, tp) < 0) + goto error; + if((hi = n->right->right->right) != N && checksliceindex(hi, tp) < 0) + goto error; + if(checksliceconst(lo, hi) < 0 || checksliceconst(lo, mid) < 0 || checksliceconst(mid, hi) < 0) + goto error; goto ret; /* @@ -1565,7 +1572,20 @@ reswitch: fatal("OITAB of %T", t); n->type = ptrto(types[TUINTPTR]); goto ret; - + + case OSPTR: + ok |= Erv; + typecheck(&n->left, Erv); + if((t = n->left->type) == T) + goto error; + if(!isslice(t) && t->etype != TSTRING) + fatal("OSPTR of %T", t); + if(t->etype == TSTRING) + n->type = ptrto(types[TUINT8]); + else + n->type = ptrto(t->type); + goto ret; + case OCLOSUREVAR: ok |= Erv; goto ret; @@ -1651,6 +1671,10 @@ reswitch: goto ret; typecheckaste(ORETURN, nil, 0, getoutargx(curfn->type), n->list, "return argument"); goto ret; + + case ORETJMP: + ok |= Etop; + goto ret; case OSELECT: ok |= Etop; @@ -1753,6 +1777,43 @@ out: *np = n; } +static int +checksliceindex(Node *r, Type *tp) +{ + Type *t; + + if((t = r->type) == T) + return -1; + if(!isint[t->etype]) { + yyerror("invalid slice index %N (type %T)", r, t); + return -1; + } + if(r->op == OLITERAL) { + if(mpgetfix(r->val.u.xval) < 0) { + yyerror("invalid slice index %N (index must be non-negative)", r); + return -1; + } else if(tp != nil && tp->bound > 0 && mpgetfix(r->val.u.xval) > tp->bound) { + yyerror("invalid slice index %N (out of bounds for %d-element array)", r, tp->bound); + return -1; + } else if(mpcmpfixfix(r->val.u.xval, maxintval[TINT]) > 0) { + yyerror("invalid slice index %N (index too large)", r); + return -1; + } + } + return 0; +} + +static int +checksliceconst(Node *lo, Node *hi) +{ + if(lo != N && hi != N && lo->op == OLITERAL && hi->op == OLITERAL + && mpcmpfixfix(lo->val.u.xval, hi->val.u.xval) > 0) { + yyerror("invalid slice index: %N > %N", lo, hi); + return -1; + } + return 0; +} + static void checkdefergo(Node *n) { @@ -1793,6 +1854,11 @@ checkdefergo(Node *n) break; default: conv: + // type is broken or missing, most likely a method call on a broken type + // we will warn about the broken type elsewhere. no need to emit a potentially confusing error + if(n->left->type == T || n->left->type->broke) + break; + if(!n->diag) { // The syntax made sure it was a call, so this must be // a conversion. @@ -2074,9 +2140,9 @@ typecheckaste(int op, Node *call, int isddd, Type *tstruct, NodeList *nl, char * for(; tn; tn=tn->down) { if(assignop(tn->type, tl->type->type, &why) == 0) { if(call != N) - yyerror("cannot use %T as type %T in argument to %N%s", tn->type, tl->type, call, why); + yyerror("cannot use %T as type %T in argument to %N%s", tn->type, tl->type->type, call, why); else - yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type, desc, why); + yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type->type, desc, why); } } goto out; @@ -2993,7 +3059,7 @@ queuemethod(Node *n) Node* typecheckdef(Node *n) { - int lno; + int lno, nerrors0; Node *e; Type *t; NodeList *l; @@ -3121,7 +3187,13 @@ typecheckdef(Node *n) n->walkdef = 1; n->type = typ(TFORW); n->type->sym = n->sym; + nerrors0 = nerrors; typecheckdeftype(n); + if(n->type->etype == TFORW && nerrors > nerrors0) { + // Something went wrong during type-checking, + // but it was reported. Silence future errors. + n->type->broke = 1; + } if(curfn) resumecheckwidth(); break; @@ -3277,6 +3349,7 @@ isterminating(NodeList *l, int top) case OGOTO: case ORETURN: + case ORETJMP: case OPANIC: case OXFALL: return 1; diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index e8ebd2892..66409d530 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -21,6 +21,7 @@ static NodeList* reorder3(NodeList*); static Node* addstr(Node*, NodeList**); static Node* appendslice(Node*, NodeList**); static Node* append(Node*, NodeList**); +static Node* copyany(Node*, NodeList**); static Node* sliceany(Node*, NodeList**); static void walkcompare(Node**, NodeList**); static void walkrotate(Node**); @@ -174,6 +175,8 @@ walkstmt(Node **np) n->ninit = nil; walkexpr(&n, &init); addinit(&n, init); + if((*np)->op == OCOPY && n->op == OCONVNOP) + n->op = OEMPTY; // don't leave plain values as statements. break; case OBREAK: @@ -184,7 +187,7 @@ walkstmt(Node **np) case OLABEL: case ODCLCONST: case ODCLTYPE: - case OCHECKNOTNIL: + case OCHECKNIL: break; case OBLOCK: @@ -282,6 +285,9 @@ walkstmt(Node **np) n->list = ll; break; + case ORETJMP: + break; + case OSELECT: walkselect(n); break; @@ -337,7 +343,7 @@ walkexpr(Node **np, NodeList **init) Node *r, *l, *var, *a; NodeList *ll, *lr, *lpost; Type *t; - int et; + int et, old_safemode; int64 v; int32 lno; Node *n, *fn, *n1, *n2; @@ -401,10 +407,6 @@ walkexpr(Node **np, NodeList **init) goto ret; case OIND: - if(n->left->type->type->width == 0) { - n->left = cheapexpr(n->left, init); - checknotnil(n->left, init); - } walkexpr(&n->left, init); goto ret; @@ -416,8 +418,9 @@ walkexpr(Node **np, NodeList **init) case ODOTPTR: usefield(n); if(n->op == ODOTPTR && n->left->type->type->width == 0) { + // No actual copy will be generated, so emit an explicit nil check. n->left = cheapexpr(n->left, init); - checknotnil(n->left, init); + checknil(n->left, init); } walkexpr(&n->left, init); goto ret; @@ -427,6 +430,7 @@ walkexpr(Node **np, NodeList **init) walkexpr(&n->right, init); goto ret; + case OSPTR: case OITAB: walkexpr(&n->left, init); goto ret; @@ -483,7 +487,15 @@ walkexpr(Node **np, NodeList **init) case ONE: walkexpr(&n->left, init); walkexpr(&n->right, init); + // Disable safemode while compiling this code: the code we + // generate internally can refer to unsafe.Pointer. + // In this case it can happen if we need to generate an == + // for a struct containing a reflect.Value, which itself has + // an unexported field of type unsafe.Pointer. + old_safemode = safemode; + safemode = 0; walkcompare(&n, init); + safemode = old_safemode; goto ret; case OANDAND: @@ -1032,8 +1044,8 @@ walkexpr(Node **np, NodeList **init) if(!n->bounded) yyerror("index out of bounds"); else { - // replace "abc"[2] with 'b'. - // delayed until now because "abc"[2] is not + // replace "abc"[1] with 'b'. + // delayed until now because "abc"[1] is not // an ideal constant. v = mpgetfix(n->right->val.u.xval); nodconst(n, n->type, n->left->val.u.sval->s[v]); @@ -1113,6 +1125,27 @@ walkexpr(Node **np, NodeList **init) n->right->right = safeexpr(n->right->right, init); n = sliceany(n, init); // chops n->right, sets n->list goto ret; + + case OSLICE3: + case OSLICE3ARR: + if(n->right == N) // already processed + goto ret; + + walkexpr(&n->left, init); + // TODO the OINDEX case is a bug elsewhere that needs to be traced. it causes a crash on ([2][]int{ ... })[1][lo:hi] + // TODO the comment on the previous line was copied from case OSLICE. it might not even be true. + if(n->left->op == OINDEX) + n->left = copyexpr(n->left, n->left->type, init); + else + n->left = safeexpr(n->left, init); + walkexpr(&n->right->left, init); + n->right->left = safeexpr(n->right->left, init); + walkexpr(&n->right->right->left, init); + n->right->right->left = safeexpr(n->right->right->left, init); + walkexpr(&n->right->right->right, init); + n->right->right->right = safeexpr(n->right->right->right, init); + n = sliceany(n, init); // chops n->right, sets n->list + goto ret; case OADDR: walkexpr(&n->left, init); @@ -1198,26 +1231,26 @@ walkexpr(Node **np, NodeList **init) goto ret; case OAPPEND: - if(n->isddd) { - if(istype(n->type->type, TUINT8) && istype(n->list->next->n->type, TSTRING)) - n = mkcall("appendstr", n->type, init, typename(n->type), n->list->n, n->list->next->n); - else - n = appendslice(n, init); - } + if(n->isddd) + n = appendslice(n, init); // also works for append(slice, string). else n = append(n, init); goto ret; case OCOPY: - if(n->right->type->etype == TSTRING) - fn = syslook("slicestringcopy", 1); - else - fn = syslook("copy", 1); - argtype(fn, n->left->type); - argtype(fn, n->right->type); - n = mkcall1(fn, n->type, init, - n->left, n->right, - nodintconst(n->left->type->type->width)); + if(flag_race) { + if(n->right->type->etype == TSTRING) + fn = syslook("slicestringcopy", 1); + else + fn = syslook("copy", 1); + argtype(fn, n->left->type); + argtype(fn, n->right->type); + n = mkcall1(fn, n->type, init, + n->left, n->right, + nodintconst(n->left->type->type->width)); + goto ret; + } + n = copyany(n, init); goto ret; case OCLOSE: @@ -1246,18 +1279,35 @@ walkexpr(Node **np, NodeList **init) goto ret; case OMAKESLICE: - // makeslice(t *Type, nel int64, max int64) (ary []any) l = n->left; r = n->right; if(r == nil) l = r = safeexpr(l, init); t = n->type; - fn = syslook("makeslice", 1); - argtype(fn, t->type); // any-1 - n = mkcall1(fn, n->type, init, - typename(n->type), - conv(l, types[TINT64]), - conv(r, types[TINT64])); + if(n->esc == EscNone + && smallintconst(l) && smallintconst(r) + && (t->type->width == 0 || mpgetfix(r->val.u.xval) < (1ULL<<16) / t->type->width)) { + // var arr [r]T + // n = arr[:l] + t = aindex(r, t->type); // [r]T + var = temp(t); + a = nod(OAS, var, N); // zero temp + typecheck(&a, Etop); + *init = list(*init, a); + r = nod(OSLICE, var, nod(OKEY, N, l)); // arr[:l] + r = conv(r, n->type); // in case n->type is named. + typecheck(&r, Erv); + walkexpr(&r, init); + n = r; + } else { + // makeslice(t *Type, nel int64, max int64) (ary []any) + fn = syslook("makeslice", 1); + argtype(fn, t->type); // any-1 + n = mkcall1(fn, n->type, init, + typename(n->type), + conv(l, types[TINT64]), + conv(r, types[TINT64])); + } goto ret; case ORUNESTR: @@ -1343,7 +1393,11 @@ ret: // constants until walk. For example, if n is y%1 == 0, the // walk of y%1 may have replaced it by 0. // Check whether n with its updated args is itself now a constant. + t = n->type; evconst(n); + n->type = t; + if(n->op == OLITERAL) + typecheck(&n, Erv); ullmancalc(n); @@ -2268,7 +2322,9 @@ paramstoheap(Type **argin, int out) v = t->nname; if(v && v->sym && v->sym->name[0] == '~') v = N; - if(v == N && out && hasdefer) { + // In precisestack mode, the garbage collector assumes results + // are always live, so zero them always. + if(out && (precisestack_enabled || (v == N && hasdefer))) { // Defer might stop a panic and show the // return values as they exist at the time of panic. // Make sure to zero them on entry to the function. @@ -2473,16 +2529,104 @@ addstr(Node *n, NodeList **init) return r; } +// expand append(l1, l2...) to +// init { +// s := l1 +// if n := len(l1) + len(l2) - cap(s); n > 0 { +// s = growslice(s, n) +// } +// s = s[:len(l1)+len(l2)] +// memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T)) +// } +// s +// +// l2 is allowed to be a string. static Node* appendslice(Node *n, NodeList **init) { - Node *f; + NodeList *l; + Node *l1, *l2, *nt, *nif, *fn; + Node *nptr1, *nptr2, *nwid; + Node *s; + + walkexprlistsafe(n->list, init); + + // walkexprlistsafe will leave OINDEX (s[n]) alone if both s + // and n are name or literal, but those may index the slice we're + // modifying here. Fix explicitly. + for(l=n->list; l; l=l->next) + l->n = cheapexpr(l->n, init); + + l1 = n->list->n; + l2 = n->list->next->n; + + s = temp(l1->type); // var s []T + l = nil; + l = list(l, nod(OAS, s, l1)); // s = l1 + + nt = temp(types[TINT]); + nif = nod(OIF, N, N); + // n := len(s) + len(l2) - cap(s) + nif->ninit = list1(nod(OAS, nt, + nod(OSUB, nod(OADD, nod(OLEN, s, N), nod(OLEN, l2, N)), nod(OCAP, s, N)))); + nif->ntest = nod(OGT, nt, nodintconst(0)); + // instantiate growslice(Type*, []any, int64) []any + fn = syslook("growslice", 1); + argtype(fn, s->type->type); + argtype(fn, s->type->type); + + // s = growslice(T, s, n) + nif->nbody = list1(nod(OAS, s, mkcall1(fn, s->type, &nif->ninit, + typename(s->type), + s, + conv(nt, types[TINT64])))); + + l = list(l, nif); + + if(flag_race) { + // rely on runtime to instrument copy. + // copy(s[len(l1):len(l1)+len(l2)], l2) + nptr1 = nod(OSLICE, s, nod(OKEY, + nod(OLEN, l1, N), + nod(OADD, nod(OLEN, l1, N), nod(OLEN, l2, N)))); + nptr1->etype = 1; + nptr2 = l2; + if(l2->type->etype == TSTRING) + fn = syslook("slicestringcopy", 1); + else + fn = syslook("copy", 1); + argtype(fn, l1->type); + argtype(fn, l2->type); + l = list(l, mkcall1(fn, types[TINT], init, + nptr1, nptr2, + nodintconst(s->type->type->width))); + } else { + // memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T)) + nptr1 = nod(OINDEX, s, nod(OLEN, l1, N)); + nptr1->bounded = 1; + nptr1 = nod(OADDR, nptr1, N); + + nptr2 = nod(OSPTR, l2, N); - f = syslook("appendslice", 1); - argtype(f, n->type); - argtype(f, n->type->type); - argtype(f, n->type); - return mkcall1(f, n->type, init, typename(n->type), n->list->n, n->list->next->n); + fn = syslook("memmove", 1); + argtype(fn, s->type->type); // 1 old []any + argtype(fn, s->type->type); // 2 ret []any + + nwid = cheapexpr(conv(nod(OLEN, l2, N), types[TUINTPTR]), &l); + nwid = nod(OMUL, nwid, nodintconst(s->type->type->width)); + l = list(l, mkcall1(fn, T, init, nptr1, nptr2, nwid)); + } + + // s = s[:len(l1)+len(l2)] + nt = nod(OADD, nod(OLEN, l1, N), nod(OLEN, l2, N)); + nt = nod(OSLICE, s, nod(OKEY, N, nt)); + nt->etype = 1; + l = list(l, nod(OAS, s, nt)); + + typechecklist(l, Etop); + walkstmtlist(l); + *init = concat(*init, l); + return s; } // expand append(src, a [, b]* ) to @@ -2544,7 +2688,7 @@ append(Node *n, NodeList **init) l = list(l, nod(OAS, nn, nod(OLEN, ns, N))); // n = len(s) nx = nod(OSLICE, ns, nod(OKEY, N, nod(OADD, nn, na))); // ...s[:n+argc] - nx->bounded = 1; + nx->etype = 1; l = list(l, nod(OAS, ns, nx)); // s = s[:n+argc] for (a = n->list->next; a != nil; a = a->next) { @@ -2561,22 +2705,81 @@ append(Node *n, NodeList **init) return ns; } +// Lower copy(a, b) to a memmove call. +// +// init { +// n := len(a) +// if n > len(b) { n = len(b) } +// memmove(a.ptr, b.ptr, n*sizeof(elem(a))) +// } +// n; +// +// Also works if b is a string. +// +static Node* +copyany(Node *n, NodeList **init) +{ + Node *nl, *nr, *nfrm, *nto, *nif, *nlen, *nwid, *fn; + NodeList *l; + + walkexpr(&n->left, init); + walkexpr(&n->right, init); + nl = temp(n->left->type); + nr = temp(n->right->type); + l = nil; + l = list(l, nod(OAS, nl, n->left)); + l = list(l, nod(OAS, nr, n->right)); + + nfrm = nod(OSPTR, nr, N); + nto = nod(OSPTR, nl, N); + + nlen = temp(types[TINT]); + // n = len(to) + l = list(l, nod(OAS, nlen, nod(OLEN, nl, N))); + // if n > len(frm) { n = len(frm) } + nif = nod(OIF, N, N); + nif->ntest = nod(OGT, nlen, nod(OLEN, nr, N)); + nif->nbody = list(nif->nbody, + nod(OAS, nlen, nod(OLEN, nr, N))); + l = list(l, nif); + + // Call memmove. + fn = syslook("memmove", 1); + argtype(fn, nl->type->type); + argtype(fn, nl->type->type); + nwid = temp(types[TUINTPTR]); + l = list(l, nod(OAS, nwid, conv(nlen, types[TUINTPTR]))); + nwid = nod(OMUL, nwid, nodintconst(nl->type->type->width)); + l = list(l, mkcall1(fn, T, init, nto, nfrm, nwid)); + + typechecklist(l, Etop); + walkstmtlist(l); + *init = concat(*init, l); + return nlen; +} -// Generate frontend part for OSLICE[ARR|STR] +// Generate frontend part for OSLICE[3][ARR|STR] // static Node* sliceany(Node* n, NodeList **init) { - int bounded; - Node *src, *lb, *hb, *bound, *chk, *chk1, *chk2; - int64 lbv, hbv, bv, w; + int bounded, slice3; + Node *src, *lb, *hb, *cb, *bound, *chk, *chk0, *chk1, *chk2; + int64 lbv, hbv, cbv, bv, w; Type *bt; // print("before sliceany: %+N\n", n); src = n->left; lb = n->right->left; - hb = n->right->right; + slice3 = n->op == OSLICE3 || n->op == OSLICE3ARR; + if(slice3) { + hb = n->right->right->left; + cb = n->right->right->right; + } else { + hb = n->right->right; + cb = N; + } bounded = n->etype; @@ -2597,6 +2800,13 @@ sliceany(Node* n, NodeList **init) bv = mpgetfix(bound->val.u.xval); } + if(isconst(cb, CTINT)) { + cbv = mpgetfix(cb->val.u.xval); + if(cbv < 0 || cbv > bv) { + yyerror("slice index out of bounds"); + cbv = -1; + } + } if(isconst(hb, CTINT)) { hbv = mpgetfix(hb->val.u.xval); if(hbv < 0 || hbv > bv) { @@ -2618,10 +2828,13 @@ sliceany(Node* n, NodeList **init) // generate // if hb > bound || lb > hb { panicslice() } chk = N; + chk0 = N; chk1 = N; chk2 = N; bt = types[simtype[TUINT]]; + if(cb != N && cb->type->width > 4) + bt = types[TUINT64]; if(hb != N && hb->type->width > 4) bt = types[TUINT64]; if(lb != N && lb->type->width > 4) @@ -2629,10 +2842,26 @@ sliceany(Node* n, NodeList **init) bound = cheapexpr(conv(bound, bt), init); + if(cb != N) { + cb = cheapexpr(conv(cb, bt), init); + if(!bounded) + chk0 = nod(OLT, bound, cb); + } else if(slice3) { + // When we figure out what this means, implement it. + fatal("slice3 with cb == N"); // rejected by parser + } + if(hb != N) { hb = cheapexpr(conv(hb, bt), init); - if(!bounded) - chk1 = nod(OLT, bound, hb); + if(!bounded) { + if(cb != N) + chk1 = nod(OLT, cb, hb); + else + chk1 = nod(OLT, bound, hb); + } + } else if(slice3) { + // When we figure out what this means, implement it. + fatal("slice3 with hb == N"); // rejected by parser } else if(n->op == OSLICEARR) { hb = bound; } else { @@ -2648,11 +2877,18 @@ sliceany(Node* n, NodeList **init) chk2 = nod(OLT, hb, lb); } - if(chk1 != N || chk2 != N) { + if(chk0 != N || chk1 != N || chk2 != N) { chk = nod(OIF, N, N); chk->nbody = list1(mkcall("panicslice", T, init)); - if(chk1 != N) - chk->ntest = chk1; + chk->likely = -1; + if(chk0 != N) + chk->ntest = chk0; + if(chk1 != N) { + if(chk->ntest == N) + chk->ntest = chk1; + else + chk->ntest = nod(OOROR, chk->ntest, chk1); + } if(chk2 != N) { if(chk->ntest == N) chk->ntest = chk2; @@ -2670,10 +2906,12 @@ sliceany(Node* n, NodeList **init) // cap = bound [ - lo ] n->right = N; n->list = nil; + if(!slice3) + cb = bound; if(lb == N) - bound = conv(bound, types[simtype[TUINT]]); + bound = conv(cb, types[simtype[TUINT]]); else - bound = nod(OSUB, conv(bound, types[simtype[TUINT]]), conv(lb, types[simtype[TUINT]])); + bound = nod(OSUB, conv(cb, types[simtype[TUINT]]), conv(lb, types[simtype[TUINT]])); typecheck(&bound, Erv); walkexpr(&bound, init); n->list = list(n->list, bound); diff --git a/src/cmd/gc/y.tab.c b/src/cmd/gc/y.tab.c index 21c67e805..390ad80b3 100644 --- a/src/cmd/gc/y.tab.c +++ b/src/cmd/gc/y.tab.c @@ -1,24 +1,21 @@ -/* A Bison parser, made by GNU Bison 2.3. */ +/* A Bison parser, made by GNU Bison 2.5. */ -/* Skeleton implementation for Bison's Yacc-like parsers in C - - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 - Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ + along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work @@ -29,7 +26,7 @@ special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. - + This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ @@ -47,7 +44,7 @@ #define YYBISON 1 /* Bison version. */ -#define YYBISON_VERSION "2.3" +#define YYBISON_VERSION "2.5" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -55,11 +52,52 @@ /* Pure parsers. */ #define YYPURE 0 +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + /* Using locations. */ #define YYLSP_NEEDED 0 +/* Copy the first part of user declarations. */ + +/* Line 268 of yacc.c */ +#line 20 "go.y" + +#include <u.h> +#include <stdio.h> /* if we don't, bison will, and go.h re-#defines getc */ +#include <libc.h> +#include "go.h" + +static void fixlbrace(int); + + +/* Line 268 of yacc.c */ +#line 81 "y.tab.c" + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + + /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE @@ -171,61 +209,36 @@ -/* Copy the first part of user declarations. */ -#line 20 "go.y" - -#include <u.h> -#include <stdio.h> /* if we don't, bison will, and go.h re-#defines getc */ -#include <libc.h> -#include "go.h" - -static void fixlbrace(int); - - -/* Enabling traces. */ -#ifndef YYDEBUG -# define YYDEBUG 0 -#endif - -/* Enabling verbose error messages. */ -#ifdef YYERROR_VERBOSE -# undef YYERROR_VERBOSE -# define YYERROR_VERBOSE 1 -#else -# define YYERROR_VERBOSE 1 -#endif - -/* Enabling the token table. */ -#ifndef YYTOKEN_TABLE -# define YYTOKEN_TABLE 0 -#endif - #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE -#line 28 "go.y" { + +/* Line 293 of yacc.c */ +#line 28 "go.y" + Node* node; NodeList* list; Type* type; Sym* sym; struct Val val; int i; -} -/* Line 193 of yacc.c. */ -#line 216 "y.tab.c" - YYSTYPE; + + + +/* Line 293 of yacc.c */ +#line 230 "y.tab.c" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 -# define YYSTYPE_IS_TRIVIAL 1 #endif - /* Copy the second part of user declarations. */ -/* Line 216 of yacc.c. */ -#line 229 "y.tab.c" +/* Line 343 of yacc.c */ +#line 242 "y.tab.c" #ifdef short # undef short @@ -300,14 +313,14 @@ typedef short int yytype_int16; #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static int -YYID (int i) +YYID (int yyi) #else static int -YYID (i) - int i; +YYID (yyi) + int yyi; #endif { - return i; + return yyi; } #endif @@ -328,11 +341,11 @@ YYID (i) # define alloca _alloca # else # define YYSTACK_ALLOC alloca -# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 # endif # endif # endif @@ -355,24 +368,24 @@ YYID (i) # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif -# if (defined __cplusplus && ! defined _STDLIB_H \ +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc -# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free -# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif @@ -388,9 +401,9 @@ void free (void *); /* INFRINGES ON USER NAME SPACE */ /* A type that is properly aligned for any stack member. */ union yyalloc { - yytype_int16 yyss; - YYSTYPE yyvs; - }; + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; +}; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) @@ -401,6 +414,27 @@ union yyalloc ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED /* Copy COUNT objects from FROM to TO. The source and destination do not overlap. */ # ifndef YYCOPY @@ -418,38 +452,21 @@ union yyalloc while (YYID (0)) # endif # endif - -/* Relocate STACK from its old location to the new one. The - local variables YYSIZE and YYSTACKSIZE give the old and new number of - elements in the stack, and YYPTR gives the new location of the - stack. Advance YYPTR to a properly aligned location for the next - stack. */ -# define YYSTACK_RELOCATE(Stack) \ - do \ - { \ - YYSIZE_T yynewbytes; \ - YYCOPY (&yyptr->Stack, Stack, yysize); \ - Stack = &yyptr->Stack; \ - yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ - yyptr += yynewbytes / sizeof (*yyptr); \ - } \ - while (YYID (0)) - -#endif +#endif /* !YYCOPY_NEEDED */ /* YYFINAL -- State number of the termination state. */ #define YYFINAL 4 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 2194 +#define YYLAST 2270 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 76 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 142 /* YYNRULES -- Number of rules. */ -#define YYNRULES 349 +#define YYNRULES 351 /* YYNRULES -- Number of states. */ -#define YYNSTATES 663 +#define YYNSTATES 667 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 @@ -512,28 +529,29 @@ static const yytype_uint16 yyprhs[] = 326, 330, 334, 338, 342, 346, 350, 354, 358, 362, 366, 370, 374, 378, 382, 384, 387, 390, 393, 396, 399, 402, 405, 408, 412, 418, 425, 427, 429, 433, - 439, 445, 450, 457, 459, 465, 471, 477, 485, 487, - 488, 492, 494, 499, 501, 506, 508, 512, 514, 516, - 518, 520, 522, 524, 526, 527, 529, 531, 533, 535, - 540, 542, 544, 546, 549, 551, 553, 555, 557, 559, - 563, 565, 567, 569, 572, 574, 576, 578, 580, 584, - 586, 588, 590, 592, 594, 596, 598, 600, 602, 606, - 611, 616, 619, 623, 629, 631, 633, 636, 640, 646, - 650, 656, 660, 664, 670, 679, 685, 694, 700, 701, - 705, 706, 708, 712, 714, 719, 722, 723, 727, 729, - 733, 735, 739, 741, 745, 747, 751, 753, 757, 761, - 764, 769, 773, 779, 785, 787, 791, 793, 796, 798, - 802, 807, 809, 812, 815, 817, 819, 823, 824, 827, - 828, 830, 832, 834, 836, 838, 840, 842, 844, 846, - 847, 852, 854, 857, 860, 863, 866, 869, 872, 874, - 878, 880, 884, 886, 890, 892, 896, 898, 902, 904, - 906, 910, 914, 915, 918, 919, 921, 922, 924, 925, - 927, 928, 930, 931, 933, 934, 936, 937, 939, 940, - 942, 943, 945, 950, 955, 961, 968, 973, 978, 980, - 982, 984, 986, 988, 990, 992, 994, 996, 1000, 1005, - 1011, 1016, 1021, 1024, 1027, 1032, 1036, 1040, 1046, 1050, - 1055, 1059, 1065, 1067, 1068, 1070, 1074, 1076, 1078, 1081, - 1083, 1085, 1091, 1092, 1095, 1097, 1101, 1103, 1107, 1109 + 439, 445, 450, 457, 466, 468, 474, 480, 486, 494, + 496, 497, 501, 503, 508, 510, 515, 517, 521, 523, + 525, 527, 529, 531, 533, 535, 536, 538, 540, 542, + 544, 549, 554, 556, 558, 560, 563, 565, 567, 569, + 571, 573, 577, 579, 581, 583, 586, 588, 590, 592, + 594, 598, 600, 602, 604, 606, 608, 610, 612, 614, + 616, 620, 625, 630, 633, 637, 643, 645, 647, 650, + 654, 660, 664, 670, 674, 678, 684, 693, 699, 708, + 714, 715, 719, 720, 722, 726, 728, 733, 736, 737, + 741, 743, 747, 749, 753, 755, 759, 761, 765, 767, + 771, 775, 778, 783, 787, 793, 799, 801, 805, 807, + 810, 812, 816, 821, 823, 826, 829, 831, 833, 837, + 838, 841, 842, 844, 846, 848, 850, 852, 854, 856, + 858, 860, 861, 866, 868, 871, 874, 877, 880, 883, + 886, 888, 892, 894, 898, 900, 904, 906, 910, 912, + 916, 918, 920, 924, 928, 929, 932, 933, 935, 936, + 938, 939, 941, 942, 944, 945, 947, 948, 950, 951, + 953, 954, 956, 957, 959, 964, 969, 975, 982, 987, + 992, 994, 996, 998, 1000, 1002, 1004, 1006, 1008, 1010, + 1014, 1019, 1025, 1030, 1035, 1038, 1041, 1046, 1050, 1054, + 1060, 1064, 1069, 1073, 1079, 1081, 1082, 1084, 1088, 1090, + 1092, 1095, 1097, 1099, 1105, 1106, 1109, 1111, 1115, 1117, + 1121, 1123 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ @@ -584,113 +602,115 @@ static const yytype_int16 yyrhs[] = 187, 11, 191, 60, -1, 3, -1, 143, -1, 134, 63, 141, -1, 134, 63, 59, 135, 60, -1, 134, 63, 59, 31, 60, -1, 134, 71, 126, 72, -1, - 134, 71, 192, 66, 192, 72, -1, 128, -1, 149, - 59, 126, 191, 60, -1, 150, 137, 130, 189, 68, - -1, 129, 67, 130, 189, 68, -1, 59, 135, 60, - 67, 130, 189, 68, -1, 165, -1, -1, 126, 66, - 133, -1, 126, -1, 67, 130, 189, 68, -1, 126, - -1, 67, 130, 189, 68, -1, 129, -1, 59, 135, - 60, -1, 126, -1, 147, -1, 146, -1, 35, -1, - 67, -1, 141, -1, 141, -1, -1, 138, -1, 24, - -1, 142, -1, 73, -1, 74, 3, 63, 24, -1, - 141, -1, 138, -1, 11, -1, 11, 146, -1, 155, - -1, 161, -1, 153, -1, 154, -1, 152, -1, 59, - 146, 60, -1, 155, -1, 161, -1, 153, -1, 53, - 147, -1, 161, -1, 153, -1, 154, -1, 152, -1, - 59, 146, 60, -1, 161, -1, 153, -1, 153, -1, - 155, -1, 161, -1, 153, -1, 154, -1, 152, -1, - 143, -1, 143, 63, 141, -1, 71, 192, 72, 146, - -1, 71, 11, 72, 146, -1, 8, 148, -1, 8, - 36, 146, -1, 23, 71, 146, 72, 146, -1, 156, - -1, 157, -1, 53, 146, -1, 36, 8, 146, -1, - 29, 137, 170, 190, 68, -1, 29, 137, 68, -1, - 22, 137, 171, 190, 68, -1, 22, 137, 68, -1, - 17, 159, 162, -1, 141, 59, 179, 60, 163, -1, - 59, 179, 60, 141, 59, 179, 60, 163, -1, 200, - 59, 195, 60, 210, -1, 59, 215, 60, 141, 59, - 195, 60, 210, -1, 17, 59, 179, 60, 163, -1, - -1, 67, 183, 68, -1, -1, 151, -1, 59, 179, - 60, -1, 161, -1, 164, 137, 183, 68, -1, 164, - 1, -1, -1, 166, 90, 62, -1, 93, -1, 167, - 62, 93, -1, 95, -1, 168, 62, 95, -1, 97, - -1, 169, 62, 97, -1, 172, -1, 170, 62, 172, - -1, 175, -1, 171, 62, 175, -1, 184, 146, 198, - -1, 174, 198, -1, 59, 174, 60, 198, -1, 53, - 174, 198, -1, 59, 53, 174, 60, 198, -1, 53, - 59, 174, 60, 198, -1, 24, -1, 24, 63, 141, - -1, 173, -1, 138, 176, -1, 173, -1, 59, 173, - 60, -1, 59, 179, 60, 163, -1, 136, -1, 141, - 136, -1, 141, 145, -1, 145, -1, 177, -1, 178, - 75, 177, -1, -1, 178, 191, -1, -1, 100, -1, - 91, -1, 181, -1, 1, -1, 98, -1, 110, -1, - 121, -1, 124, -1, 113, -1, -1, 144, 66, 182, - 180, -1, 15, -1, 6, 140, -1, 10, 140, -1, - 18, 128, -1, 13, 128, -1, 19, 138, -1, 27, - 193, -1, 180, -1, 183, 62, 180, -1, 138, -1, - 184, 75, 138, -1, 139, -1, 185, 75, 139, -1, - 126, -1, 186, 75, 126, -1, 135, -1, 187, 75, - 135, -1, 131, -1, 132, -1, 188, 75, 131, -1, - 188, 75, 132, -1, -1, 188, 191, -1, -1, 62, - -1, -1, 75, -1, -1, 126, -1, -1, 186, -1, - -1, 98, -1, -1, 215, -1, -1, 216, -1, -1, - 217, -1, -1, 3, -1, 21, 24, 3, 62, -1, - 32, 200, 202, 62, -1, 9, 200, 65, 213, 62, - -1, 9, 200, 202, 65, 213, 62, -1, 31, 201, - 202, 62, -1, 17, 160, 162, 62, -1, 142, -1, - 200, -1, 204, -1, 205, -1, 206, -1, 204, -1, - 206, -1, 142, -1, 24, -1, 71, 72, 202, -1, - 71, 3, 72, 202, -1, 23, 71, 202, 72, 202, - -1, 29, 67, 196, 68, -1, 22, 67, 197, 68, - -1, 53, 202, -1, 8, 203, -1, 8, 59, 205, - 60, -1, 8, 36, 202, -1, 36, 8, 202, -1, - 17, 59, 195, 60, 210, -1, 141, 202, 198, -1, - 141, 11, 202, 198, -1, 141, 202, 198, -1, 141, - 59, 195, 60, 210, -1, 202, -1, -1, 211, -1, - 59, 195, 60, -1, 202, -1, 3, -1, 50, 3, - -1, 141, -1, 212, -1, 59, 212, 49, 212, 60, - -1, -1, 214, 199, -1, 207, -1, 215, 75, 207, - -1, 208, -1, 216, 62, 208, -1, 209, -1, 217, - 62, 209, -1 + 134, 71, 192, 66, 192, 72, -1, 134, 71, 192, + 66, 192, 66, 192, 72, -1, 128, -1, 149, 59, + 126, 191, 60, -1, 150, 137, 130, 189, 68, -1, + 129, 67, 130, 189, 68, -1, 59, 135, 60, 67, + 130, 189, 68, -1, 165, -1, -1, 126, 66, 133, + -1, 126, -1, 67, 130, 189, 68, -1, 126, -1, + 67, 130, 189, 68, -1, 129, -1, 59, 135, 60, + -1, 126, -1, 147, -1, 146, -1, 35, -1, 67, + -1, 141, -1, 141, -1, -1, 138, -1, 24, -1, + 142, -1, 73, -1, 74, 3, 63, 24, -1, 74, + 3, 63, 73, -1, 141, -1, 138, -1, 11, -1, + 11, 146, -1, 155, -1, 161, -1, 153, -1, 154, + -1, 152, -1, 59, 146, 60, -1, 155, -1, 161, + -1, 153, -1, 53, 147, -1, 161, -1, 153, -1, + 154, -1, 152, -1, 59, 146, 60, -1, 161, -1, + 153, -1, 153, -1, 155, -1, 161, -1, 153, -1, + 154, -1, 152, -1, 143, -1, 143, 63, 141, -1, + 71, 192, 72, 146, -1, 71, 11, 72, 146, -1, + 8, 148, -1, 8, 36, 146, -1, 23, 71, 146, + 72, 146, -1, 156, -1, 157, -1, 53, 146, -1, + 36, 8, 146, -1, 29, 137, 170, 190, 68, -1, + 29, 137, 68, -1, 22, 137, 171, 190, 68, -1, + 22, 137, 68, -1, 17, 159, 162, -1, 141, 59, + 179, 60, 163, -1, 59, 179, 60, 141, 59, 179, + 60, 163, -1, 200, 59, 195, 60, 210, -1, 59, + 215, 60, 141, 59, 195, 60, 210, -1, 17, 59, + 179, 60, 163, -1, -1, 67, 183, 68, -1, -1, + 151, -1, 59, 179, 60, -1, 161, -1, 164, 137, + 183, 68, -1, 164, 1, -1, -1, 166, 90, 62, + -1, 93, -1, 167, 62, 93, -1, 95, -1, 168, + 62, 95, -1, 97, -1, 169, 62, 97, -1, 172, + -1, 170, 62, 172, -1, 175, -1, 171, 62, 175, + -1, 184, 146, 198, -1, 174, 198, -1, 59, 174, + 60, 198, -1, 53, 174, 198, -1, 59, 53, 174, + 60, 198, -1, 53, 59, 174, 60, 198, -1, 24, + -1, 24, 63, 141, -1, 173, -1, 138, 176, -1, + 173, -1, 59, 173, 60, -1, 59, 179, 60, 163, + -1, 136, -1, 141, 136, -1, 141, 145, -1, 145, + -1, 177, -1, 178, 75, 177, -1, -1, 178, 191, + -1, -1, 100, -1, 91, -1, 181, -1, 1, -1, + 98, -1, 110, -1, 121, -1, 124, -1, 113, -1, + -1, 144, 66, 182, 180, -1, 15, -1, 6, 140, + -1, 10, 140, -1, 18, 128, -1, 13, 128, -1, + 19, 138, -1, 27, 193, -1, 180, -1, 183, 62, + 180, -1, 138, -1, 184, 75, 138, -1, 139, -1, + 185, 75, 139, -1, 126, -1, 186, 75, 126, -1, + 135, -1, 187, 75, 135, -1, 131, -1, 132, -1, + 188, 75, 131, -1, 188, 75, 132, -1, -1, 188, + 191, -1, -1, 62, -1, -1, 75, -1, -1, 126, + -1, -1, 186, -1, -1, 98, -1, -1, 215, -1, + -1, 216, -1, -1, 217, -1, -1, 3, -1, 21, + 24, 3, 62, -1, 32, 200, 202, 62, -1, 9, + 200, 65, 213, 62, -1, 9, 200, 202, 65, 213, + 62, -1, 31, 201, 202, 62, -1, 17, 160, 162, + 62, -1, 142, -1, 200, -1, 204, -1, 205, -1, + 206, -1, 204, -1, 206, -1, 142, -1, 24, -1, + 71, 72, 202, -1, 71, 3, 72, 202, -1, 23, + 71, 202, 72, 202, -1, 29, 67, 196, 68, -1, + 22, 67, 197, 68, -1, 53, 202, -1, 8, 203, + -1, 8, 59, 205, 60, -1, 8, 36, 202, -1, + 36, 8, 202, -1, 17, 59, 195, 60, 210, -1, + 141, 202, 198, -1, 141, 11, 202, 198, -1, 141, + 202, 198, -1, 141, 59, 195, 60, 210, -1, 202, + -1, -1, 211, -1, 59, 195, 60, -1, 202, -1, + 3, -1, 50, 3, -1, 141, -1, 212, -1, 59, + 212, 49, 212, 60, -1, -1, 214, 199, -1, 207, + -1, 215, 75, 207, -1, 208, -1, 216, 62, 208, + -1, 209, -1, 217, 62, 209, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { - 0, 124, 124, 133, 140, 151, 151, 166, 167, 170, - 171, 172, 175, 208, 219, 220, 223, 230, 237, 246, - 260, 261, 268, 268, 281, 285, 286, 290, 295, 301, - 305, 309, 313, 319, 325, 331, 336, 340, 344, 350, - 356, 360, 364, 370, 374, 380, 381, 385, 391, 400, - 406, 424, 429, 441, 457, 462, 469, 489, 507, 516, - 535, 534, 549, 548, 579, 582, 589, 588, 599, 605, - 614, 625, 631, 634, 642, 641, 652, 658, 670, 674, - 679, 669, 700, 699, 712, 715, 721, 724, 736, 740, - 735, 758, 757, 773, 774, 778, 782, 786, 790, 794, - 798, 802, 806, 810, 814, 818, 822, 826, 830, 834, - 838, 842, 846, 851, 857, 858, 862, 873, 877, 881, - 885, 890, 894, 904, 908, 913, 921, 925, 926, 937, - 941, 945, 949, 953, 954, 960, 967, 973, 980, 983, - 990, 996, 1013, 1020, 1021, 1028, 1029, 1048, 1049, 1052, - 1055, 1059, 1070, 1079, 1085, 1088, 1091, 1098, 1099, 1105, - 1120, 1128, 1140, 1145, 1151, 1152, 1153, 1154, 1155, 1156, - 1162, 1163, 1164, 1165, 1171, 1172, 1173, 1174, 1175, 1181, - 1182, 1185, 1188, 1189, 1190, 1191, 1192, 1195, 1196, 1209, - 1213, 1218, 1223, 1228, 1232, 1233, 1236, 1242, 1249, 1255, - 1262, 1268, 1279, 1293, 1322, 1362, 1387, 1405, 1414, 1417, - 1425, 1429, 1433, 1440, 1446, 1451, 1463, 1466, 1476, 1477, - 1483, 1484, 1490, 1494, 1500, 1501, 1507, 1511, 1517, 1540, - 1545, 1551, 1557, 1564, 1573, 1582, 1597, 1603, 1608, 1612, - 1619, 1632, 1633, 1639, 1645, 1648, 1652, 1658, 1661, 1670, - 1673, 1674, 1678, 1679, 1685, 1686, 1687, 1688, 1689, 1691, - 1690, 1705, 1710, 1714, 1718, 1722, 1726, 1731, 1750, 1756, - 1764, 1768, 1774, 1778, 1784, 1788, 1794, 1798, 1807, 1811, - 1815, 1819, 1825, 1828, 1836, 1837, 1839, 1840, 1843, 1846, - 1849, 1852, 1855, 1858, 1861, 1864, 1867, 1870, 1873, 1876, - 1879, 1882, 1888, 1892, 1896, 1900, 1904, 1908, 1928, 1935, - 1946, 1947, 1948, 1951, 1952, 1955, 1959, 1969, 1973, 1977, - 1981, 1985, 1989, 1993, 1999, 2005, 2013, 2021, 2027, 2034, - 2050, 2068, 2072, 2078, 2081, 2084, 2088, 2098, 2102, 2117, - 2125, 2126, 2138, 2139, 2142, 2146, 2152, 2156, 2162, 2166 + 0, 124, 124, 133, 139, 150, 150, 165, 166, 169, + 170, 171, 174, 211, 222, 223, 226, 233, 240, 249, + 263, 264, 271, 271, 284, 288, 289, 293, 298, 304, + 308, 312, 316, 322, 328, 334, 339, 343, 347, 353, + 359, 363, 367, 373, 377, 383, 384, 388, 394, 403, + 409, 427, 432, 444, 460, 465, 472, 492, 510, 519, + 538, 537, 552, 551, 582, 585, 592, 591, 602, 608, + 617, 628, 634, 637, 645, 644, 655, 661, 673, 677, + 682, 672, 703, 702, 715, 718, 724, 727, 739, 743, + 738, 761, 760, 776, 777, 781, 785, 789, 793, 797, + 801, 805, 809, 813, 817, 821, 825, 829, 833, 837, + 841, 845, 849, 854, 860, 861, 865, 876, 880, 884, + 888, 893, 897, 907, 911, 916, 924, 928, 929, 940, + 944, 948, 952, 956, 964, 965, 971, 978, 984, 991, + 994, 1001, 1007, 1024, 1031, 1032, 1039, 1040, 1059, 1060, + 1063, 1066, 1070, 1081, 1090, 1096, 1099, 1102, 1109, 1110, + 1116, 1129, 1144, 1152, 1164, 1169, 1175, 1176, 1177, 1178, + 1179, 1180, 1186, 1187, 1188, 1189, 1195, 1196, 1197, 1198, + 1199, 1205, 1206, 1209, 1212, 1213, 1214, 1215, 1216, 1219, + 1220, 1233, 1237, 1242, 1247, 1252, 1256, 1257, 1260, 1266, + 1273, 1279, 1286, 1292, 1303, 1317, 1346, 1386, 1411, 1429, + 1438, 1441, 1449, 1453, 1457, 1464, 1470, 1475, 1487, 1490, + 1500, 1501, 1507, 1508, 1514, 1518, 1524, 1525, 1531, 1535, + 1541, 1564, 1569, 1575, 1581, 1588, 1597, 1606, 1621, 1627, + 1632, 1636, 1643, 1656, 1657, 1663, 1669, 1672, 1676, 1682, + 1685, 1694, 1697, 1698, 1702, 1703, 1709, 1710, 1711, 1712, + 1713, 1715, 1714, 1729, 1734, 1738, 1742, 1746, 1750, 1755, + 1774, 1780, 1788, 1792, 1798, 1802, 1808, 1812, 1818, 1822, + 1831, 1835, 1839, 1843, 1849, 1852, 1860, 1861, 1863, 1864, + 1867, 1870, 1873, 1876, 1879, 1882, 1885, 1888, 1891, 1894, + 1897, 1900, 1903, 1906, 1912, 1916, 1920, 1924, 1928, 1932, + 1952, 1959, 1970, 1971, 1972, 1975, 1976, 1979, 1983, 1993, + 1997, 2001, 2005, 2009, 2013, 2017, 2023, 2029, 2037, 2045, + 2051, 2058, 2074, 2096, 2100, 2106, 2109, 2112, 2116, 2126, + 2130, 2145, 2153, 2154, 2166, 2167, 2170, 2174, 2180, 2184, + 2190, 2194 }; #endif @@ -709,16 +729,16 @@ const char *yytname[] = "'/'", "'%'", "'&'", "NotPackage", "NotParen", "'('", "')'", "PreferToRightParen", "';'", "'.'", "'$'", "'='", "':'", "'{'", "'}'", "'!'", "'~'", "'['", "']'", "'?'", "'@'", "','", "$accept", "file", - "package", "loadsys", "@1", "imports", "import", "import_stmt", + "package", "loadsys", "$@1", "imports", "import", "import_stmt", "import_stmt_list", "import_here", "import_package", "import_safety", - "import_there", "@2", "xdcl", "common_dcl", "lconst", "vardcl", + "import_there", "$@2", "xdcl", "common_dcl", "lconst", "vardcl", "constdcl", "constdcl1", "typedclname", "typedcl", "simple_stmt", "case", - "compound_stmt", "@3", "caseblock", "@4", "caseblock_list", "loop_body", - "@5", "range_stmt", "for_header", "for_body", "for_stmt", "@6", - "if_header", "if_stmt", "@7", "@8", "@9", "elseif", "@10", "elseif_list", - "else", "switch_stmt", "@11", "@12", "select_stmt", "@13", "expr", - "uexpr", "pseudocall", "pexpr_no_paren", "start_complit", "keyval", - "bare_complitexpr", "complitexpr", "pexpr", "expr_or_type", + "compound_stmt", "$@3", "caseblock", "$@4", "caseblock_list", + "loop_body", "$@5", "range_stmt", "for_header", "for_body", "for_stmt", + "$@6", "if_header", "if_stmt", "$@7", "$@8", "$@9", "elseif", "$@10", + "elseif_list", "else", "switch_stmt", "$@11", "$@12", "select_stmt", + "$@13", "expr", "uexpr", "pseudocall", "pexpr_no_paren", "start_complit", + "keyval", "bare_complitexpr", "complitexpr", "pexpr", "expr_or_type", "name_or_type", "lbrace", "new_name", "dcl_name", "onew_name", "sym", "hidden_importsym", "name", "labelname", "dotdotdot", "ntype", "non_expr_type", "non_recvchantype", "convtype", "comptype", @@ -728,7 +748,7 @@ const char *yytname[] = "vardcl_list", "constdcl_list", "typedcl_list", "structdcl_list", "interfacedcl_list", "structdcl", "packname", "embed", "interfacedcl", "indcl", "arg_type", "arg_type_list", "oarg_type_list_ocomma", "stmt", - "non_dcl_stmt", "@14", "stmt_list", "new_name_list", "dcl_name_list", + "non_dcl_stmt", "$@14", "stmt_list", "new_name_list", "dcl_name_list", "expr_list", "expr_or_type_list", "keyval_list", "braced_keyval_list", "osemi", "ocomma", "oexpr", "oexpr_list", "osimple_stmt", "ohidden_funarg_list", "ohidden_structdcl_list", @@ -775,28 +795,29 @@ static const yytype_uint8 yyr1[] = 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 127, 127, 127, 127, 127, 127, 127, 127, 127, 128, 128, 128, 129, 129, 129, 129, - 129, 129, 129, 129, 129, 129, 129, 129, 129, 130, - 131, 132, 132, 133, 133, 134, 134, 135, 135, 136, - 137, 137, 138, 139, 140, 140, 141, 141, 141, 142, - 143, 144, 145, 145, 146, 146, 146, 146, 146, 146, - 147, 147, 147, 147, 148, 148, 148, 148, 148, 149, - 149, 150, 151, 151, 151, 151, 151, 152, 152, 153, - 153, 153, 153, 153, 153, 153, 154, 155, 156, 156, - 157, 157, 158, 159, 159, 160, 160, 161, 162, 162, - 163, 163, 163, 164, 165, 165, 166, 166, 167, 167, - 168, 168, 169, 169, 170, 170, 171, 171, 172, 172, - 172, 172, 172, 172, 173, 173, 174, 175, 175, 175, - 176, 177, 177, 177, 177, 178, 178, 179, 179, 180, - 180, 180, 180, 180, 181, 181, 181, 181, 181, 182, - 181, 181, 181, 181, 181, 181, 181, 181, 183, 183, - 184, 184, 185, 185, 186, 186, 187, 187, 188, 188, - 188, 188, 189, 189, 190, 190, 191, 191, 192, 192, - 193, 193, 194, 194, 195, 195, 196, 196, 197, 197, - 198, 198, 199, 199, 199, 199, 199, 199, 200, 201, - 202, 202, 202, 203, 203, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 205, 206, 207, 207, - 208, 209, 209, 210, 210, 211, 211, 212, 212, 212, - 213, 213, 214, 214, 215, 215, 216, 216, 217, 217 + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 130, 131, 132, 132, 133, 133, 134, 134, 135, 135, + 136, 137, 137, 138, 139, 140, 140, 141, 141, 141, + 142, 142, 143, 144, 145, 145, 146, 146, 146, 146, + 146, 146, 147, 147, 147, 147, 148, 148, 148, 148, + 148, 149, 149, 150, 151, 151, 151, 151, 151, 152, + 152, 153, 153, 153, 153, 153, 153, 153, 154, 155, + 156, 156, 157, 157, 158, 159, 159, 160, 160, 161, + 162, 162, 163, 163, 163, 164, 165, 165, 166, 166, + 167, 167, 168, 168, 169, 169, 170, 170, 171, 171, + 172, 172, 172, 172, 172, 172, 173, 173, 174, 175, + 175, 175, 176, 177, 177, 177, 177, 178, 178, 179, + 179, 180, 180, 180, 180, 180, 181, 181, 181, 181, + 181, 182, 181, 181, 181, 181, 181, 181, 181, 181, + 183, 183, 184, 184, 185, 185, 186, 186, 187, 187, + 188, 188, 188, 188, 189, 189, 190, 190, 191, 191, + 192, 192, 193, 193, 194, 194, 195, 195, 196, 196, + 197, 197, 198, 198, 199, 199, 199, 199, 199, 199, + 200, 201, 202, 202, 202, 203, 203, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 205, 206, + 207, 207, 208, 209, 209, 210, 210, 211, 211, 212, + 212, 212, 213, 213, 214, 214, 215, 215, 216, 216, + 217, 217 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ @@ -815,669 +836,691 @@ static const yytype_uint8 yyr2[] = 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 5, 6, 1, 1, 3, 5, - 5, 4, 6, 1, 5, 5, 5, 7, 1, 0, - 3, 1, 4, 1, 4, 1, 3, 1, 1, 1, - 1, 1, 1, 1, 0, 1, 1, 1, 1, 4, - 1, 1, 1, 2, 1, 1, 1, 1, 1, 3, - 1, 1, 1, 2, 1, 1, 1, 1, 3, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 3, 4, - 4, 2, 3, 5, 1, 1, 2, 3, 5, 3, - 5, 3, 3, 5, 8, 5, 8, 5, 0, 3, - 0, 1, 3, 1, 4, 2, 0, 3, 1, 3, - 1, 3, 1, 3, 1, 3, 1, 3, 3, 2, - 4, 3, 5, 5, 1, 3, 1, 2, 1, 3, - 4, 1, 2, 2, 1, 1, 3, 0, 2, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, - 4, 1, 2, 2, 2, 2, 2, 2, 1, 3, - 1, 3, 1, 3, 1, 3, 1, 3, 1, 1, - 3, 3, 0, 2, 0, 1, 0, 1, 0, 1, + 5, 4, 6, 8, 1, 5, 5, 5, 7, 1, + 0, 3, 1, 4, 1, 4, 1, 3, 1, 1, + 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, + 4, 4, 1, 1, 1, 2, 1, 1, 1, 1, + 1, 3, 1, 1, 1, 2, 1, 1, 1, 1, + 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 3, 4, 4, 2, 3, 5, 1, 1, 2, 3, + 5, 3, 5, 3, 3, 5, 8, 5, 8, 5, + 0, 3, 0, 1, 3, 1, 4, 2, 0, 3, + 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, + 3, 2, 4, 3, 5, 5, 1, 3, 1, 2, + 1, 3, 4, 1, 2, 2, 1, 1, 3, 0, + 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 4, 1, 2, 2, 2, 2, 2, 2, + 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, + 1, 1, 3, 3, 0, 2, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, - 0, 1, 4, 4, 5, 6, 4, 4, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 3, 4, 5, - 4, 4, 2, 2, 4, 3, 3, 5, 3, 4, - 3, 5, 1, 0, 1, 3, 1, 1, 2, 1, - 1, 5, 0, 2, 1, 3, 1, 3, 1, 3 + 0, 1, 0, 1, 4, 4, 5, 6, 4, 4, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, + 4, 5, 4, 4, 2, 2, 4, 3, 3, 5, + 3, 4, 3, 5, 1, 0, 1, 3, 1, 1, + 2, 1, 1, 5, 0, 2, 1, 3, 1, 3, + 1, 3 }; -/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state - STATE-NUM when YYTABLE doesn't specify something else to do. Zero +/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const yytype_uint16 yydefact[] = { - 5, 0, 3, 0, 1, 0, 7, 0, 22, 156, - 158, 0, 0, 157, 216, 20, 6, 342, 0, 4, + 5, 0, 3, 0, 1, 0, 7, 0, 22, 157, + 159, 0, 0, 158, 218, 20, 6, 344, 0, 4, 0, 0, 0, 21, 0, 0, 0, 16, 0, 0, - 9, 22, 0, 8, 28, 126, 154, 0, 39, 154, - 0, 261, 74, 0, 0, 0, 78, 0, 0, 290, + 9, 22, 0, 8, 28, 126, 155, 0, 39, 155, + 0, 263, 74, 0, 0, 0, 78, 0, 0, 292, 91, 0, 88, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 288, 0, 25, 0, 254, 255, - 258, 256, 257, 50, 93, 133, 145, 114, 161, 160, - 127, 0, 0, 0, 181, 194, 195, 26, 213, 0, - 138, 27, 0, 19, 0, 0, 0, 0, 0, 0, - 343, 159, 11, 14, 284, 18, 22, 13, 17, 155, - 262, 152, 0, 0, 0, 0, 160, 187, 191, 177, - 175, 176, 174, 263, 133, 0, 292, 247, 0, 208, - 133, 266, 292, 150, 151, 0, 0, 274, 291, 267, - 0, 0, 292, 0, 0, 36, 48, 0, 29, 272, - 153, 0, 122, 117, 118, 121, 115, 116, 0, 0, - 147, 0, 148, 172, 170, 171, 119, 120, 0, 289, - 0, 217, 0, 32, 0, 0, 0, 0, 0, 55, - 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 139, 0, - 0, 288, 259, 0, 139, 215, 0, 0, 0, 0, - 308, 0, 0, 208, 0, 0, 309, 0, 0, 23, - 285, 0, 12, 247, 0, 0, 192, 168, 166, 167, - 164, 165, 196, 0, 0, 293, 72, 0, 75, 0, - 71, 162, 241, 160, 244, 149, 245, 286, 0, 247, - 0, 202, 79, 76, 156, 0, 201, 0, 284, 238, - 226, 0, 64, 0, 0, 199, 270, 284, 224, 236, - 300, 0, 89, 38, 222, 284, 49, 31, 218, 284, - 0, 0, 40, 0, 173, 146, 0, 0, 35, 284, - 0, 0, 51, 95, 110, 113, 96, 100, 101, 99, - 111, 98, 97, 94, 112, 102, 103, 104, 105, 106, - 107, 108, 109, 282, 123, 276, 286, 0, 128, 289, - 0, 0, 286, 282, 253, 60, 251, 250, 268, 252, - 0, 53, 52, 275, 0, 0, 0, 0, 316, 0, - 0, 0, 0, 0, 315, 0, 310, 311, 312, 0, - 344, 0, 0, 294, 0, 0, 0, 15, 10, 0, - 0, 0, 178, 188, 66, 73, 0, 0, 292, 163, - 242, 243, 287, 248, 210, 0, 0, 0, 292, 0, - 234, 0, 247, 237, 285, 0, 0, 0, 0, 300, - 0, 0, 285, 0, 301, 229, 0, 300, 0, 285, - 0, 285, 0, 42, 273, 0, 0, 0, 197, 168, - 166, 167, 165, 139, 190, 189, 285, 0, 44, 0, - 139, 141, 278, 279, 286, 0, 286, 287, 0, 0, - 0, 131, 288, 260, 287, 0, 0, 0, 0, 214, - 0, 0, 323, 313, 314, 294, 298, 0, 296, 0, - 322, 337, 0, 0, 339, 340, 0, 0, 0, 0, - 0, 300, 0, 0, 307, 0, 295, 302, 306, 303, - 210, 169, 0, 0, 0, 0, 246, 247, 160, 211, - 186, 184, 185, 182, 183, 207, 210, 209, 80, 77, - 235, 239, 0, 227, 200, 193, 0, 0, 92, 62, - 65, 0, 231, 0, 300, 225, 198, 271, 228, 64, - 223, 37, 219, 30, 41, 0, 282, 45, 220, 284, - 47, 33, 43, 282, 0, 287, 283, 136, 0, 277, - 124, 130, 129, 0, 134, 135, 0, 269, 325, 0, - 0, 316, 0, 315, 0, 332, 348, 299, 0, 0, - 0, 346, 297, 326, 338, 0, 304, 0, 317, 0, - 300, 328, 0, 345, 333, 0, 69, 68, 292, 0, - 247, 203, 84, 210, 0, 59, 0, 300, 300, 230, - 0, 169, 0, 285, 0, 46, 0, 139, 143, 140, - 280, 281, 125, 132, 61, 324, 333, 294, 321, 0, - 0, 300, 320, 0, 0, 318, 305, 329, 294, 294, - 336, 205, 334, 67, 70, 212, 0, 86, 240, 0, - 0, 56, 0, 63, 233, 232, 90, 137, 221, 34, - 142, 282, 327, 0, 349, 319, 330, 347, 0, 0, - 0, 210, 0, 85, 81, 0, 0, 0, 333, 341, - 333, 335, 204, 82, 87, 58, 57, 144, 331, 206, - 292, 0, 83 + 0, 0, 0, 0, 290, 0, 25, 0, 256, 257, + 260, 258, 259, 50, 93, 134, 146, 114, 163, 162, + 127, 0, 0, 0, 183, 196, 197, 26, 215, 0, + 139, 27, 0, 19, 0, 0, 0, 0, 0, 0, + 345, 160, 161, 11, 14, 286, 18, 22, 13, 17, + 156, 264, 153, 0, 0, 0, 0, 162, 189, 193, + 179, 177, 178, 176, 265, 134, 0, 294, 249, 0, + 210, 134, 268, 294, 151, 152, 0, 0, 276, 293, + 269, 0, 0, 294, 0, 0, 36, 48, 0, 29, + 274, 154, 0, 122, 117, 118, 121, 115, 116, 0, + 0, 148, 0, 149, 174, 172, 173, 119, 120, 0, + 291, 0, 219, 0, 32, 0, 0, 0, 0, 0, + 55, 0, 0, 0, 54, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, + 0, 0, 290, 261, 0, 140, 217, 0, 0, 0, + 0, 310, 0, 0, 210, 0, 0, 311, 0, 0, + 23, 287, 0, 12, 249, 0, 0, 194, 170, 168, + 169, 166, 167, 198, 0, 0, 295, 72, 0, 75, + 0, 71, 164, 243, 162, 246, 150, 247, 288, 0, + 249, 0, 204, 79, 76, 157, 0, 203, 0, 286, + 240, 228, 0, 64, 0, 0, 201, 272, 286, 226, + 238, 302, 0, 89, 38, 224, 286, 49, 31, 220, + 286, 0, 0, 40, 0, 175, 147, 0, 0, 35, + 286, 0, 0, 51, 95, 110, 113, 96, 100, 101, + 99, 111, 98, 97, 94, 112, 102, 103, 104, 105, + 106, 107, 108, 109, 284, 123, 278, 288, 0, 128, + 291, 0, 0, 288, 284, 255, 60, 253, 252, 270, + 254, 0, 53, 52, 277, 0, 0, 0, 0, 318, + 0, 0, 0, 0, 0, 317, 0, 312, 313, 314, + 0, 346, 0, 0, 296, 0, 0, 0, 15, 10, + 0, 0, 0, 180, 190, 66, 73, 0, 0, 294, + 165, 244, 245, 289, 250, 212, 0, 0, 0, 294, + 0, 236, 0, 249, 239, 287, 0, 0, 0, 0, + 302, 0, 0, 287, 0, 303, 231, 0, 302, 0, + 287, 0, 287, 0, 42, 275, 0, 0, 0, 199, + 170, 168, 169, 167, 140, 192, 191, 287, 0, 44, + 0, 140, 142, 280, 281, 288, 0, 288, 289, 0, + 0, 0, 131, 290, 262, 289, 0, 0, 0, 0, + 216, 0, 0, 325, 315, 316, 296, 300, 0, 298, + 0, 324, 339, 0, 0, 341, 342, 0, 0, 0, + 0, 0, 302, 0, 0, 309, 0, 297, 304, 308, + 305, 212, 171, 0, 0, 0, 0, 248, 249, 162, + 213, 188, 186, 187, 184, 185, 209, 212, 211, 80, + 77, 237, 241, 0, 229, 202, 195, 0, 0, 92, + 62, 65, 0, 233, 0, 302, 227, 200, 273, 230, + 64, 225, 37, 221, 30, 41, 0, 284, 45, 222, + 286, 47, 33, 43, 284, 0, 289, 285, 137, 0, + 279, 124, 130, 129, 0, 135, 136, 0, 271, 327, + 0, 0, 318, 0, 317, 0, 334, 350, 301, 0, + 0, 0, 348, 299, 328, 340, 0, 306, 0, 319, + 0, 302, 330, 0, 347, 335, 0, 69, 68, 294, + 0, 249, 205, 84, 212, 0, 59, 0, 302, 302, + 232, 0, 171, 0, 287, 0, 46, 0, 140, 144, + 141, 282, 283, 125, 290, 132, 61, 326, 335, 296, + 323, 0, 0, 302, 322, 0, 0, 320, 307, 331, + 296, 296, 338, 207, 336, 67, 70, 214, 0, 86, + 242, 0, 0, 56, 0, 63, 235, 234, 90, 138, + 223, 34, 143, 284, 0, 329, 0, 351, 321, 332, + 349, 0, 0, 0, 212, 0, 85, 81, 0, 0, + 0, 133, 335, 343, 335, 337, 206, 82, 87, 58, + 57, 145, 333, 208, 294, 0, 83 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { - -1, 1, 6, 2, 3, 14, 21, 30, 104, 31, - 8, 24, 16, 17, 65, 326, 67, 148, 517, 518, - 144, 145, 68, 499, 327, 437, 500, 576, 387, 365, - 472, 236, 237, 238, 69, 126, 252, 70, 132, 377, - 572, 643, 660, 617, 644, 71, 142, 398, 72, 140, - 73, 74, 75, 76, 313, 422, 423, 589, 77, 315, - 242, 135, 78, 149, 110, 116, 13, 80, 81, 244, - 245, 162, 118, 82, 83, 479, 227, 84, 229, 230, - 85, 86, 87, 129, 213, 88, 251, 485, 89, 90, - 22, 279, 519, 275, 267, 258, 268, 269, 270, 260, - 383, 246, 247, 248, 328, 329, 321, 330, 271, 151, - 92, 316, 424, 425, 221, 373, 170, 139, 253, 465, - 550, 544, 395, 100, 211, 217, 610, 442, 346, 347, - 348, 350, 551, 546, 611, 612, 455, 456, 25, 466, - 552, 547 + -1, 1, 6, 2, 3, 14, 21, 30, 105, 31, + 8, 24, 16, 17, 65, 327, 67, 149, 518, 519, + 145, 146, 68, 500, 328, 438, 501, 577, 388, 366, + 473, 237, 238, 239, 69, 127, 253, 70, 133, 378, + 573, 646, 664, 619, 647, 71, 143, 399, 72, 141, + 73, 74, 75, 76, 314, 423, 424, 590, 77, 316, + 243, 136, 78, 150, 111, 117, 13, 80, 81, 245, + 246, 163, 119, 82, 83, 480, 228, 84, 230, 231, + 85, 86, 87, 130, 214, 88, 252, 486, 89, 90, + 22, 280, 520, 276, 268, 259, 269, 270, 271, 261, + 384, 247, 248, 249, 329, 330, 322, 331, 272, 152, + 92, 317, 425, 426, 222, 374, 171, 140, 254, 466, + 551, 545, 396, 100, 212, 218, 612, 443, 347, 348, + 349, 351, 552, 547, 613, 614, 456, 457, 25, 467, + 553, 548 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ -#define YYPACT_NINF -485 +#define YYPACT_NINF -474 static const yytype_int16 yypact[] = { - -485, 67, 35, 55, -485, 44, -485, 64, -485, -485, - -485, 96, 38, -485, 77, 85, -485, -485, 66, -485, - 34, 84, 1059, -485, 86, 294, 147, -485, 165, 210, - -485, 55, 221, -485, -485, -485, 44, 1762, -485, 44, - 290, -485, -485, 442, 290, 44, -485, 80, 69, 1608, - -485, 80, -485, 450, 452, 1608, 1608, 1608, 1608, 1608, - 1608, 1651, 1608, 1608, 920, 157, -485, 460, -485, -485, - -485, -485, -485, 718, -485, -485, 167, 344, -485, 176, - -485, 180, 193, 80, 206, -485, -485, -485, 218, 91, - -485, -485, 76, -485, 205, 10, 260, 205, 205, 223, - -485, -485, -485, -485, 230, -485, -485, -485, -485, -485, - -485, -485, 237, 1770, 1770, 1770, -485, 236, -485, -485, - -485, -485, -485, -485, 220, 344, 1608, 990, 241, 235, - 262, -485, 1608, -485, -485, 405, 1770, 2090, 254, -485, - 297, 444, 1608, 61, 1770, -485, -485, 271, -485, -485, - -485, 671, -485, -485, -485, -485, -485, -485, 1694, 1651, - 2090, 291, -485, 181, -485, 60, -485, -485, 287, 2090, - 301, -485, 496, -485, 912, 1608, 1608, 1608, 1608, -485, - 1608, 1608, 1608, -485, 1608, 1608, 1608, 1608, 1608, 1608, - 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, -485, 1290, - 468, 1608, -485, 1608, -485, -485, 1221, 1608, 1608, 1608, - -485, 573, 44, 235, 275, 347, -485, 1301, 1301, -485, - 113, 302, -485, 990, 358, 1770, -485, -485, -485, -485, - -485, -485, -485, 316, 44, -485, -485, 340, -485, 78, - 318, 1770, -485, 990, -485, -485, -485, 307, 325, 990, - 1221, -485, -485, 324, 117, 365, -485, 343, 337, -485, - -485, 333, -485, 32, 23, -485, -485, 350, -485, -485, - 406, 1737, -485, -485, -485, 351, -485, -485, -485, 352, - 1608, 44, 354, 1796, -485, 353, 1770, 1770, -485, 359, - 1608, 357, 2090, 1928, -485, 2114, 1212, 1212, 1212, 1212, - -485, 1212, 1212, 2138, -485, 566, 566, 566, 566, -485, - -485, -485, -485, 1345, -485, -485, 31, 1400, -485, 1988, - 360, 1147, 1955, 1345, -485, -485, -485, -485, -485, -485, - 95, 254, 254, 2090, 1857, 368, 361, 371, -485, 363, - 427, 1301, 247, 51, -485, 374, -485, -485, -485, 1890, - -485, 36, 382, 44, 384, 385, 387, -485, -485, 391, - 1770, 395, -485, -485, -485, -485, 1455, 1510, 1608, -485, - -485, -485, 990, -485, 1823, 399, 135, 340, 1608, 44, - 397, 403, 990, -485, 542, 407, 1770, 278, 365, 406, - 365, 411, 364, 413, -485, -485, 44, 406, 430, 44, - 423, 44, 425, 254, -485, 1608, 1849, 1770, -485, 216, - 219, 274, 288, -485, -485, -485, 44, 426, 254, 1608, - -485, 2018, -485, -485, 414, 422, 416, 1651, 433, 434, - 436, -485, 1608, -485, -485, 439, 437, 1221, 1147, -485, - 1301, 466, -485, -485, -485, 44, 1882, 1301, 44, 1301, - -485, -485, 504, 207, -485, -485, 446, 438, 1301, 247, - 1301, 406, 44, 44, -485, 453, 455, -485, -485, -485, - 1823, -485, 1221, 1608, 1608, 467, -485, 990, 472, -485, - -485, -485, -485, -485, -485, -485, 1823, -485, -485, -485, - -485, -485, 475, -485, -485, -485, 1651, 470, -485, -485, - -485, 490, -485, 493, 406, -485, -485, -485, -485, -485, - -485, -485, -485, -485, 254, 495, 1345, -485, -485, 498, - 912, -485, 254, 1345, 1553, 1345, -485, -485, 497, -485, - -485, -485, -485, 486, -485, -485, 143, -485, -485, 501, - 502, 473, 508, 513, 505, -485, -485, 515, 503, 1301, - 511, -485, 518, -485, -485, 533, -485, 1301, -485, 522, - 406, -485, 526, -485, 1916, 144, 2090, 2090, 1608, 527, - 990, -485, -485, 1823, 39, -485, 1147, 406, 406, -485, - 315, 293, 521, 44, 548, 357, 525, -485, 2090, -485, - -485, -485, -485, -485, -485, -485, 1916, 44, -485, 1882, - 1301, 406, -485, 44, 207, -485, -485, -485, 44, 44, - -485, -485, -485, -485, -485, -485, 551, 572, -485, 1608, - 1608, -485, 1651, 550, -485, -485, -485, -485, -485, -485, - -485, 1345, -485, 558, -485, -485, -485, -485, 563, 564, - 565, 1823, 46, -485, -485, 2042, 2066, 559, 1916, -485, - 1916, -485, -485, -485, -485, -485, -485, -485, -485, -485, - 1608, 340, -485 + -474, 48, 28, 35, -474, 258, -474, 37, -474, -474, + -474, 61, 12, -474, 85, 107, -474, -474, 70, -474, + 156, 82, 1059, -474, 122, 328, 22, -474, 56, 199, + -474, 35, 211, -474, -474, -474, 258, 767, -474, 258, + 459, -474, -474, 152, 459, 258, -474, 23, 145, 1650, + -474, 23, -474, 294, 359, 1650, 1650, 1650, 1650, 1650, + 1650, 1693, 1650, 1650, 1289, 159, -474, 412, -474, -474, + -474, -474, -474, 939, -474, -474, 157, 302, -474, 168, + -474, 175, 184, 23, 204, -474, -474, -474, 219, 54, + -474, -474, 47, -474, 227, -12, 269, 227, 227, 239, + -474, -474, -474, -474, -474, 240, -474, -474, -474, -474, + -474, -474, -474, 250, 1813, 1813, 1813, -474, 259, -474, + -474, -474, -474, -474, -474, 64, 302, 1650, 1805, 262, + 260, 174, -474, 1650, -474, -474, 221, 1813, 2166, 255, + -474, 290, 237, 1650, 304, 1813, -474, -474, 420, -474, + -474, -474, 580, -474, -474, -474, -474, -474, -474, 1736, + 1693, 2166, 280, -474, 253, -474, 50, -474, -474, 275, + 2166, 285, -474, 430, -474, 612, 1650, 1650, 1650, 1650, + -474, 1650, 1650, 1650, -474, 1650, 1650, 1650, 1650, 1650, + 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, -474, + 1332, 428, 1650, -474, 1650, -474, -474, 1234, 1650, 1650, + 1650, -474, 763, 258, 260, 293, 369, -474, 1992, 1992, + -474, 51, 326, -474, 1805, 392, 1813, -474, -474, -474, + -474, -474, -474, -474, 341, 258, -474, -474, 371, -474, + 89, 342, 1813, -474, 1805, -474, -474, -474, 335, 360, + 1805, 1234, -474, -474, 357, 99, 399, -474, 365, 380, + -474, -474, 377, -474, 173, 151, -474, -474, 381, -474, + -474, 456, 1779, -474, -474, -474, 401, -474, -474, -474, + 404, 1650, 258, 366, 1838, -474, 405, 1813, 1813, -474, + 407, 1650, 410, 2166, 650, -474, 2190, 877, 877, 877, + 877, -474, 877, 877, 2214, -474, 461, 461, 461, 461, + -474, -474, -474, -474, 1387, -474, -474, 52, 1442, -474, + 2064, 411, 1160, 2031, 1387, -474, -474, -474, -474, -474, + -474, 19, 255, 255, 2166, 1905, 447, 441, 439, -474, + 444, 505, 1992, 225, 27, -474, 454, -474, -474, -474, + 1931, -474, 125, 458, 258, 460, 465, 466, -474, -474, + 463, 1813, 480, -474, -474, -474, -474, 1497, 1552, 1650, + -474, -474, -474, 1805, -474, 1872, 484, 24, 371, 1650, + 258, 485, 487, 1805, -474, 472, 481, 1813, 81, 399, + 456, 399, 490, 289, 483, -474, -474, 258, 456, 519, + 258, 495, 258, 496, 255, -474, 1650, 1897, 1813, -474, + 321, 349, 350, 354, -474, -474, -474, 258, 497, 255, + 1650, -474, 2094, -474, -474, 488, 491, 489, 1693, 498, + 500, 502, -474, 1650, -474, -474, 506, 503, 1234, 1160, + -474, 1992, 534, -474, -474, -474, 258, 1958, 1992, 258, + 1992, -474, -474, 565, 149, -474, -474, 510, 504, 1992, + 225, 1992, 456, 258, 258, -474, 514, 507, -474, -474, + -474, 1872, -474, 1234, 1650, 1650, 515, -474, 1805, 520, + -474, -474, -474, -474, -474, -474, -474, 1872, -474, -474, + -474, -474, -474, 518, -474, -474, -474, 1693, 517, -474, + -474, -474, 524, -474, 525, 456, -474, -474, -474, -474, + -474, -474, -474, -474, -474, 255, 526, 1387, -474, -474, + 527, 612, -474, 255, 1387, 1595, 1387, -474, -474, 530, + -474, -474, -474, -474, 116, -474, -474, 141, -474, -474, + 539, 540, 521, 542, 546, 538, -474, -474, 548, 543, + 1992, 549, -474, 552, -474, -474, 562, -474, 1992, -474, + 556, 456, -474, 560, -474, 1984, 238, 2166, 2166, 1650, + 561, 1805, -474, -474, 1872, 32, -474, 1160, 456, 456, + -474, 186, 370, 554, 258, 563, 410, 557, -474, 2166, + -474, -474, -474, -474, 1650, -474, -474, -474, 1984, 258, + -474, 1958, 1992, 456, -474, 258, 149, -474, -474, -474, + 258, 258, -474, -474, -474, -474, -474, -474, 564, 613, + -474, 1650, 1650, -474, 1693, 566, -474, -474, -474, -474, + -474, -474, -474, 1387, 558, -474, 571, -474, -474, -474, + -474, 577, 582, 583, 1872, 36, -474, -474, 2118, 2142, + 572, -474, 1984, -474, 1984, -474, -474, -474, -474, -474, + -474, -474, -474, -474, 1650, 371, -474 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { - -485, -485, -485, -485, -485, -485, -485, -6, -485, -485, - 597, -485, -3, -485, -485, 608, -485, -131, -28, 50, - -485, -135, -106, -485, -7, -485, -485, -485, 125, -370, - -485, -485, -485, -485, -485, -485, -138, -485, -485, -485, - -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, - 665, 15, 116, -485, -190, 111, 112, -485, 164, -59, - 398, 137, 14, 367, 603, -5, 454, 432, -485, 402, - -50, 491, -485, -485, -485, -485, -36, 18, -34, -9, - -485, -485, -485, -485, -485, 257, 441, -445, -485, -485, - -485, -485, -485, -485, -485, -485, 259, -116, -218, 265, - -485, 284, -485, -217, -286, 636, -485, -237, -485, -62, - -24, 166, -485, -314, -246, -265, -177, -485, -115, -415, - -485, -485, -379, -485, -8, -485, 435, -485, 326, 225, - 327, 204, 65, 70, -484, -485, -426, 211, -485, 462, - -485, -485 + -474, -474, -474, -474, -474, -474, -474, -15, -474, -474, + 616, -474, -3, -474, -474, 622, -474, -125, -27, 66, + -474, -124, -112, -474, 11, -474, -474, -474, 147, -368, + -474, -474, -474, -474, -474, -474, -140, -474, -474, -474, + -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, + 532, 10, 247, -474, -194, 132, 135, -474, 279, -59, + 418, 67, 5, 384, 624, 425, 317, 20, -474, 424, + 636, 509, -474, -474, -474, -474, -36, -37, -31, -49, + -474, -474, -474, -474, -474, -32, 464, -473, -474, -474, + -474, -474, -474, -474, -474, -474, 277, -119, -231, 287, + -474, 300, -474, -205, -300, 652, -474, -242, -474, -63, + 106, 182, -474, -316, -241, -285, -195, -474, -111, -420, + -474, -474, -245, -474, 402, -474, -176, -474, 345, 249, + 346, 218, 87, 96, -415, -474, -429, 252, -474, 522, + -474, -474 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which - number is the opposite. If zero, do what YYDEFACT says. - If YYTABLE_NINF, syntax error. */ -#define YYTABLE_NINF -275 + number is the opposite. If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -277 static const yytype_int16 yytable[] = { - 12, 119, 161, 121, 272, 174, 359, 488, 274, 436, - 502, 240, 385, 376, 323, 32, 278, 79, 508, 259, - 235, 393, 103, 32, 320, 138, 235, 555, 107, 400, - 540, 111, 375, 402, 111, 433, 235, 27, 128, 173, - 111, 571, 426, 417, 619, 389, 391, 380, 146, 150, - 109, 428, 164, 109, 457, 120, 380, 435, 9, 131, - 5, -213, 150, 226, 232, 233, 653, 4, 9, 212, - 152, 153, 154, 155, 156, 157, 390, 166, 167, 163, - 7, 207, 561, 366, 11, 9, 261, 214, 15, 216, - 218, 388, 205, 28, 276, -213, 462, 29, 20, 18, - 19, 282, 239, 222, 620, 621, 427, 10, 11, 23, - 174, 463, 632, 325, 622, 133, 27, 10, 11, -179, - -234, 273, 243, 458, 291, 579, 133, -213, 618, 26, - 111, 228, 228, 228, 10, 11, 111, 9, 146, 381, - 136, 208, 150, 367, 289, 228, 33, 134, 93, 257, - 164, 209, 537, 209, 228, 266, 124, 438, 134, 526, - 130, 528, 228, 439, 658, 492, 659, 150, 27, 228, - 501, 101, 503, 152, 156, 361, 29, 163, 638, -234, - 379, 607, 633, 331, 332, -234, 10, 11, 141, 9, - 164, 369, 228, 639, 640, 318, 652, 438, 624, 625, - 536, 79, 582, 487, 125, 438, 438, 349, 125, 586, - 451, 594, 613, 105, 357, 32, -181, 163, 243, 171, - 204, 397, 636, 516, 108, 102, 206, -265, 29, 363, - 523, 9, -265, 408, 198, 565, 414, 415, 10, 11, - -180, 228, -152, 228, 243, 79, 202, 409, -181, 411, - 451, -177, 203, 475, -175, 533, 403, 452, 430, 228, - 569, 228, 235, 489, 510, -180, 418, 228, 259, -264, - 512, 9, 235, 584, -264, -177, 150, -179, -175, 11, - 10, 11, -265, -177, 215, 496, -175, 219, -265, 228, - 497, 662, 220, 35, 122, 9, 223, 452, 37, 234, - 249, 410, 250, 94, 228, 228, 453, 112, 164, -176, - 408, 95, 47, 48, 9, 96, 79, 647, 165, 51, - 10, 11, 496, -174, -264, 97, 98, 497, -178, 209, - -264, 277, 262, -176, 353, 163, 495, 454, 480, 623, - 482, -176, 331, 332, 10, 11, 498, -174, 349, 61, - 354, 285, -178, 616, 520, -174, 226, 515, 99, 286, - -178, 64, 358, 10, 11, 483, 360, 243, 529, 478, - 231, 231, 231, 287, 490, 364, 362, 243, 228, 111, - 368, 514, 372, 626, 231, 374, 378, 111, 254, 380, - 228, 111, 481, 231, 146, 522, 150, 631, 257, 384, - 228, 231, 382, 199, 228, 386, 266, 200, 231, 394, - 507, 150, 392, 399, 401, 201, 165, 263, 164, 405, - 413, 416, 419, 264, 228, 228, 432, 445, 446, 254, - 448, 231, 79, 79, 480, 449, 482, 10, 11, 459, - 349, 542, 447, 549, 464, 163, 467, 468, 454, 469, - 480, 470, 482, 614, 454, 471, 165, 562, 349, 486, - 379, 483, 235, 491, 255, 509, 9, 79, 254, 117, - 585, 504, 243, 256, 9, 494, 9, 483, 10, 11, - 231, 506, 231, 511, 9, 513, 521, 164, 481, 525, - 527, 434, 9, 530, 531, 228, 532, 263, 231, 534, - 231, 127, 340, 264, 481, 535, 231, 554, 556, 143, - 557, 147, 265, 564, 163, 10, 11, 10, 11, 172, - 9, 520, 661, 10, 11, 10, 11, 317, 231, 568, - 463, 570, -156, 10, 11, 573, 575, 480, 228, 482, - 412, 10, 11, 231, 231, 117, 117, 117, 210, 210, - 577, 210, 210, 578, 235, 581, 288, 592, 593, 117, - 583, 595, 596, 529, 483, 243, 254, 597, 117, 10, - 11, 79, -157, 598, 165, 600, 117, 599, 150, 602, - 603, 334, 604, 117, 606, 608, 642, 615, 228, 627, - 335, 481, 349, 630, 542, 336, 337, 338, 549, 454, - 177, 255, 339, 349, 349, 480, 117, 482, 629, 340, - 185, 641, 438, 164, 189, 10, 11, 231, 648, 194, - 195, 196, 197, 649, 650, 651, 341, 657, 106, 231, - 66, 484, 483, 628, 580, 654, 590, 591, 342, 231, - 163, 370, 123, 231, 343, 371, 345, 11, 404, 493, - 284, 505, 355, 356, 352, 117, 476, 117, 91, 481, - 443, 444, 574, 231, 231, 344, 539, 563, 637, 634, - 559, 344, 344, 117, 351, 117, 0, 0, 0, 37, - 0, 117, 0, 0, 165, 0, 0, 0, 112, 0, - 0, 0, 0, 47, 48, 9, 0, 0, 0, 0, - 51, 0, 0, 117, 0, 0, 0, 224, 0, 0, - 0, 0, 0, 0, 137, 117, 0, 0, 117, 117, - 0, 0, 175, -274, 114, 0, 160, 484, 0, 169, - 225, 0, 0, 0, 231, 0, 280, 0, 0, 0, - 0, 0, 64, 484, 10, 11, 281, 0, 0, 0, - 0, 176, 177, 165, 178, 179, 180, 181, 182, 0, - 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, - 193, 194, 195, 196, 197, 0, 450, 231, 0, 0, - 0, 0, 0, -274, 461, 0, 0, 0, 344, 0, - 0, 0, 117, -274, 0, 344, 0, 0, 0, 0, - 0, 0, 0, 344, 117, 0, 117, 0, 0, 0, - 0, 0, 0, 0, 117, 0, 0, 0, 117, 0, - 0, 0, 0, 0, 0, 0, 0, 231, 0, 0, - 484, 0, 0, 0, 0, 0, 0, 0, 117, 117, - 292, 293, 294, 295, 0, 296, 297, 298, 0, 299, - 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, - 310, 311, 312, 0, 160, 0, 319, 0, 322, 0, - 0, 0, 137, 137, 333, 538, 0, 0, 0, 165, - 0, 545, 548, 0, 553, 0, 0, 0, 0, 0, - 0, 0, 0, 558, 344, 560, 0, 0, 484, 0, - 543, 344, 117, 344, 0, 0, 0, 0, 0, 117, - 0, 0, 344, 0, 344, 0, 0, 0, 117, 0, - 37, 0, 0, 35, 0, 0, 0, 0, 37, 112, - 0, 168, 0, 0, 47, 48, 9, 112, 0, 0, - 0, 51, 47, 48, 9, 137, 0, 0, 224, 51, - 0, 0, 117, 0, 0, 137, 55, 0, 0, 0, - 0, 0, 0, 0, 0, 114, 0, 0, 0, 56, - 57, 225, 58, 59, 0, 0, 60, 290, 421, 61, - 0, 0, 160, 64, 601, 10, 11, 281, 421, 62, - 63, 64, 605, 10, 11, 0, 0, 0, 37, 0, - 0, 241, 117, 344, 0, 117, 0, 112, 0, 0, - 0, 344, 47, 48, 9, 0, 0, 0, 344, 51, - 0, 0, 0, 0, 0, 0, 224, 0, 0, 0, - 0, 137, 137, 0, 545, 635, 0, 0, 0, 0, - 0, 0, 0, 114, 0, 0, 0, 0, 0, 225, - 344, 0, 0, 543, 344, 0, 0, 0, 0, -2, - 34, 64, 35, 10, 11, 36, 0, 37, 38, 39, - 137, 0, 40, 117, 41, 42, 43, 44, 45, 46, - 0, 47, 48, 9, 137, 0, 49, 50, 51, 52, - 53, 54, 160, 0, 0, 55, 0, 169, 0, 0, - 0, 0, 344, 0, 344, 0, 0, 0, 56, 57, + 121, 120, 162, 273, 175, 123, 122, 321, 437, 377, + 489, 324, 165, 104, 572, 236, 241, 260, 386, 360, + 275, 236, 434, 279, 164, 556, 541, 394, 108, 166, + 458, 236, 429, 390, 392, 401, 346, 621, 436, 403, + 174, 110, 356, 357, 110, 376, 101, 213, 4, 418, + 132, -215, 208, 5, 27, 206, 657, 118, 134, 27, + 7, 15, 11, 427, 18, 153, 154, 155, 156, 157, + 158, -267, 167, 168, 19, 9, -267, 229, 229, 229, + 9, 439, 232, 232, 232, -215, 439, 440, 497, 134, + 135, 229, 488, 498, 367, 102, 232, 622, 623, 459, + 229, 620, -236, 326, 223, 232, 20, 624, 229, -181, + 175, 165, 209, 232, 29, 229, 103, -215, 142, 29, + 232, 135, 210, 164, 10, 11, -267, 428, 166, 10, + 11, 23, -267, 26, 118, 118, 118, 382, 229, 538, + 527, 258, 529, 232, 33, 503, 290, 267, 118, 499, + 205, 165, 452, 509, 368, 139, 207, 118, 502, 27, + 504, -236, 380, 164, 210, 118, 451, -236, 166, 153, + 157, 656, 118, 9, 462, 381, 9, 641, 493, 636, + 9, -266, 594, 635, 93, 463, -266, 229, 595, 229, + 642, 643, 232, 497, 232, 118, 537, 381, 498, 453, + 464, 583, 106, 439, 391, 229, 358, 229, 587, 596, + 232, 128, 232, 229, 109, 28, 137, 562, 232, 29, + 517, 172, 10, 11, 199, 10, 11, 524, 452, 10, + 11, 566, 389, 240, -153, 229, -266, 662, 534, 663, + 232, 203, -266, 204, 118, 255, 118, 411, 410, 9, + 229, 229, 413, 412, 628, 232, 232, 236, 476, 431, + 580, 255, 118, -182, 118, 539, 260, 236, 490, 165, + 118, 546, 549, 570, 554, 453, 511, 513, -181, 585, + 256, 164, 9, 559, 454, 561, 166, 125, -183, 257, + 264, 131, 118, 216, 10, 11, 265, 666, 10, 11, + 439, 11, 221, 220, 118, 266, 615, 118, 118, 224, + 10, 11, -182, 255, 332, 333, 609, 650, 9, 126, + -183, 250, 235, 126, 229, 263, 484, 251, 9, 232, + 210, 10, 11, 626, 627, 625, 229, 94, 482, 481, + 286, 232, 264, 485, 483, 95, 229, 287, 265, 96, + 229, 232, 354, 144, 521, 232, -179, 288, 639, 97, + 98, 200, 10, 11, 274, 201, 618, 10, 11, 530, + 229, 229, 355, 202, 603, 232, 232, 10, 11, 165, + -179, 118, 607, 9, -177, -178, 359, 404, -179, -176, + 258, 164, 99, 118, 633, 118, 166, 419, 267, 634, + 361, 363, 508, 118, 369, -180, 365, 118, -177, -178, + 373, 211, 211, -176, 211, 211, -177, -178, 148, 379, + 375, -176, 484, 381, 383, 546, 638, 118, 118, -180, + 12, 406, 10, 11, 482, 481, 9, -180, 484, 485, + 483, 229, 385, 393, 9, 32, 232, 79, 165, 387, + 482, 481, 9, 32, 9, 485, 483, 236, 616, 395, + 164, 112, 35, 400, 112, 166, 402, 37, 129, 417, + 112, 173, 414, 332, 333, 420, 113, 433, 147, 151, + 278, 47, 48, 9, 229, 10, 11, 318, 51, 232, + 289, 118, 151, 10, 11, 178, 255, 215, 118, 217, + 219, 10, 11, 10, 11, 186, 446, 118, 447, 190, + 448, 449, 515, 450, 195, 196, 197, 198, 61, 460, + 465, 521, 468, 471, 665, 484, 523, 469, 470, 345, + 64, 256, 10, 11, 229, 345, 345, 482, 481, 232, + 472, 118, 485, 483, 487, 10, 11, 492, 380, 495, + 505, 507, 236, 244, 510, 512, 514, 522, 531, 528, + 532, 112, 533, 526, 435, 530, 535, 112, 555, 147, + 341, 536, 557, 151, 565, 165, 558, 569, 574, 571, + -157, 138, 464, 576, 578, 579, 582, 164, 37, 584, + 593, 118, 166, 161, 118, 484, 170, 113, 151, 597, + 598, 599, 47, 48, 9, -158, 600, 482, 481, 51, + 601, 606, 485, 483, 605, 602, 225, 604, 608, 610, + 37, 617, 629, 631, 644, 632, 319, 645, 439, 113, + 651, 652, 79, 115, 47, 48, 9, 653, 350, 226, + 661, 51, 654, 655, 66, 281, 32, 107, 225, 244, + 630, 64, 345, 10, 11, 282, 658, 581, 591, 345, + 364, 592, 371, 124, 118, 115, 405, 345, 372, 285, + 506, 226, 494, 477, 91, 244, 79, 291, 353, 575, + 444, 445, 564, 64, 178, 10, 11, 282, 181, 182, + 183, 540, 640, 185, 186, 187, 188, 637, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 151, 293, 294, + 295, 296, 560, 297, 298, 299, 0, 300, 301, 302, + 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, + 313, 0, 161, 0, 320, 352, 323, 0, 0, 0, + 138, 138, 334, 0, 0, 0, 0, 79, 0, 0, + 227, 233, 234, 0, 0, 0, 0, 0, 345, 0, + 0, 0, 0, 0, 544, 345, 0, 345, 455, 0, + 0, 335, 0, 262, 0, 37, 345, 0, 345, 350, + 336, 277, 0, 0, 113, 337, 338, 339, 283, 47, + 48, 9, 340, 0, 0, 0, 51, 0, 244, 341, + 479, 0, 0, 114, 0, 491, 0, 0, 244, 0, + 112, 292, 0, 138, 0, 0, 342, 0, 112, 0, + 115, 0, 112, 138, 0, 147, 116, 151, 343, 0, + 0, 0, 0, 0, 344, 0, 0, 11, 64, 0, + 10, 11, 151, 0, 0, 0, 422, 0, 0, 0, + 161, 0, 0, 0, 0, 0, 422, 0, 0, 0, + 0, 0, 362, 79, 79, 0, 0, 345, 0, 0, + 0, 350, 543, 0, 550, 345, 0, 0, 370, 455, + 0, 0, 345, 0, 0, 455, 0, 0, 563, 350, + 0, 0, 0, 0, 0, 0, 0, 0, 79, 138, + 138, 0, 0, 244, 0, 0, 0, 0, 398, 0, + 0, 178, 0, 0, 0, 345, 0, 0, 544, 345, + 409, 186, 0, 415, 416, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 0, 0, 0, 0, 138, 0, + 0, 0, 0, 176, -276, 0, 0, 0, 0, 0, + 0, 0, 138, 0, 0, 0, 0, 0, 0, 0, + 161, 0, 0, 0, 0, 170, 0, 0, 0, 345, + 0, 345, 177, 178, 0, 179, 180, 181, 182, 183, + 0, 184, 185, 186, 187, 188, 189, 190, 191, 192, + 193, 194, 195, 196, 197, 198, 244, 409, 0, 0, + 0, 0, 79, 0, -276, 0, 567, 568, 0, 151, + 0, 0, 0, 0, -276, 0, 0, 0, 0, 0, + 0, 0, 0, 496, 350, 0, 543, 0, 0, 161, + 550, 455, 0, 0, 0, 350, 350, 0, 0, 0, + 0, 0, 0, 227, 516, 0, 0, 0, 0, 422, + 0, 0, 0, 0, 0, 0, 422, 589, 422, -2, + 34, 0, 35, 0, 0, 36, 0, 37, 38, 39, + 0, 0, 40, 0, 41, 42, 43, 44, 45, 46, + 0, 47, 48, 9, 0, 0, 49, 50, 51, 52, + 53, 54, 0, 0, 0, 55, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 0, 58, 59, 0, 0, 60, 0, 0, 61, 0, - 0, -24, 0, 0, 0, 0, 0, 0, 62, 63, - 64, 0, 10, 11, 0, 0, 0, 0, 566, 567, - 0, 0, 0, 0, 0, 0, 0, 0, 324, 0, - 35, 0, 0, 36, -249, 37, 38, 39, 0, -249, - 40, 160, 41, 42, 112, 44, 45, 46, 0, 47, - 48, 9, 0, 0, 49, 50, 51, 52, 53, 54, - 0, 421, 0, 55, 0, 0, 0, 0, 421, 588, - 421, 0, 0, 0, 0, 0, 56, 57, 0, 58, - 59, 0, 0, 60, 0, 0, 61, 0, 0, -249, - 0, 0, 0, 0, 325, -249, 62, 63, 64, 0, - 10, 11, 324, 0, 35, 0, 0, 36, 0, 37, - 38, 39, 0, 0, 40, 0, 41, 42, 112, 44, - 45, 46, 0, 47, 48, 9, 177, 0, 49, 50, - 51, 52, 53, 54, 0, 0, 185, 55, 0, 0, - 189, 190, 191, 192, 193, 194, 195, 196, 197, 0, - 56, 57, 0, 58, 59, 0, 0, 60, 0, 0, - 61, 0, 0, -249, 645, 646, 0, 160, 325, -249, - 62, 63, 64, 35, 10, 11, 421, 0, 37, 0, - 0, 0, 0, 0, 0, 0, 0, 112, 0, 334, - 0, 0, 47, 48, 9, 0, 0, 0, 335, 51, - 0, 0, 0, 336, 337, 338, 158, 0, 0, 0, - 339, 0, 0, 0, 0, 0, 0, 340, 0, 56, - 57, 0, 58, 159, 0, 0, 60, 0, 35, 61, - 314, 0, 0, 37, 341, 0, 0, 0, 0, 62, - 63, 64, 112, 10, 11, 0, 0, 47, 48, 9, - 0, 0, 343, 0, 51, 11, 0, 0, 0, 0, - 0, 55, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 56, 57, 0, 58, 59, 0, - 0, 60, 0, 35, 61, 0, 0, 0, 37, 0, - 0, 0, 420, 0, 62, 63, 64, 112, 10, 11, - 0, 0, 47, 48, 9, 0, 0, 0, 0, 51, - 0, 429, 0, 0, 0, 0, 158, 0, 0, 0, + 0, -24, 0, 0, 0, 0, 170, 0, 62, 63, + 64, 0, 10, 11, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 648, 649, 0, 161, 586, 0, 0, + 0, 325, 0, 35, 0, 422, 36, -251, 37, 38, + 39, 0, -251, 40, 0, 41, 42, 113, 44, 45, + 46, 0, 47, 48, 9, 0, 0, 49, 50, 51, + 52, 53, 54, 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, - 57, 0, 58, 159, 0, 0, 60, 0, 35, 61, - 0, 0, 0, 37, 0, 0, 0, 0, 0, 62, - 63, 64, 112, 10, 11, 0, 0, 47, 48, 9, - 0, 473, 0, 0, 51, 0, 0, 0, 0, 0, + 57, 0, 58, 59, 0, 0, 60, 0, 0, 61, + 0, 0, -251, 0, 0, 0, 0, 326, -251, 62, + 63, 64, 0, 10, 11, 325, 0, 35, 0, 0, + 36, 0, 37, 38, 39, 0, 0, 40, 0, 41, + 42, 113, 44, 45, 46, 0, 47, 48, 9, 0, + 0, 49, 50, 51, 52, 53, 54, 0, 0, 0, + 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 56, 57, 0, 58, 59, 0, 0, + 60, 0, 35, 61, 0, 0, -251, 37, 0, 0, + 169, 326, -251, 62, 63, 64, 113, 10, 11, 0, + 0, 47, 48, 9, 0, 0, 0, 0, 51, 0, + 0, 0, 0, 0, 0, 55, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 35, 0, 0, 56, 57, + 37, 58, 59, 0, 0, 60, 0, 0, 61, 113, + 0, 0, 0, 0, 47, 48, 9, 0, 62, 63, + 64, 51, 10, 11, 0, 0, 0, 0, 159, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 56, 57, 0, 58, 160, 0, 0, 60, 0, + 35, 61, 315, 0, 0, 37, 0, 0, 0, 0, + 0, 62, 63, 64, 113, 10, 11, 0, 0, 47, + 48, 9, 0, 0, 0, 0, 51, 0, 0, 0, + 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 56, 57, 0, 58, + 59, 0, 0, 60, 0, 35, 61, 0, 0, 0, + 37, 0, 0, 0, 421, 0, 62, 63, 64, 113, + 10, 11, 0, 0, 47, 48, 9, 0, 0, 0, + 0, 51, 0, 430, 0, 0, 0, 0, 159, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 56, 57, 0, 58, 160, 0, 0, 60, 0, + 35, 61, 0, 0, 0, 37, 0, 0, 0, 0, + 0, 62, 63, 64, 113, 10, 11, 0, 0, 47, + 48, 9, 0, 474, 0, 0, 51, 0, 0, 0, + 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 56, 57, 0, 58, + 59, 0, 0, 60, 0, 35, 61, 0, 0, 0, + 37, 0, 0, 0, 0, 0, 62, 63, 64, 113, + 10, 11, 0, 0, 47, 48, 9, 0, 475, 0, + 0, 51, 0, 0, 0, 0, 0, 0, 55, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, + 0, 56, 57, 37, 58, 59, 0, 0, 60, 0, + 0, 61, 113, 0, 0, 0, 0, 47, 48, 9, + 0, 62, 63, 64, 51, 10, 11, 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 0, 58, 59, 0, 0, 60, 0, 35, 61, 0, 0, 0, 37, 0, - 0, 0, 0, 0, 62, 63, 64, 112, 10, 11, - 0, 0, 47, 48, 9, 0, 474, 0, 0, 51, + 0, 0, 588, 0, 62, 63, 64, 113, 10, 11, + 0, 0, 47, 48, 9, 0, 0, 0, 0, 51, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 56, 57, 37, 58, 59, 0, 0, 60, 0, 0, 61, - 112, 0, 0, 0, 0, 47, 48, 9, 0, 62, - 63, 64, 51, 10, 11, 0, 0, 0, 0, 55, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 56, 57, 0, 58, 59, 0, 0, 60, - 0, 35, 61, 0, 0, 0, 37, 0, 0, 0, - 587, 0, 62, 63, 64, 112, 10, 11, 0, 0, - 47, 48, 9, 0, 0, 0, 0, 51, 0, 0, - 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 35, 0, 0, 56, 57, 37, - 58, 59, 0, 0, 60, 0, 0, 61, 112, 0, - 0, 0, 0, 47, 48, 9, 0, 62, 63, 64, - 51, 10, 11, 0, 0, 0, 0, 158, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, - 56, 57, 283, 58, 159, 0, 0, 60, 0, 0, - 61, 112, 0, 0, 0, 0, 47, 48, 9, 0, - 62, 63, 64, 51, 10, 11, 0, 0, 0, 0, - 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 56, 57, 37, 58, 59, 0, 0, - 60, 0, 0, 61, 112, 0, 0, 0, 0, 47, - 48, 9, 0, 62, 63, 64, 51, 10, 11, 0, - 37, 0, 0, 224, 0, 0, 0, 0, 37, 112, - 0, 0, 0, 0, 47, 48, 9, 112, 0, 0, - 114, 51, 47, 48, 9, 0, 225, 0, 113, 51, - 0, 0, 0, 0, 37, 0, 224, 0, 64, 0, - 10, 11, 396, 112, 0, 114, 0, 0, 47, 48, - 9, 115, 0, 114, 0, 51, 0, 0, 0, 225, - 0, 37, 406, 64, 0, 10, 11, 0, 0, 0, - 112, 64, 0, 10, 11, 47, 48, 9, 0, 114, - 0, 0, 51, 0, 0, 407, 0, 283, 0, 224, - 0, 0, 0, 0, 0, 334, 112, 64, 0, 10, - 11, 47, 48, 9, 335, 0, 114, 0, 51, 336, - 337, 338, 477, 0, 0, 224, 339, 0, 0, 0, - 334, 0, 0, 440, 64, 0, 10, 11, 334, 335, - 0, 460, 114, 0, 336, 337, 541, 335, 225, 0, - 341, 339, 336, 337, 338, 0, 441, 0, 340, 339, - 64, 0, 10, 11, 334, 0, 340, 0, 343, 0, - 0, 11, 0, 335, 0, 341, 0, 0, 336, 337, - 338, 0, 0, 341, 0, 339, 0, 0, 0, 0, - 0, 0, 340, 343, 0, 10, 11, 0, 0, 0, - 0, 343, 177, 0, 11, 0, 180, 181, 182, 341, - 0, 184, 185, 186, 187, 609, 189, 190, 191, 192, - 193, 194, 195, 196, 197, 0, 0, 343, 176, 177, - 11, 178, 0, 180, 181, 182, 0, 0, 184, 185, + 113, 0, 0, 0, 0, 47, 48, 9, 0, 62, + 63, 64, 51, 10, 11, 0, 0, 0, 0, 159, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, + 0, 0, 56, 57, 284, 58, 160, 0, 0, 60, + 0, 0, 61, 113, 0, 0, 0, 0, 47, 48, + 9, 0, 62, 63, 64, 51, 10, 11, 0, 0, + 0, 0, 55, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 56, 57, 37, 58, 59, + 0, 0, 60, 0, 0, 61, 113, 0, 0, 0, + 0, 47, 48, 9, 0, 62, 63, 64, 51, 10, + 11, 0, 0, 37, 0, 225, 242, 0, 0, 0, + 0, 37, 113, 0, 0, 0, 0, 47, 48, 9, + 113, 0, 115, 0, 51, 47, 48, 9, 226, 0, + 0, 225, 51, 0, 0, 0, 37, 0, 0, 225, + 64, 0, 10, 11, 397, 113, 0, 0, 115, 0, + 47, 48, 9, 0, 226, 0, 115, 51, 0, 0, + 0, 0, 226, 0, 407, 0, 64, 0, 10, 11, + 37, 0, 0, 0, 64, 0, 10, 11, 0, 113, + 0, 115, 0, 0, 47, 48, 9, 408, 0, 0, + 0, 51, 0, 0, 0, 284, 0, 0, 225, 64, + 0, 10, 11, 335, 113, 0, 0, 0, 0, 47, + 48, 9, 336, 0, 0, 115, 51, 337, 338, 339, + 0, 478, 0, 225, 340, 0, 0, 0, 0, 335, + 0, 441, 461, 64, 0, 10, 11, 0, 336, 0, + 115, 0, 0, 337, 338, 339, 226, 0, 342, 0, + 340, 0, 0, 0, 442, 0, 335, 341, 64, 0, + 10, 11, 0, 0, 0, 336, 344, 0, 0, 11, + 337, 338, 542, 0, 342, 0, 0, 340, 0, 0, + 0, 0, 335, 0, 341, 0, 0, 0, 0, 0, + 335, 336, 344, 0, 0, 11, 337, 338, 339, 336, + 0, 342, 0, 340, 337, 338, 339, 0, 0, 0, + 341, 340, 0, 0, 0, 0, 0, 0, 341, 344, + 0, 10, 11, 0, 0, 0, 0, 342, 0, 0, + 0, 0, 0, 611, 0, 342, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 344, 0, 0, 11, 0, + 0, 0, 0, 344, 177, 178, 11, 179, 0, 181, + 182, 183, 0, 0, 185, 186, 187, 188, 189, 190, + 191, 192, 193, 194, 195, 196, 197, 198, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 177, 178, 0, + 179, 0, 181, 182, 183, 0, 435, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 0, 0, 0, 0, 0, 0, 177, 178, 0, + 179, 0, 181, 182, 183, 0, 432, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 177, 178, 0, 179, 0, 181, 182, 183, 0, + 525, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 177, 178, 0, 179, 0, + 181, 182, 183, 0, 659, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 177, + 178, 0, 179, 0, 181, 182, 183, 0, 660, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, - 196, 197, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 176, 177, 0, 178, 0, 180, 181, 182, 0, - 434, 184, 185, 186, 187, 188, 189, 190, 191, 192, - 193, 194, 195, 196, 197, 0, 0, 0, 0, 0, - 0, 176, 177, 0, 178, 0, 180, 181, 182, 0, - 431, 184, 185, 186, 187, 188, 189, 190, 191, 192, - 193, 194, 195, 196, 197, 176, 177, 0, 178, 0, - 180, 181, 182, 0, 524, 184, 185, 186, 187, 188, - 189, 190, 191, 192, 193, 194, 195, 196, 197, 176, - 177, 0, 178, 0, 180, 181, 182, 0, 655, 184, - 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, - 195, 196, 197, 176, 177, 0, 178, 0, 180, 181, - 182, 0, 656, 184, 185, 186, 187, 188, 189, 190, - 191, 192, 193, 194, 195, 196, 197, 176, 177, 0, - 0, 0, 180, 181, 182, 0, 0, 184, 185, 186, - 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, - 197, 176, 177, 0, 0, 0, 180, 181, 182, 0, - 0, 184, 185, 186, 187, 0, 189, 190, 191, 192, - 193, 194, 195, 196, 197 + 196, 197, 198, 177, 178, 0, 0, 0, 181, 182, + 183, 0, 0, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 177, 178, 0, + 0, 0, 181, 182, 183, 0, 0, 185, 186, 187, + 188, 0, 190, 191, 192, 193, 194, 195, 196, 197, + 198 }; +#define yypact_value_is_default(yystate) \ + ((yystate) == (-474)) + +#define yytable_value_is_error(yytable_value) \ + YYID (0) + static const yytype_int16 yycheck[] = { - 5, 37, 61, 37, 142, 67, 223, 377, 143, 323, - 389, 126, 258, 250, 204, 20, 147, 22, 397, 135, - 126, 267, 28, 28, 201, 49, 132, 453, 31, 275, - 445, 36, 249, 279, 39, 321, 142, 3, 43, 67, - 45, 486, 11, 289, 5, 263, 264, 24, 53, 54, - 36, 316, 61, 39, 3, 37, 24, 322, 24, 45, - 25, 1, 67, 113, 114, 115, 20, 0, 24, 59, - 55, 56, 57, 58, 59, 60, 53, 62, 63, 61, - 25, 5, 461, 5, 74, 24, 136, 95, 24, 97, - 98, 59, 1, 59, 144, 35, 60, 63, 21, 3, - 62, 151, 126, 106, 65, 66, 75, 73, 74, 24, - 172, 75, 596, 67, 75, 35, 3, 73, 74, 59, - 3, 60, 127, 72, 174, 504, 35, 67, 573, 63, - 135, 113, 114, 115, 73, 74, 141, 24, 143, 255, - 71, 65, 147, 65, 172, 127, 62, 67, 62, 135, - 159, 75, 438, 75, 136, 141, 40, 62, 67, 424, - 44, 426, 144, 68, 648, 382, 650, 172, 3, 151, - 388, 24, 390, 158, 159, 225, 63, 159, 604, 62, - 63, 560, 597, 207, 208, 68, 73, 74, 51, 24, - 199, 241, 174, 608, 609, 200, 641, 62, 577, 578, - 437, 206, 516, 68, 40, 62, 62, 212, 44, 523, - 3, 68, 68, 3, 220, 220, 35, 199, 223, 62, - 83, 271, 601, 413, 3, 60, 89, 7, 63, 234, - 420, 24, 12, 283, 67, 472, 286, 287, 73, 74, - 59, 223, 66, 225, 249, 250, 66, 283, 67, 283, - 3, 35, 59, 368, 35, 432, 280, 50, 317, 241, - 477, 243, 368, 378, 399, 59, 290, 249, 384, 7, - 401, 24, 378, 519, 12, 59, 281, 59, 59, 74, - 73, 74, 62, 67, 24, 7, 67, 64, 68, 271, - 12, 661, 62, 3, 37, 24, 59, 50, 8, 63, - 59, 283, 67, 9, 286, 287, 59, 17, 317, 35, - 360, 17, 22, 23, 24, 21, 321, 631, 61, 29, - 73, 74, 7, 35, 62, 31, 32, 12, 35, 75, - 68, 60, 35, 59, 59, 317, 386, 342, 374, 576, - 374, 67, 366, 367, 73, 74, 68, 59, 353, 59, - 3, 60, 59, 570, 416, 67, 406, 407, 64, 72, - 67, 71, 60, 73, 74, 374, 8, 372, 427, 374, - 113, 114, 115, 72, 379, 35, 60, 382, 360, 384, - 62, 405, 75, 68, 127, 60, 62, 392, 24, 24, - 372, 396, 374, 136, 399, 419, 401, 587, 384, 62, - 382, 144, 59, 59, 386, 72, 392, 63, 151, 3, - 396, 416, 62, 62, 62, 71, 159, 53, 427, 65, - 67, 62, 65, 59, 406, 407, 66, 59, 67, 24, - 67, 174, 437, 438, 470, 8, 470, 73, 74, 65, - 445, 446, 71, 448, 62, 427, 62, 62, 453, 62, - 486, 60, 486, 568, 459, 60, 199, 462, 463, 60, - 63, 470, 568, 60, 59, 35, 24, 472, 24, 37, - 520, 60, 477, 68, 24, 68, 24, 486, 73, 74, - 223, 68, 225, 60, 24, 60, 60, 496, 470, 75, - 68, 75, 24, 60, 60, 477, 60, 53, 241, 60, - 243, 59, 36, 59, 486, 68, 249, 3, 62, 59, - 72, 59, 68, 60, 496, 73, 74, 73, 74, 59, - 24, 583, 660, 73, 74, 73, 74, 59, 271, 62, - 75, 59, 59, 73, 74, 60, 66, 573, 520, 573, - 283, 73, 74, 286, 287, 113, 114, 115, 94, 95, - 60, 97, 98, 60, 660, 60, 60, 60, 72, 127, - 62, 60, 60, 622, 573, 570, 24, 59, 136, 73, - 74, 576, 59, 68, 317, 72, 144, 62, 583, 68, - 62, 8, 49, 151, 62, 59, 14, 60, 570, 68, - 17, 573, 597, 68, 599, 22, 23, 24, 603, 604, - 34, 59, 29, 608, 609, 641, 174, 641, 60, 36, - 44, 60, 62, 622, 48, 73, 74, 360, 60, 53, - 54, 55, 56, 60, 60, 60, 53, 68, 31, 372, - 22, 374, 641, 583, 509, 642, 525, 525, 65, 382, - 622, 243, 39, 386, 71, 243, 211, 74, 281, 384, - 159, 392, 217, 218, 213, 223, 372, 225, 22, 641, - 334, 334, 496, 406, 407, 211, 441, 463, 603, 599, - 459, 217, 218, 241, 212, 243, -1, -1, -1, 8, - -1, 249, -1, -1, 427, -1, -1, -1, 17, -1, - -1, -1, -1, 22, 23, 24, -1, -1, -1, -1, - 29, -1, -1, 271, -1, -1, -1, 36, -1, -1, - -1, -1, -1, -1, 49, 283, -1, -1, 286, 287, - -1, -1, 4, 5, 53, -1, 61, 470, -1, 64, - 59, -1, -1, -1, 477, -1, 65, -1, -1, -1, - -1, -1, 71, 486, 73, 74, 75, -1, -1, -1, - -1, 33, 34, 496, 36, 37, 38, 39, 40, -1, - 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, - 52, 53, 54, 55, 56, -1, 341, 520, -1, -1, - -1, -1, -1, 65, 349, -1, -1, -1, 334, -1, - -1, -1, 360, 75, -1, 341, -1, -1, -1, -1, - -1, -1, -1, 349, 372, -1, 374, -1, -1, -1, - -1, -1, -1, -1, 382, -1, -1, -1, 386, -1, - -1, -1, -1, -1, -1, -1, -1, 570, -1, -1, - 573, -1, -1, -1, -1, -1, -1, -1, 406, 407, - 175, 176, 177, 178, -1, 180, 181, 182, -1, 184, - 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, - 195, 196, 197, -1, 199, -1, 201, -1, 203, -1, - -1, -1, 207, 208, 209, 440, -1, -1, -1, 622, - -1, 446, 447, -1, 449, -1, -1, -1, -1, -1, - -1, -1, -1, 458, 440, 460, -1, -1, 641, -1, - 446, 447, 470, 449, -1, -1, -1, -1, -1, 477, - -1, -1, 458, -1, 460, -1, -1, -1, 486, -1, - 8, -1, -1, 3, -1, -1, -1, -1, 8, 17, - -1, 11, -1, -1, 22, 23, 24, 17, -1, -1, - -1, 29, 22, 23, 24, 280, -1, -1, 36, 29, - -1, -1, 520, -1, -1, 290, 36, -1, -1, -1, - -1, -1, -1, -1, -1, 53, -1, -1, -1, 49, - 50, 59, 52, 53, -1, -1, 56, 65, 313, 59, - -1, -1, 317, 71, 549, 73, 74, 75, 323, 69, - 70, 71, 557, 73, 74, -1, -1, -1, 8, -1, - -1, 11, 570, 549, -1, 573, -1, 17, -1, -1, - -1, 557, 22, 23, 24, -1, -1, -1, 564, 29, - -1, -1, -1, -1, -1, -1, 36, -1, -1, -1, - -1, 366, 367, -1, 599, 600, -1, -1, -1, -1, - -1, -1, -1, 53, -1, -1, -1, -1, -1, 59, - 596, -1, -1, 599, 600, -1, -1, -1, -1, 0, - 1, 71, 3, 73, 74, 6, -1, 8, 9, 10, - 405, -1, 13, 641, 15, 16, 17, 18, 19, 20, - -1, 22, 23, 24, 419, -1, 27, 28, 29, 30, - 31, 32, 427, -1, -1, 36, -1, 432, -1, -1, - -1, -1, 648, -1, 650, -1, -1, -1, 49, 50, + 37, 37, 61, 143, 67, 37, 37, 202, 324, 251, + 378, 205, 61, 28, 487, 127, 127, 136, 259, 224, + 144, 133, 322, 148, 61, 454, 446, 268, 31, 61, + 3, 143, 317, 264, 265, 276, 212, 5, 323, 280, + 67, 36, 218, 219, 39, 250, 24, 59, 0, 290, + 45, 1, 5, 25, 3, 1, 20, 37, 35, 3, + 25, 24, 74, 11, 3, 55, 56, 57, 58, 59, + 60, 7, 62, 63, 62, 24, 12, 114, 115, 116, + 24, 62, 114, 115, 116, 35, 62, 68, 7, 35, + 67, 128, 68, 12, 5, 73, 128, 65, 66, 72, + 137, 574, 3, 67, 107, 137, 21, 75, 145, 59, + 173, 160, 65, 145, 63, 152, 60, 67, 51, 63, + 152, 67, 75, 160, 73, 74, 62, 75, 160, 73, + 74, 24, 68, 63, 114, 115, 116, 256, 175, 439, + 425, 136, 427, 175, 62, 390, 173, 142, 128, 68, + 83, 200, 3, 398, 65, 49, 89, 137, 389, 3, + 391, 62, 63, 200, 75, 145, 342, 68, 200, 159, + 160, 644, 152, 24, 350, 24, 24, 606, 383, 599, + 24, 7, 66, 598, 62, 60, 12, 224, 72, 226, + 610, 611, 224, 7, 226, 175, 438, 24, 12, 50, + 75, 517, 3, 62, 53, 242, 221, 244, 524, 68, + 242, 59, 244, 250, 3, 59, 71, 462, 250, 63, + 414, 62, 73, 74, 67, 73, 74, 421, 3, 73, + 74, 473, 59, 127, 66, 272, 62, 652, 433, 654, + 272, 66, 68, 59, 224, 24, 226, 284, 284, 24, + 287, 288, 284, 284, 68, 287, 288, 369, 369, 318, + 505, 24, 242, 59, 244, 441, 385, 379, 379, 318, + 250, 447, 448, 478, 450, 50, 400, 402, 59, 520, + 59, 318, 24, 459, 59, 461, 318, 40, 35, 68, + 53, 44, 272, 24, 73, 74, 59, 665, 73, 74, + 62, 74, 62, 64, 284, 68, 68, 287, 288, 59, + 73, 74, 59, 24, 208, 209, 561, 633, 24, 40, + 67, 59, 63, 44, 361, 35, 375, 67, 24, 361, + 75, 73, 74, 578, 579, 577, 373, 9, 375, 375, + 60, 373, 53, 375, 375, 17, 383, 72, 59, 21, + 387, 383, 59, 59, 417, 387, 35, 72, 603, 31, + 32, 59, 73, 74, 60, 63, 571, 73, 74, 428, + 407, 408, 3, 71, 550, 407, 408, 73, 74, 428, + 59, 361, 558, 24, 35, 35, 60, 281, 67, 35, + 385, 428, 64, 373, 588, 375, 428, 291, 393, 594, + 8, 60, 397, 383, 62, 35, 35, 387, 59, 59, + 75, 94, 95, 59, 97, 98, 67, 67, 59, 62, + 60, 67, 471, 24, 59, 601, 602, 407, 408, 59, + 5, 65, 73, 74, 471, 471, 24, 67, 487, 471, + 471, 478, 62, 62, 24, 20, 478, 22, 497, 72, + 487, 487, 24, 28, 24, 487, 487, 569, 569, 3, + 497, 36, 3, 62, 39, 497, 62, 8, 43, 62, + 45, 59, 67, 367, 368, 65, 17, 66, 53, 54, + 60, 22, 23, 24, 521, 73, 74, 59, 29, 521, + 60, 471, 67, 73, 74, 34, 24, 95, 478, 97, + 98, 73, 74, 73, 74, 44, 59, 487, 67, 48, + 71, 67, 406, 8, 53, 54, 55, 56, 59, 65, + 62, 584, 62, 60, 664, 574, 420, 62, 62, 212, + 71, 59, 73, 74, 571, 218, 219, 574, 574, 571, + 60, 521, 574, 574, 60, 73, 74, 60, 63, 68, + 60, 68, 664, 128, 35, 60, 60, 60, 60, 68, + 60, 136, 60, 75, 75, 624, 60, 142, 3, 144, + 36, 68, 62, 148, 60, 624, 72, 62, 60, 59, + 59, 49, 75, 66, 60, 60, 60, 624, 8, 62, + 60, 571, 624, 61, 574, 644, 64, 17, 173, 60, + 60, 59, 22, 23, 24, 59, 68, 644, 644, 29, + 62, 49, 644, 644, 62, 72, 36, 68, 62, 59, + 8, 60, 68, 60, 60, 68, 201, 14, 62, 17, + 72, 60, 207, 53, 22, 23, 24, 60, 213, 59, + 68, 29, 60, 60, 22, 65, 221, 31, 36, 224, + 584, 71, 335, 73, 74, 75, 645, 510, 526, 342, + 235, 526, 244, 39, 644, 53, 282, 350, 244, 160, + 393, 59, 385, 373, 22, 250, 251, 65, 214, 497, + 335, 335, 464, 71, 34, 73, 74, 75, 38, 39, + 40, 442, 605, 43, 44, 45, 46, 601, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 282, 176, 177, + 178, 179, 460, 181, 182, 183, -1, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, -1, 200, -1, 202, 213, 204, -1, -1, -1, + 208, 209, 210, -1, -1, -1, -1, 322, -1, -1, + 114, 115, 116, -1, -1, -1, -1, -1, 441, -1, + -1, -1, -1, -1, 447, 448, -1, 450, 343, -1, + -1, 8, -1, 137, -1, 8, 459, -1, 461, 354, + 17, 145, -1, -1, 17, 22, 23, 24, 152, 22, + 23, 24, 29, -1, -1, -1, 29, -1, 373, 36, + 375, -1, -1, 36, -1, 380, -1, -1, 383, -1, + 385, 175, -1, 281, -1, -1, 53, -1, 393, -1, + 53, -1, 397, 291, -1, 400, 59, 402, 65, -1, + -1, -1, -1, -1, 71, -1, -1, 74, 71, -1, + 73, 74, 417, -1, -1, -1, 314, -1, -1, -1, + 318, -1, -1, -1, -1, -1, 324, -1, -1, -1, + -1, -1, 226, 438, 439, -1, -1, 550, -1, -1, + -1, 446, 447, -1, 449, 558, -1, -1, 242, 454, + -1, -1, 565, -1, -1, 460, -1, -1, 463, 464, + -1, -1, -1, -1, -1, -1, -1, -1, 473, 367, + 368, -1, -1, 478, -1, -1, -1, -1, 272, -1, + -1, 34, -1, -1, -1, 598, -1, -1, 601, 602, + 284, 44, -1, 287, 288, 48, 49, 50, 51, 52, + 53, 54, 55, 56, -1, -1, -1, -1, 406, -1, + -1, -1, -1, 4, 5, -1, -1, -1, -1, -1, + -1, -1, 420, -1, -1, -1, -1, -1, -1, -1, + 428, -1, -1, -1, -1, 433, -1, -1, -1, 652, + -1, 654, 33, 34, -1, 36, 37, 38, 39, 40, + -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 571, 361, -1, -1, + -1, -1, 577, -1, 65, -1, 474, 475, -1, 584, + -1, -1, -1, -1, 75, -1, -1, -1, -1, -1, + -1, -1, -1, 387, 599, -1, 601, -1, -1, 497, + 605, 606, -1, -1, -1, 610, 611, -1, -1, -1, + -1, -1, -1, 407, 408, -1, -1, -1, -1, 517, + -1, -1, -1, -1, -1, -1, 524, 525, 526, 0, + 1, -1, 3, -1, -1, 6, -1, 8, 9, 10, + -1, -1, 13, -1, 15, 16, 17, 18, 19, 20, + -1, 22, 23, 24, -1, -1, 27, 28, 29, 30, + 31, 32, -1, -1, -1, 36, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 49, 50, -1, 52, 53, -1, -1, 56, -1, -1, 59, -1, - -1, 62, -1, -1, -1, -1, -1, -1, 69, 70, - 71, -1, 73, 74, -1, -1, -1, -1, 473, 474, - -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, - 3, -1, -1, 6, 7, 8, 9, 10, -1, 12, - 13, 496, 15, 16, 17, 18, 19, 20, -1, 22, - 23, 24, -1, -1, 27, 28, 29, 30, 31, 32, - -1, 516, -1, 36, -1, -1, -1, -1, 523, 524, - 525, -1, -1, -1, -1, -1, 49, 50, -1, 52, - 53, -1, -1, 56, -1, -1, 59, -1, -1, 62, - -1, -1, -1, -1, 67, 68, 69, 70, 71, -1, - 73, 74, 1, -1, 3, -1, -1, 6, -1, 8, - 9, 10, -1, -1, 13, -1, 15, 16, 17, 18, - 19, 20, -1, 22, 23, 24, 34, -1, 27, 28, - 29, 30, 31, 32, -1, -1, 44, 36, -1, -1, - 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, - 49, 50, -1, 52, 53, -1, -1, 56, -1, -1, - 59, -1, -1, 62, 619, 620, -1, 622, 67, 68, - 69, 70, 71, 3, 73, 74, 631, -1, 8, -1, - -1, -1, -1, -1, -1, -1, -1, 17, -1, 8, - -1, -1, 22, 23, 24, -1, -1, -1, 17, 29, - -1, -1, -1, 22, 23, 24, 36, -1, -1, -1, - 29, -1, -1, -1, -1, -1, -1, 36, -1, 49, - 50, -1, 52, 53, -1, -1, 56, -1, 3, 59, - 60, -1, -1, 8, 53, -1, -1, -1, -1, 69, - 70, 71, 17, 73, 74, -1, -1, 22, 23, 24, - -1, -1, 71, -1, 29, 74, -1, -1, -1, -1, + -1, 62, -1, -1, -1, -1, 594, -1, 69, 70, + 71, -1, 73, 74, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 621, 622, -1, 624, 521, -1, -1, + -1, 1, -1, 3, -1, 633, 6, 7, 8, 9, + 10, -1, 12, 13, -1, 15, 16, 17, 18, 19, + 20, -1, 22, 23, 24, -1, -1, 27, 28, 29, + 30, 31, 32, -1, -1, -1, 36, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 49, + 50, -1, 52, 53, -1, -1, 56, -1, -1, 59, + -1, -1, 62, -1, -1, -1, -1, 67, 68, 69, + 70, 71, -1, 73, 74, 1, -1, 3, -1, -1, + 6, -1, 8, 9, 10, -1, -1, 13, -1, 15, + 16, 17, 18, 19, 20, -1, 22, 23, 24, -1, + -1, 27, 28, 29, 30, 31, 32, -1, -1, -1, + 36, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 49, 50, -1, 52, 53, -1, -1, + 56, -1, 3, 59, -1, -1, 62, 8, -1, -1, + 11, 67, 68, 69, 70, 71, 17, 73, 74, -1, + -1, 22, 23, 24, -1, -1, -1, -1, 29, -1, + -1, -1, -1, -1, -1, 36, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 3, -1, -1, 49, 50, + 8, 52, 53, -1, -1, 56, -1, -1, 59, 17, + -1, -1, -1, -1, 22, 23, 24, -1, 69, 70, + 71, 29, 73, 74, -1, -1, -1, -1, 36, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 49, 50, -1, 52, 53, -1, -1, 56, -1, + 3, 59, 60, -1, -1, 8, -1, -1, -1, -1, + -1, 69, 70, 71, 17, 73, 74, -1, -1, 22, + 23, 24, -1, -1, -1, -1, 29, -1, -1, -1, + -1, -1, -1, 36, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 49, 50, -1, 52, + 53, -1, -1, 56, -1, 3, 59, -1, -1, -1, + 8, -1, -1, -1, 67, -1, 69, 70, 71, 17, + 73, 74, -1, -1, 22, 23, 24, -1, -1, -1, + -1, 29, -1, 31, -1, -1, -1, -1, 36, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 49, 50, -1, 52, 53, -1, -1, 56, -1, + 3, 59, -1, -1, -1, 8, -1, -1, -1, -1, + -1, 69, 70, 71, 17, 73, 74, -1, -1, 22, + 23, 24, -1, 26, -1, -1, 29, -1, -1, -1, + -1, -1, -1, 36, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 49, 50, -1, 52, + 53, -1, -1, 56, -1, 3, 59, -1, -1, -1, + 8, -1, -1, -1, -1, -1, 69, 70, 71, 17, + 73, 74, -1, -1, 22, 23, 24, -1, 26, -1, + -1, 29, -1, -1, -1, -1, -1, -1, 36, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 3, -1, + -1, 49, 50, 8, 52, 53, -1, -1, 56, -1, + -1, 59, 17, -1, -1, -1, -1, 22, 23, 24, + -1, 69, 70, 71, 29, 73, 74, -1, -1, -1, -1, 36, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 49, 50, -1, 52, 53, -1, -1, 56, -1, 3, 59, -1, -1, -1, 8, -1, -1, -1, 67, -1, 69, 70, 71, 17, 73, 74, -1, -1, 22, 23, 24, -1, -1, -1, -1, 29, - -1, 31, -1, -1, -1, -1, 36, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 49, - 50, -1, 52, 53, -1, -1, 56, -1, 3, 59, - -1, -1, -1, 8, -1, -1, -1, -1, -1, 69, - 70, 71, 17, 73, 74, -1, -1, 22, 23, 24, - -1, 26, -1, -1, 29, -1, -1, -1, -1, -1, - -1, 36, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 49, 50, -1, 52, 53, -1, - -1, 56, -1, 3, 59, -1, -1, -1, 8, -1, - -1, -1, -1, -1, 69, 70, 71, 17, 73, 74, - -1, -1, 22, 23, 24, -1, 26, -1, -1, 29, -1, -1, -1, -1, -1, -1, 36, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, -1, -1, 49, 50, 8, 52, 53, -1, -1, 56, -1, -1, 59, 17, -1, -1, -1, -1, 22, 23, 24, -1, 69, 70, 71, 29, 73, 74, -1, -1, -1, -1, 36, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 49, 50, -1, 52, 53, -1, -1, 56, - -1, 3, 59, -1, -1, -1, 8, -1, -1, -1, - 67, -1, 69, 70, 71, 17, 73, 74, -1, -1, - 22, 23, 24, -1, -1, -1, -1, 29, -1, -1, - -1, -1, -1, -1, 36, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 3, -1, -1, 49, 50, 8, - 52, 53, -1, -1, 56, -1, -1, 59, 17, -1, - -1, -1, -1, 22, 23, 24, -1, 69, 70, 71, - 29, 73, 74, -1, -1, -1, -1, 36, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 3, -1, -1, - 49, 50, 8, 52, 53, -1, -1, 56, -1, -1, - 59, 17, -1, -1, -1, -1, 22, 23, 24, -1, - 69, 70, 71, 29, 73, 74, -1, -1, -1, -1, - 36, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 49, 50, 8, 52, 53, -1, -1, - 56, -1, -1, 59, 17, -1, -1, -1, -1, 22, - 23, 24, -1, 69, 70, 71, 29, 73, 74, -1, - 8, -1, -1, 36, -1, -1, -1, -1, 8, 17, - -1, -1, -1, -1, 22, 23, 24, 17, -1, -1, - 53, 29, 22, 23, 24, -1, 59, -1, 36, 29, - -1, -1, -1, -1, 8, -1, 36, -1, 71, -1, - 73, 74, 75, 17, -1, 53, -1, -1, 22, 23, - 24, 59, -1, 53, -1, 29, -1, -1, -1, 59, - -1, 8, 36, 71, -1, 73, 74, -1, -1, -1, - 17, 71, -1, 73, 74, 22, 23, 24, -1, 53, - -1, -1, 29, -1, -1, 59, -1, 8, -1, 36, - -1, -1, -1, -1, -1, 8, 17, 71, -1, 73, - 74, 22, 23, 24, 17, -1, 53, -1, 29, 22, - 23, 24, 59, -1, -1, 36, 29, -1, -1, -1, - 8, -1, -1, 36, 71, -1, 73, 74, 8, 17, - -1, 11, 53, -1, 22, 23, 24, 17, 59, -1, - 53, 29, 22, 23, 24, -1, 59, -1, 36, 29, - 71, -1, 73, 74, 8, -1, 36, -1, 71, -1, - -1, 74, -1, 17, -1, 53, -1, -1, 22, 23, - 24, -1, -1, 53, -1, 29, -1, -1, -1, -1, - -1, -1, 36, 71, -1, 73, 74, -1, -1, -1, - -1, 71, 34, -1, 74, -1, 38, 39, 40, 53, - -1, 43, 44, 45, 46, 59, 48, 49, 50, 51, - 52, 53, 54, 55, 56, -1, -1, 71, 33, 34, - 74, 36, -1, 38, 39, 40, -1, -1, 43, 44, - 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, - 55, 56, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 33, 34, -1, 36, -1, 38, 39, 40, -1, - 75, 43, 44, 45, 46, 47, 48, 49, 50, 51, - 52, 53, 54, 55, 56, -1, -1, -1, -1, -1, - -1, 33, 34, -1, 36, -1, 38, 39, 40, -1, - 72, 43, 44, 45, 46, 47, 48, 49, 50, 51, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, + -1, -1, 49, 50, 8, 52, 53, -1, -1, 56, + -1, -1, 59, 17, -1, -1, -1, -1, 22, 23, + 24, -1, 69, 70, 71, 29, 73, 74, -1, -1, + -1, -1, 36, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 49, 50, 8, 52, 53, + -1, -1, 56, -1, -1, 59, 17, -1, -1, -1, + -1, 22, 23, 24, -1, 69, 70, 71, 29, 73, + 74, -1, -1, 8, -1, 36, 11, -1, -1, -1, + -1, 8, 17, -1, -1, -1, -1, 22, 23, 24, + 17, -1, 53, -1, 29, 22, 23, 24, 59, -1, + -1, 36, 29, -1, -1, -1, 8, -1, -1, 36, + 71, -1, 73, 74, 75, 17, -1, -1, 53, -1, + 22, 23, 24, -1, 59, -1, 53, 29, -1, -1, + -1, -1, 59, -1, 36, -1, 71, -1, 73, 74, + 8, -1, -1, -1, 71, -1, 73, 74, -1, 17, + -1, 53, -1, -1, 22, 23, 24, 59, -1, -1, + -1, 29, -1, -1, -1, 8, -1, -1, 36, 71, + -1, 73, 74, 8, 17, -1, -1, -1, -1, 22, + 23, 24, 17, -1, -1, 53, 29, 22, 23, 24, + -1, 59, -1, 36, 29, -1, -1, -1, -1, 8, + -1, 36, 11, 71, -1, 73, 74, -1, 17, -1, + 53, -1, -1, 22, 23, 24, 59, -1, 53, -1, + 29, -1, -1, -1, 59, -1, 8, 36, 71, -1, + 73, 74, -1, -1, -1, 17, 71, -1, -1, 74, + 22, 23, 24, -1, 53, -1, -1, 29, -1, -1, + -1, -1, 8, -1, 36, -1, -1, -1, -1, -1, + 8, 17, 71, -1, -1, 74, 22, 23, 24, 17, + -1, 53, -1, 29, 22, 23, 24, -1, -1, -1, + 36, 29, -1, -1, -1, -1, -1, -1, 36, 71, + -1, 73, 74, -1, -1, -1, -1, 53, -1, -1, + -1, -1, -1, 59, -1, 53, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 71, -1, -1, 74, -1, + -1, -1, -1, 71, 33, 34, 74, 36, -1, 38, + 39, 40, -1, -1, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 33, 34, -1, + 36, -1, 38, 39, 40, -1, 75, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, -1, -1, -1, -1, -1, -1, 33, 34, -1, + 36, -1, 38, 39, 40, -1, 72, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 33, 34, -1, 36, -1, 38, 39, 40, -1, + 66, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 33, 34, -1, 36, -1, 38, 39, 40, -1, 66, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 33, 34, -1, 36, -1, 38, 39, 40, -1, 66, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - 54, 55, 56, 33, 34, -1, 36, -1, 38, 39, - 40, -1, 66, 43, 44, 45, 46, 47, 48, 49, + 54, 55, 56, 33, 34, -1, -1, -1, 38, 39, + 40, -1, -1, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 33, 34, -1, -1, -1, 38, 39, 40, -1, -1, 43, 44, 45, - 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - 56, 33, 34, -1, -1, -1, 38, 39, 40, -1, - -1, 43, 44, 45, 46, -1, 48, 49, 50, 51, - 52, 53, 54, 55, 56 + 46, -1, 48, 49, 50, 51, 52, 53, 54, 55, + 56 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing @@ -1494,63 +1537,63 @@ static const yytype_uint8 yystos[] = 113, 121, 124, 126, 127, 128, 129, 134, 138, 141, 143, 144, 149, 150, 153, 156, 157, 158, 161, 164, 165, 181, 186, 62, 9, 17, 21, 31, 32, 64, - 199, 24, 60, 83, 84, 3, 86, 88, 3, 138, - 140, 141, 17, 36, 53, 59, 141, 143, 148, 152, - 153, 154, 161, 140, 128, 134, 111, 59, 141, 159, - 128, 138, 114, 35, 67, 137, 71, 126, 186, 193, - 125, 137, 122, 59, 96, 97, 141, 59, 93, 139, - 141, 185, 127, 127, 127, 127, 127, 127, 36, 53, - 126, 135, 147, 153, 155, 161, 127, 127, 11, 126, - 192, 62, 59, 94, 185, 4, 33, 34, 36, 37, - 38, 39, 40, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 52, 53, 54, 55, 56, 67, 59, - 63, 71, 66, 59, 137, 1, 137, 5, 65, 75, - 142, 200, 59, 160, 200, 24, 200, 201, 200, 64, - 62, 190, 88, 59, 36, 59, 146, 152, 153, 154, - 155, 161, 146, 146, 63, 98, 107, 108, 109, 186, - 194, 11, 136, 141, 145, 146, 177, 178, 179, 59, - 67, 162, 112, 194, 24, 59, 68, 138, 171, 173, - 175, 146, 35, 53, 59, 68, 138, 170, 172, 173, - 174, 184, 112, 60, 97, 169, 146, 60, 93, 167, - 65, 75, 146, 8, 147, 60, 72, 72, 60, 94, - 65, 146, 126, 126, 126, 126, 126, 126, 126, 126, + 199, 24, 73, 60, 83, 84, 3, 86, 88, 3, + 138, 140, 141, 17, 36, 53, 59, 141, 143, 148, + 152, 153, 154, 161, 140, 128, 134, 111, 59, 141, + 159, 128, 138, 114, 35, 67, 137, 71, 126, 186, + 193, 125, 137, 122, 59, 96, 97, 141, 59, 93, + 139, 141, 185, 127, 127, 127, 127, 127, 127, 36, + 53, 126, 135, 147, 153, 155, 161, 127, 127, 11, + 126, 192, 62, 59, 94, 185, 4, 33, 34, 36, + 37, 38, 39, 40, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 67, + 59, 63, 71, 66, 59, 137, 1, 137, 5, 65, + 75, 142, 200, 59, 160, 200, 24, 200, 201, 200, + 64, 62, 190, 88, 59, 36, 59, 146, 152, 153, + 154, 155, 161, 146, 146, 63, 98, 107, 108, 109, + 186, 194, 11, 136, 141, 145, 146, 177, 178, 179, + 59, 67, 162, 112, 194, 24, 59, 68, 138, 171, + 173, 175, 146, 35, 53, 59, 68, 138, 170, 172, + 173, 174, 184, 112, 60, 97, 169, 146, 60, 93, + 167, 65, 75, 146, 8, 147, 60, 72, 72, 60, + 94, 65, 146, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 130, 60, 135, 187, 59, 141, 126, - 192, 182, 126, 130, 1, 67, 91, 100, 180, 181, - 183, 186, 186, 126, 8, 17, 22, 23, 24, 29, - 36, 53, 65, 71, 142, 202, 204, 205, 206, 141, - 207, 215, 162, 59, 3, 202, 202, 83, 60, 179, - 8, 146, 60, 141, 35, 105, 5, 65, 62, 146, - 136, 145, 75, 191, 60, 179, 183, 115, 62, 63, - 24, 173, 59, 176, 62, 190, 72, 104, 59, 174, - 53, 174, 62, 190, 3, 198, 75, 146, 123, 62, - 190, 62, 190, 186, 139, 65, 36, 59, 146, 152, - 153, 154, 161, 67, 146, 146, 62, 190, 186, 65, - 67, 126, 131, 132, 188, 189, 11, 75, 191, 31, - 135, 72, 66, 180, 75, 191, 189, 101, 62, 68, - 36, 59, 203, 204, 206, 59, 67, 71, 67, 8, - 202, 3, 50, 59, 141, 212, 213, 3, 72, 65, - 11, 202, 60, 75, 62, 195, 215, 62, 62, 62, - 60, 60, 106, 26, 26, 194, 177, 59, 141, 151, - 152, 153, 154, 155, 161, 163, 60, 68, 105, 194, - 141, 60, 179, 175, 68, 146, 7, 12, 68, 99, - 102, 174, 198, 174, 60, 172, 68, 138, 198, 35, - 97, 60, 93, 60, 186, 146, 130, 94, 95, 168, - 185, 60, 186, 130, 66, 75, 191, 68, 191, 135, - 60, 60, 60, 192, 60, 68, 183, 180, 202, 205, - 195, 24, 141, 142, 197, 202, 209, 217, 202, 141, - 196, 208, 216, 202, 3, 212, 62, 72, 202, 213, - 202, 198, 141, 207, 60, 183, 126, 126, 62, 179, - 59, 163, 116, 60, 187, 66, 103, 60, 60, 198, - 104, 60, 189, 62, 190, 146, 189, 67, 126, 133, - 131, 132, 60, 72, 68, 60, 60, 59, 68, 62, - 72, 202, 68, 62, 49, 202, 62, 198, 59, 59, - 202, 210, 211, 68, 194, 60, 179, 119, 163, 5, - 65, 66, 75, 183, 198, 198, 68, 68, 95, 60, - 68, 130, 210, 195, 209, 202, 198, 208, 212, 195, - 195, 60, 14, 117, 120, 126, 126, 189, 60, 60, - 60, 60, 163, 20, 100, 66, 66, 68, 210, 210, - 118, 112, 105 + 126, 126, 126, 126, 130, 60, 135, 187, 59, 141, + 126, 192, 182, 126, 130, 1, 67, 91, 100, 180, + 181, 183, 186, 186, 126, 8, 17, 22, 23, 24, + 29, 36, 53, 65, 71, 142, 202, 204, 205, 206, + 141, 207, 215, 162, 59, 3, 202, 202, 83, 60, + 179, 8, 146, 60, 141, 35, 105, 5, 65, 62, + 146, 136, 145, 75, 191, 60, 179, 183, 115, 62, + 63, 24, 173, 59, 176, 62, 190, 72, 104, 59, + 174, 53, 174, 62, 190, 3, 198, 75, 146, 123, + 62, 190, 62, 190, 186, 139, 65, 36, 59, 146, + 152, 153, 154, 161, 67, 146, 146, 62, 190, 186, + 65, 67, 126, 131, 132, 188, 189, 11, 75, 191, + 31, 135, 72, 66, 180, 75, 191, 189, 101, 62, + 68, 36, 59, 203, 204, 206, 59, 67, 71, 67, + 8, 202, 3, 50, 59, 141, 212, 213, 3, 72, + 65, 11, 202, 60, 75, 62, 195, 215, 62, 62, + 62, 60, 60, 106, 26, 26, 194, 177, 59, 141, + 151, 152, 153, 154, 155, 161, 163, 60, 68, 105, + 194, 141, 60, 179, 175, 68, 146, 7, 12, 68, + 99, 102, 174, 198, 174, 60, 172, 68, 138, 198, + 35, 97, 60, 93, 60, 186, 146, 130, 94, 95, + 168, 185, 60, 186, 130, 66, 75, 191, 68, 191, + 135, 60, 60, 60, 192, 60, 68, 183, 180, 202, + 205, 195, 24, 141, 142, 197, 202, 209, 217, 202, + 141, 196, 208, 216, 202, 3, 212, 62, 72, 202, + 213, 202, 198, 141, 207, 60, 183, 126, 126, 62, + 179, 59, 163, 116, 60, 187, 66, 103, 60, 60, + 198, 104, 60, 189, 62, 190, 146, 189, 67, 126, + 133, 131, 132, 60, 66, 72, 68, 60, 60, 59, + 68, 62, 72, 202, 68, 62, 49, 202, 62, 198, + 59, 59, 202, 210, 211, 68, 194, 60, 179, 119, + 163, 5, 65, 66, 75, 183, 198, 198, 68, 68, + 95, 60, 68, 130, 192, 210, 195, 209, 202, 198, + 208, 212, 195, 195, 60, 14, 117, 120, 126, 126, + 189, 72, 60, 60, 60, 60, 163, 20, 100, 66, + 66, 68, 210, 210, 118, 112, 105 }; #define yyerrok (yyerrstatus = 0) @@ -1565,9 +1608,18 @@ static const yytype_uint8 yystos[] = /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. - Once GCC version 2 has supplanted version 1, this can go. */ + Once GCC version 2 has supplanted version 1, this can go. However, + YYFAIL appears to be in use. Nevertheless, it is formally deprecated + in Bison 2.4.2's NEWS entry, where a plan to phase it out is + discussed. */ #define YYFAIL goto yyerrlab +#if defined YYFAIL + /* This is here to suppress warnings from the GCC cpp's + -Wunused-macros. Normally we don't worry about that warning, but + some users do, and we want to make it easy for users to remove + YYFAIL uses, which will produce warnings from Bison 2.5. */ +#endif #define YYRECOVERING() (!!yyerrstatus) @@ -1577,7 +1629,6 @@ do \ { \ yychar = (Token); \ yylval = (Value); \ - yytoken = YYTRANSLATE (yychar); \ YYPOPSTACK (1); \ goto yybackup; \ } \ @@ -1619,19 +1670,10 @@ while (YYID (0)) #endif -/* YY_LOCATION_PRINT -- Print the location on the stream. - This macro was not mandated originally: define only if we know - we won't break user code: when these are the locations we know. */ +/* This macro is provided for backward compatibility. */ #ifndef YY_LOCATION_PRINT -# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL -# define YY_LOCATION_PRINT(File, Loc) \ - fprintf (File, "%d.%d-%d.%d", \ - (Loc).first_line, (Loc).first_column, \ - (Loc).last_line, (Loc).last_column) -# else -# define YY_LOCATION_PRINT(File, Loc) ((void) 0) -# endif +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) #endif @@ -1735,17 +1777,20 @@ yy_symbol_print (yyoutput, yytype, yyvaluep) #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void -yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) #else static void -yy_stack_print (bottom, top) - yytype_int16 *bottom; - yytype_int16 *top; +yy_stack_print (yybottom, yytop) + yytype_int16 *yybottom; + yytype_int16 *yytop; #endif { YYFPRINTF (stderr, "Stack now"); - for (; bottom <= top; ++bottom) - YYFPRINTF (stderr, " %d", *bottom); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } YYFPRINTF (stderr, "\n"); } @@ -1779,11 +1824,11 @@ yy_reduce_print (yyvsp, yyrule) /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { - fprintf (stderr, " $%d = ", yyi + 1); + YYFPRINTF (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], &(yyvsp[(yyi + 1) - (yynrhs)]) ); - fprintf (stderr, "\n"); + YYFPRINTF (stderr, "\n"); } } @@ -1820,7 +1865,6 @@ int yydebug; # define YYMAXDEPTH 10000 #endif - #if YYERROR_VERBOSE @@ -1923,115 +1967,142 @@ yytnamerr (char *yyres, const char *yystr) } # endif -/* Copy into YYRESULT an error message about the unexpected token - YYCHAR while in state YYSTATE. Return the number of bytes copied, - including the terminating null byte. If YYRESULT is null, do not - copy anything; just return the number of bytes that would be - copied. As a special case, return 0 if an ordinary "syntax error" - message will do. Return YYSIZE_MAXIMUM if overflow occurs during - size calculation. */ -static YYSIZE_T -yysyntax_error (char *yyresult, int yystate, int yychar) -{ - int yyn = yypact[yystate]; +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. - if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) - return 0; - else - { - int yytype = YYTRANSLATE (yychar); - YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); - YYSIZE_T yysize = yysize0; - YYSIZE_T yysize1; - int yysize_overflow = 0; - enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; - char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; - int yyx; - -# if 0 - /* This is so xgettext sees the translatable formats that are - constructed on the fly. */ - YY_("syntax error, unexpected %s"); - YY_("syntax error, unexpected %s, expecting %s"); - YY_("syntax error, unexpected %s, expecting %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); -# endif - char *yyfmt; - char const *yyf; - static char const yyunexpected[] = "syntax error, unexpected %s"; - static char const yyexpecting[] = ", expecting %s"; - static char const yyor[] = " or %s"; - char yyformat[sizeof yyunexpected - + sizeof yyexpecting - 1 - + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) - * (sizeof yyor - 1))]; - char const *yyprefix = yyexpecting; - - /* Start YYX at -YYN if negative to avoid negative indexes in - YYCHECK. */ - int yyxbegin = yyn < 0 ? -yyn : 0; - - /* Stay within bounds of both yycheck and yytname. */ - int yychecklim = YYLAST - yyn + 1; - int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; - int yycount = 1; - - yyarg[0] = yytname[yytype]; - yyfmt = yystpcpy (yyformat, yyunexpected); - - for (yyx = yyxbegin; yyx < yyxend; ++yyx) - if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) - { - if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) - { - yycount = 1; - yysize = yysize0; - yyformat[sizeof yyunexpected - 1] = '\0'; - break; - } - yyarg[yycount++] = yytname[yyx]; - yysize1 = yysize + yytnamerr (0, yytname[yyx]); - yysize_overflow |= (yysize1 < yysize); - yysize = yysize1; - yyfmt = yystpcpy (yyfmt, yyprefix); - yyprefix = yyor; - } + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) +{ + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = 0; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; + + /* There are many possibilities here to consider: + - Assume YYFAIL is not used. It's too flawed to consider. See + <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html> + for details. YYERROR is fine as it does not invoke this + function. + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } - yyf = YY_(yyformat); - yysize1 = yysize + yystrlen (yyf); - yysize_overflow |= (yysize1 < yysize); - yysize = yysize1; + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } - if (yysize_overflow) - return YYSIZE_MAXIMUM; + yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; - if (yyresult) - { - /* Avoid sprintf, as that infringes on the user's name space. - Don't have undefined behavior even if the translation - produced a string with the wrong number of "%s"s. */ - char *yyp = yyresult; - int yyi = 0; - while ((*yyp = *yyf) != '\0') - { - if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) - { - yyp += yytnamerr (yyp, yyarg[yyi++]); - yyf += 2; - } - else - { - yyp++; - yyf++; - } - } - } - return yysize; + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; } #endif /* YYERROR_VERBOSE */ - /*-----------------------------------------------. | Release the memory associated to this symbol. | @@ -2063,10 +2134,9 @@ yydestruct (yymsg, yytype, yyvaluep) break; } } - -/* Prevent warnings from -Wmissing-prototypes. */ +/* Prevent warnings from -Wmissing-prototypes. */ #ifdef YYPARSE_PARAM #if defined __STDC__ || defined __cplusplus int yyparse (void *YYPARSE_PARAM); @@ -2082,18 +2152,16 @@ int yyparse (); #endif /* ! YYPARSE_PARAM */ - -/* The look-ahead symbol. */ +/* The lookahead symbol. */ int yychar, yystate; -/* The semantic value of the look-ahead symbol. */ +/* The semantic value of the lookahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; - /*----------. | yyparse. | `----------*/ @@ -2120,65 +2188,65 @@ yyparse () #endif #endif { - - int yyn; - int yyresult; - /* Number of tokens to shift before error messages enabled. */ - int yyerrstatus; - /* Look-ahead token as an internal (translated) token number. */ - int yytoken = 0; -#if YYERROR_VERBOSE - /* Buffer for error messages, and its allocated size. */ - char yymsgbuf[128]; - char *yymsg = yymsgbuf; - YYSIZE_T yymsg_alloc = sizeof yymsgbuf; -#endif - - /* Three stacks and their tools: - `yyss': related to states, - `yyvs': related to semantic values, - `yyls': related to locations. + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; - Refer to the stacks thru separate pointers, to allow yyoverflow - to reallocate them elsewhere. */ + /* The stacks and their tools: + `yyss': related to states. + `yyvs': related to semantic values. - /* The state stack. */ - yytype_int16 yyssa[YYINITDEPTH]; - yytype_int16 *yyss = yyssa; - yytype_int16 *yyssp; + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ - /* The semantic value stack. */ - YYSTYPE yyvsa[YYINITDEPTH]; - YYSTYPE *yyvs = yyvsa; - YYSTYPE *yyvsp; + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss; + yytype_int16 *yyssp; + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + YYSIZE_T yystacksize; -#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) - - YYSIZE_T yystacksize = YYINITDEPTH; - + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; + yytoken = 0; + yyss = yyssa; + yyvs = yyvsa; + yystacksize = YYINITDEPTH; + YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; - yychar = YYEMPTY; /* Cause a token to be read. */ + yychar = YYEMPTY; /* Cause a token to be read. */ /* Initialize stack pointers. Waste one element of value and location stack so that they stay on the same level as the state stack. The wasted elements are never initialized. */ - yyssp = yyss; yyvsp = yyvs; @@ -2208,7 +2276,6 @@ yyparse () YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; - /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might @@ -2216,7 +2283,6 @@ yyparse () yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), - &yystacksize); yyss = yyss1; @@ -2239,9 +2305,8 @@ yyparse () (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; - YYSTACK_RELOCATE (yyss); - YYSTACK_RELOCATE (yyvs); - + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); @@ -2252,7 +2317,6 @@ yyparse () yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; - YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); @@ -2262,6 +2326,9 @@ yyparse () YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + if (yystate == YYFINAL) + YYACCEPT; + goto yybackup; /*-----------. @@ -2270,16 +2337,16 @@ yyparse () yybackup: /* Do appropriate processing given the current state. Read a - look-ahead token if we need one and don't already have one. */ + lookahead token if we need one and don't already have one. */ - /* First try to decide what to do without reference to look-ahead token. */ + /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; - if (yyn == YYPACT_NINF) + if (yypact_value_is_default (yyn)) goto yydefault; - /* Not known => get a look-ahead token if don't already have one. */ + /* Not known => get a lookahead token if don't already have one. */ - /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); @@ -2305,26 +2372,22 @@ yybackup: yyn = yytable[yyn]; if (yyn <= 0) { - if (yyn == 0 || yyn == YYTABLE_NINF) - goto yyerrlab; + if (yytable_value_is_error (yyn)) + goto yyerrlab; yyn = -yyn; goto yyreduce; } - if (yyn == YYFINAL) - YYACCEPT; - /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; - /* Shift the look-ahead token. */ + /* Shift the lookahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); - /* Discard the shifted token unless it is eof. */ - if (yychar != YYEOF) - yychar = YYEMPTY; + /* Discard the shifted token. */ + yychar = YYEMPTY; yystate = yyn; *++yyvsp = yylval; @@ -2364,6 +2427,8 @@ yyreduce: switch (yyn) { case 2: + +/* Line 1806 of yacc.c */ #line 128 "go.y" { xtop = concat(xtop, (yyvsp[(4) - (4)].list)); @@ -2371,24 +2436,29 @@ yyreduce: break; case 3: + +/* Line 1806 of yacc.c */ #line 134 "go.y" { prevlineno = lineno; yyerror("package statement must be first"); - flusherrors(); - mkpackage("main"); + errorexit(); } break; case 4: -#line 141 "go.y" + +/* Line 1806 of yacc.c */ +#line 140 "go.y" { mkpackage((yyvsp[(2) - (3)].sym)->name); } break; case 5: -#line 151 "go.y" + +/* Line 1806 of yacc.c */ +#line 150 "go.y" { importpkg = runtimepkg; @@ -2401,14 +2471,18 @@ yyreduce: break; case 6: -#line 162 "go.y" + +/* Line 1806 of yacc.c */ +#line 161 "go.y" { importpkg = nil; } break; case 12: -#line 176 "go.y" + +/* Line 1806 of yacc.c */ +#line 175 "go.y" { Pkg *ipkg; Sym *my; @@ -2431,6 +2505,10 @@ yyreduce: importdot(ipkg, pack); break; } + if(strcmp(my->name, "init") == 0) { + yyerror("cannot import package as init - init must be a func"); + break; + } if(my->name[0] == '_' && my->name[1] == '\0') break; if(my->def) { @@ -2444,7 +2522,9 @@ yyreduce: break; case 13: -#line 209 "go.y" + +/* Line 1806 of yacc.c */ +#line 212 "go.y" { // When an invalid import path is passed to importfile, // it calls yyerror and then sets up a fake import with @@ -2456,7 +2536,9 @@ yyreduce: break; case 16: -#line 224 "go.y" + +/* Line 1806 of yacc.c */ +#line 227 "go.y" { // import with original name (yyval.i) = parserline(); @@ -2466,7 +2548,9 @@ yyreduce: break; case 17: -#line 231 "go.y" + +/* Line 1806 of yacc.c */ +#line 234 "go.y" { // import with given name (yyval.i) = parserline(); @@ -2476,7 +2560,9 @@ yyreduce: break; case 18: -#line 238 "go.y" + +/* Line 1806 of yacc.c */ +#line 241 "go.y" { // import into my name space (yyval.i) = parserline(); @@ -2486,7 +2572,9 @@ yyreduce: break; case 19: -#line 247 "go.y" + +/* Line 1806 of yacc.c */ +#line 250 "go.y" { if(importpkg->name == nil) { importpkg->name = (yyvsp[(2) - (4)].sym)->name; @@ -2502,7 +2590,9 @@ yyreduce: break; case 21: -#line 262 "go.y" + +/* Line 1806 of yacc.c */ +#line 265 "go.y" { if(strcmp((yyvsp[(1) - (1)].sym)->name, "safe") == 0) curio.importsafe = 1; @@ -2510,14 +2600,18 @@ yyreduce: break; case 22: -#line 268 "go.y" + +/* Line 1806 of yacc.c */ +#line 271 "go.y" { defercheckwidth(); } break; case 23: -#line 272 "go.y" + +/* Line 1806 of yacc.c */ +#line 275 "go.y" { resumecheckwidth(); unimportfile(); @@ -2525,7 +2619,9 @@ yyreduce: break; case 24: -#line 281 "go.y" + +/* Line 1806 of yacc.c */ +#line 284 "go.y" { yyerror("empty top-level declaration"); (yyval.list) = nil; @@ -2533,14 +2629,18 @@ yyreduce: break; case 26: -#line 287 "go.y" + +/* Line 1806 of yacc.c */ +#line 290 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 27: -#line 291 "go.y" + +/* Line 1806 of yacc.c */ +#line 294 "go.y" { yyerror("non-declaration statement outside function body"); (yyval.list) = nil; @@ -2548,35 +2648,45 @@ yyreduce: break; case 28: -#line 296 "go.y" + +/* Line 1806 of yacc.c */ +#line 299 "go.y" { (yyval.list) = nil; } break; case 29: -#line 302 "go.y" + +/* Line 1806 of yacc.c */ +#line 305 "go.y" { (yyval.list) = (yyvsp[(2) - (2)].list); } break; case 30: -#line 306 "go.y" + +/* Line 1806 of yacc.c */ +#line 309 "go.y" { (yyval.list) = (yyvsp[(3) - (5)].list); } break; case 31: -#line 310 "go.y" + +/* Line 1806 of yacc.c */ +#line 313 "go.y" { (yyval.list) = nil; } break; case 32: -#line 314 "go.y" + +/* Line 1806 of yacc.c */ +#line 317 "go.y" { (yyval.list) = (yyvsp[(2) - (2)].list); iota = -100000; @@ -2585,7 +2695,9 @@ yyreduce: break; case 33: -#line 320 "go.y" + +/* Line 1806 of yacc.c */ +#line 323 "go.y" { (yyval.list) = (yyvsp[(3) - (5)].list); iota = -100000; @@ -2594,7 +2706,9 @@ yyreduce: break; case 34: -#line 326 "go.y" + +/* Line 1806 of yacc.c */ +#line 329 "go.y" { (yyval.list) = concat((yyvsp[(3) - (7)].list), (yyvsp[(5) - (7)].list)); iota = -100000; @@ -2603,7 +2717,9 @@ yyreduce: break; case 35: -#line 332 "go.y" + +/* Line 1806 of yacc.c */ +#line 335 "go.y" { (yyval.list) = nil; iota = -100000; @@ -2611,84 +2727,108 @@ yyreduce: break; case 36: -#line 337 "go.y" + +/* Line 1806 of yacc.c */ +#line 340 "go.y" { (yyval.list) = list1((yyvsp[(2) - (2)].node)); } break; case 37: -#line 341 "go.y" + +/* Line 1806 of yacc.c */ +#line 344 "go.y" { (yyval.list) = (yyvsp[(3) - (5)].list); } break; case 38: -#line 345 "go.y" + +/* Line 1806 of yacc.c */ +#line 348 "go.y" { (yyval.list) = nil; } break; case 39: -#line 351 "go.y" + +/* Line 1806 of yacc.c */ +#line 354 "go.y" { iota = 0; } break; case 40: -#line 357 "go.y" + +/* Line 1806 of yacc.c */ +#line 360 "go.y" { (yyval.list) = variter((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node), nil); } break; case 41: -#line 361 "go.y" + +/* Line 1806 of yacc.c */ +#line 364 "go.y" { (yyval.list) = variter((yyvsp[(1) - (4)].list), (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].list)); } break; case 42: -#line 365 "go.y" + +/* Line 1806 of yacc.c */ +#line 368 "go.y" { (yyval.list) = variter((yyvsp[(1) - (3)].list), nil, (yyvsp[(3) - (3)].list)); } break; case 43: -#line 371 "go.y" + +/* Line 1806 of yacc.c */ +#line 374 "go.y" { (yyval.list) = constiter((yyvsp[(1) - (4)].list), (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].list)); } break; case 44: -#line 375 "go.y" + +/* Line 1806 of yacc.c */ +#line 378 "go.y" { (yyval.list) = constiter((yyvsp[(1) - (3)].list), N, (yyvsp[(3) - (3)].list)); } break; case 46: -#line 382 "go.y" + +/* Line 1806 of yacc.c */ +#line 385 "go.y" { (yyval.list) = constiter((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node), nil); } break; case 47: -#line 386 "go.y" + +/* Line 1806 of yacc.c */ +#line 389 "go.y" { (yyval.list) = constiter((yyvsp[(1) - (1)].list), N, nil); } break; case 48: -#line 392 "go.y" + +/* Line 1806 of yacc.c */ +#line 395 "go.y" { // different from dclname because the name // becomes visible right here, not at the end @@ -2698,14 +2838,18 @@ yyreduce: break; case 49: -#line 401 "go.y" + +/* Line 1806 of yacc.c */ +#line 404 "go.y" { (yyval.node) = typedcl1((yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node), 1); } break; case 50: -#line 407 "go.y" + +/* Line 1806 of yacc.c */ +#line 410 "go.y" { (yyval.node) = (yyvsp[(1) - (1)].node); @@ -2726,7 +2870,9 @@ yyreduce: break; case 51: -#line 425 "go.y" + +/* Line 1806 of yacc.c */ +#line 428 "go.y" { (yyval.node) = nod(OASOP, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); (yyval.node)->etype = (yyvsp[(2) - (3)].i); // rathole to pass opcode @@ -2734,7 +2880,9 @@ yyreduce: break; case 52: -#line 430 "go.y" + +/* Line 1806 of yacc.c */ +#line 433 "go.y" { if((yyvsp[(1) - (3)].list)->next == nil && (yyvsp[(3) - (3)].list)->next == nil) { // simple @@ -2749,7 +2897,9 @@ yyreduce: break; case 53: -#line 442 "go.y" + +/* Line 1806 of yacc.c */ +#line 445 "go.y" { if((yyvsp[(3) - (3)].list)->n->op == OTYPESW) { (yyval.node) = nod(OTYPESW, N, (yyvsp[(3) - (3)].list)->n->right); @@ -2768,7 +2918,9 @@ yyreduce: break; case 54: -#line 458 "go.y" + +/* Line 1806 of yacc.c */ +#line 461 "go.y" { (yyval.node) = nod(OASOP, (yyvsp[(1) - (2)].node), nodintconst(1)); (yyval.node)->etype = OADD; @@ -2776,7 +2928,9 @@ yyreduce: break; case 55: -#line 463 "go.y" + +/* Line 1806 of yacc.c */ +#line 466 "go.y" { (yyval.node) = nod(OASOP, (yyvsp[(1) - (2)].node), nodintconst(1)); (yyval.node)->etype = OSUB; @@ -2784,7 +2938,9 @@ yyreduce: break; case 56: -#line 470 "go.y" + +/* Line 1806 of yacc.c */ +#line 473 "go.y" { Node *n, *nn; @@ -2807,7 +2963,9 @@ yyreduce: break; case 57: -#line 490 "go.y" + +/* Line 1806 of yacc.c */ +#line 493 "go.y" { Node *n; @@ -2828,7 +2986,9 @@ yyreduce: break; case 58: -#line 508 "go.y" + +/* Line 1806 of yacc.c */ +#line 511 "go.y" { // will be converted to OCASE // right will point to next case @@ -2840,7 +3000,9 @@ yyreduce: break; case 59: -#line 517 "go.y" + +/* Line 1806 of yacc.c */ +#line 520 "go.y" { Node *n, *nn; @@ -2859,14 +3021,18 @@ yyreduce: break; case 60: -#line 535 "go.y" + +/* Line 1806 of yacc.c */ +#line 538 "go.y" { markdcl(); } break; case 61: -#line 539 "go.y" + +/* Line 1806 of yacc.c */ +#line 542 "go.y" { if((yyvsp[(3) - (4)].list) == nil) (yyval.node) = nod(OEMPTY, N, N); @@ -2877,7 +3043,9 @@ yyreduce: break; case 62: -#line 549 "go.y" + +/* Line 1806 of yacc.c */ +#line 552 "go.y" { // If the last token read by the lexer was consumed // as part of the case, clear it (parser has cleared yychar). @@ -2890,7 +3058,9 @@ yyreduce: break; case 63: -#line 559 "go.y" + +/* Line 1806 of yacc.c */ +#line 562 "go.y" { int last; @@ -2912,28 +3082,36 @@ yyreduce: break; case 64: -#line 579 "go.y" + +/* Line 1806 of yacc.c */ +#line 582 "go.y" { (yyval.list) = nil; } break; case 65: -#line 583 "go.y" + +/* Line 1806 of yacc.c */ +#line 586 "go.y" { (yyval.list) = list((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node)); } break; case 66: -#line 589 "go.y" + +/* Line 1806 of yacc.c */ +#line 592 "go.y" { markdcl(); } break; case 67: -#line 593 "go.y" + +/* Line 1806 of yacc.c */ +#line 596 "go.y" { (yyval.list) = (yyvsp[(3) - (4)].list); popdcl(); @@ -2941,7 +3119,9 @@ yyreduce: break; case 68: -#line 600 "go.y" + +/* Line 1806 of yacc.c */ +#line 603 "go.y" { (yyval.node) = nod(ORANGE, N, (yyvsp[(4) - (4)].node)); (yyval.node)->list = (yyvsp[(1) - (4)].list); @@ -2950,7 +3130,9 @@ yyreduce: break; case 69: -#line 606 "go.y" + +/* Line 1806 of yacc.c */ +#line 609 "go.y" { (yyval.node) = nod(ORANGE, N, (yyvsp[(4) - (4)].node)); (yyval.node)->list = (yyvsp[(1) - (4)].list); @@ -2960,7 +3142,9 @@ yyreduce: break; case 70: -#line 615 "go.y" + +/* Line 1806 of yacc.c */ +#line 618 "go.y" { // init ; test ; incr if((yyvsp[(5) - (5)].node) != N && (yyvsp[(5) - (5)].node)->colas != 0) @@ -2974,7 +3158,9 @@ yyreduce: break; case 71: -#line 626 "go.y" + +/* Line 1806 of yacc.c */ +#line 629 "go.y" { // normal test (yyval.node) = nod(OFOR, N, N); @@ -2983,7 +3169,9 @@ yyreduce: break; case 73: -#line 635 "go.y" + +/* Line 1806 of yacc.c */ +#line 638 "go.y" { (yyval.node) = (yyvsp[(1) - (2)].node); (yyval.node)->nbody = concat((yyval.node)->nbody, (yyvsp[(2) - (2)].list)); @@ -2991,14 +3179,18 @@ yyreduce: break; case 74: -#line 642 "go.y" + +/* Line 1806 of yacc.c */ +#line 645 "go.y" { markdcl(); } break; case 75: -#line 646 "go.y" + +/* Line 1806 of yacc.c */ +#line 649 "go.y" { (yyval.node) = (yyvsp[(3) - (3)].node); popdcl(); @@ -3006,7 +3198,9 @@ yyreduce: break; case 76: -#line 653 "go.y" + +/* Line 1806 of yacc.c */ +#line 656 "go.y" { // test (yyval.node) = nod(OIF, N, N); @@ -3015,7 +3209,9 @@ yyreduce: break; case 77: -#line 659 "go.y" + +/* Line 1806 of yacc.c */ +#line 662 "go.y" { // init ; test (yyval.node) = nod(OIF, N, N); @@ -3026,14 +3222,18 @@ yyreduce: break; case 78: -#line 670 "go.y" + +/* Line 1806 of yacc.c */ +#line 673 "go.y" { markdcl(); } break; case 79: -#line 674 "go.y" + +/* Line 1806 of yacc.c */ +#line 677 "go.y" { if((yyvsp[(3) - (3)].node)->ntest == N) yyerror("missing condition in if statement"); @@ -3041,14 +3241,18 @@ yyreduce: break; case 80: -#line 679 "go.y" + +/* Line 1806 of yacc.c */ +#line 682 "go.y" { (yyvsp[(3) - (5)].node)->nbody = (yyvsp[(5) - (5)].list); } break; case 81: -#line 683 "go.y" + +/* Line 1806 of yacc.c */ +#line 686 "go.y" { Node *n; NodeList *nn; @@ -3066,14 +3270,18 @@ yyreduce: break; case 82: -#line 700 "go.y" + +/* Line 1806 of yacc.c */ +#line 703 "go.y" { markdcl(); } break; case 83: -#line 704 "go.y" + +/* Line 1806 of yacc.c */ +#line 707 "go.y" { if((yyvsp[(4) - (5)].node)->ntest == N) yyerror("missing condition in if statement"); @@ -3083,28 +3291,36 @@ yyreduce: break; case 84: -#line 712 "go.y" + +/* Line 1806 of yacc.c */ +#line 715 "go.y" { (yyval.list) = nil; } break; case 85: -#line 716 "go.y" + +/* Line 1806 of yacc.c */ +#line 719 "go.y" { (yyval.list) = concat((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].list)); } break; case 86: -#line 721 "go.y" + +/* Line 1806 of yacc.c */ +#line 724 "go.y" { (yyval.list) = nil; } break; case 87: -#line 725 "go.y" + +/* Line 1806 of yacc.c */ +#line 728 "go.y" { NodeList *node; @@ -3116,14 +3332,18 @@ yyreduce: break; case 88: -#line 736 "go.y" + +/* Line 1806 of yacc.c */ +#line 739 "go.y" { markdcl(); } break; case 89: -#line 740 "go.y" + +/* Line 1806 of yacc.c */ +#line 743 "go.y" { Node *n; n = (yyvsp[(3) - (3)].node)->ntest; @@ -3134,7 +3354,9 @@ yyreduce: break; case 90: -#line 748 "go.y" + +/* Line 1806 of yacc.c */ +#line 751 "go.y" { (yyval.node) = (yyvsp[(3) - (7)].node); (yyval.node)->op = OSWITCH; @@ -3145,14 +3367,18 @@ yyreduce: break; case 91: -#line 758 "go.y" + +/* Line 1806 of yacc.c */ +#line 761 "go.y" { typesw = nod(OXXX, typesw, N); } break; case 92: -#line 762 "go.y" + +/* Line 1806 of yacc.c */ +#line 765 "go.y" { (yyval.node) = nod(OSELECT, N, N); (yyval.node)->lineno = typesw->lineno; @@ -3162,154 +3388,198 @@ yyreduce: break; case 94: -#line 775 "go.y" + +/* Line 1806 of yacc.c */ +#line 778 "go.y" { (yyval.node) = nod(OOROR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 95: -#line 779 "go.y" + +/* Line 1806 of yacc.c */ +#line 782 "go.y" { (yyval.node) = nod(OANDAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 96: -#line 783 "go.y" + +/* Line 1806 of yacc.c */ +#line 786 "go.y" { (yyval.node) = nod(OEQ, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 97: -#line 787 "go.y" + +/* Line 1806 of yacc.c */ +#line 790 "go.y" { (yyval.node) = nod(ONE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 98: -#line 791 "go.y" + +/* Line 1806 of yacc.c */ +#line 794 "go.y" { (yyval.node) = nod(OLT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 99: -#line 795 "go.y" + +/* Line 1806 of yacc.c */ +#line 798 "go.y" { (yyval.node) = nod(OLE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 100: -#line 799 "go.y" + +/* Line 1806 of yacc.c */ +#line 802 "go.y" { (yyval.node) = nod(OGE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 101: -#line 803 "go.y" + +/* Line 1806 of yacc.c */ +#line 806 "go.y" { (yyval.node) = nod(OGT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 102: -#line 807 "go.y" + +/* Line 1806 of yacc.c */ +#line 810 "go.y" { (yyval.node) = nod(OADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 103: -#line 811 "go.y" + +/* Line 1806 of yacc.c */ +#line 814 "go.y" { (yyval.node) = nod(OSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 104: -#line 815 "go.y" + +/* Line 1806 of yacc.c */ +#line 818 "go.y" { (yyval.node) = nod(OOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 105: -#line 819 "go.y" + +/* Line 1806 of yacc.c */ +#line 822 "go.y" { (yyval.node) = nod(OXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 106: -#line 823 "go.y" + +/* Line 1806 of yacc.c */ +#line 826 "go.y" { (yyval.node) = nod(OMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 107: -#line 827 "go.y" + +/* Line 1806 of yacc.c */ +#line 830 "go.y" { (yyval.node) = nod(ODIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 108: -#line 831 "go.y" + +/* Line 1806 of yacc.c */ +#line 834 "go.y" { (yyval.node) = nod(OMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 109: -#line 835 "go.y" + +/* Line 1806 of yacc.c */ +#line 838 "go.y" { (yyval.node) = nod(OAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 110: -#line 839 "go.y" + +/* Line 1806 of yacc.c */ +#line 842 "go.y" { (yyval.node) = nod(OANDNOT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 111: -#line 843 "go.y" + +/* Line 1806 of yacc.c */ +#line 846 "go.y" { (yyval.node) = nod(OLSH, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 112: -#line 847 "go.y" + +/* Line 1806 of yacc.c */ +#line 850 "go.y" { (yyval.node) = nod(ORSH, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 113: -#line 852 "go.y" + +/* Line 1806 of yacc.c */ +#line 855 "go.y" { (yyval.node) = nod(OSEND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 115: -#line 859 "go.y" + +/* Line 1806 of yacc.c */ +#line 862 "go.y" { (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N); } break; case 116: -#line 863 "go.y" + +/* Line 1806 of yacc.c */ +#line 866 "go.y" { if((yyvsp[(2) - (2)].node)->op == OCOMPLIT) { // Special case for &T{...}: turn into (*T){...}. @@ -3323,28 +3593,36 @@ yyreduce: break; case 117: -#line 874 "go.y" + +/* Line 1806 of yacc.c */ +#line 877 "go.y" { (yyval.node) = nod(OPLUS, (yyvsp[(2) - (2)].node), N); } break; case 118: -#line 878 "go.y" + +/* Line 1806 of yacc.c */ +#line 881 "go.y" { (yyval.node) = nod(OMINUS, (yyvsp[(2) - (2)].node), N); } break; case 119: -#line 882 "go.y" + +/* Line 1806 of yacc.c */ +#line 885 "go.y" { (yyval.node) = nod(ONOT, (yyvsp[(2) - (2)].node), N); } break; case 120: -#line 886 "go.y" + +/* Line 1806 of yacc.c */ +#line 889 "go.y" { yyerror("the bitwise complement operator is ^"); (yyval.node) = nod(OCOM, (yyvsp[(2) - (2)].node), N); @@ -3352,28 +3630,36 @@ yyreduce: break; case 121: -#line 891 "go.y" + +/* Line 1806 of yacc.c */ +#line 894 "go.y" { (yyval.node) = nod(OCOM, (yyvsp[(2) - (2)].node), N); } break; case 122: -#line 895 "go.y" + +/* Line 1806 of yacc.c */ +#line 898 "go.y" { (yyval.node) = nod(ORECV, (yyvsp[(2) - (2)].node), N); } break; case 123: -#line 905 "go.y" + +/* Line 1806 of yacc.c */ +#line 908 "go.y" { (yyval.node) = nod(OCALL, (yyvsp[(1) - (3)].node), N); } break; case 124: -#line 909 "go.y" + +/* Line 1806 of yacc.c */ +#line 912 "go.y" { (yyval.node) = nod(OCALL, (yyvsp[(1) - (5)].node), N); (yyval.node)->list = (yyvsp[(3) - (5)].list); @@ -3381,7 +3667,9 @@ yyreduce: break; case 125: -#line 914 "go.y" + +/* Line 1806 of yacc.c */ +#line 917 "go.y" { (yyval.node) = nod(OCALL, (yyvsp[(1) - (6)].node), N); (yyval.node)->list = (yyvsp[(3) - (6)].list); @@ -3390,14 +3678,18 @@ yyreduce: break; case 126: -#line 922 "go.y" + +/* Line 1806 of yacc.c */ +#line 925 "go.y" { (yyval.node) = nodlit((yyvsp[(1) - (1)].val)); } break; case 128: -#line 927 "go.y" + +/* Line 1806 of yacc.c */ +#line 930 "go.y" { if((yyvsp[(1) - (3)].node)->op == OPACK) { Sym *s; @@ -3411,35 +3703,58 @@ yyreduce: break; case 129: -#line 938 "go.y" + +/* Line 1806 of yacc.c */ +#line 941 "go.y" { (yyval.node) = nod(ODOTTYPE, (yyvsp[(1) - (5)].node), (yyvsp[(4) - (5)].node)); } break; case 130: -#line 942 "go.y" + +/* Line 1806 of yacc.c */ +#line 945 "go.y" { (yyval.node) = nod(OTYPESW, N, (yyvsp[(1) - (5)].node)); } break; case 131: -#line 946 "go.y" + +/* Line 1806 of yacc.c */ +#line 949 "go.y" { (yyval.node) = nod(OINDEX, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node)); } break; case 132: -#line 950 "go.y" + +/* Line 1806 of yacc.c */ +#line 953 "go.y" { (yyval.node) = nod(OSLICE, (yyvsp[(1) - (6)].node), nod(OKEY, (yyvsp[(3) - (6)].node), (yyvsp[(5) - (6)].node))); } break; - case 134: -#line 955 "go.y" + case 133: + +/* Line 1806 of yacc.c */ +#line 957 "go.y" + { + if((yyvsp[(5) - (8)].node) == N) + yyerror("middle index required in 3-index slice"); + if((yyvsp[(7) - (8)].node) == N) + yyerror("final index required in 3-index slice"); + (yyval.node) = nod(OSLICE3, (yyvsp[(1) - (8)].node), nod(OKEY, (yyvsp[(3) - (8)].node), nod(OKEY, (yyvsp[(5) - (8)].node), (yyvsp[(7) - (8)].node)))); + } + break; + + case 135: + +/* Line 1806 of yacc.c */ +#line 966 "go.y" { // conversion (yyval.node) = nod(OCALL, (yyvsp[(1) - (5)].node), N); @@ -3447,8 +3762,10 @@ yyreduce: } break; - case 135: -#line 961 "go.y" + case 136: + +/* Line 1806 of yacc.c */ +#line 972 "go.y" { (yyval.node) = (yyvsp[(3) - (5)].node); (yyval.node)->right = (yyvsp[(1) - (5)].node); @@ -3457,8 +3774,10 @@ yyreduce: } break; - case 136: -#line 968 "go.y" + case 137: + +/* Line 1806 of yacc.c */ +#line 979 "go.y" { (yyval.node) = (yyvsp[(3) - (5)].node); (yyval.node)->right = (yyvsp[(1) - (5)].node); @@ -3466,8 +3785,10 @@ yyreduce: } break; - case 137: -#line 974 "go.y" + case 138: + +/* Line 1806 of yacc.c */ +#line 985 "go.y" { yyerror("cannot parenthesize type in composite literal"); (yyval.node) = (yyvsp[(5) - (7)].node); @@ -3476,8 +3797,10 @@ yyreduce: } break; - case 139: -#line 983 "go.y" + case 140: + +/* Line 1806 of yacc.c */ +#line 994 "go.y" { // composite expression. // make node early so we get the right line number. @@ -3485,15 +3808,19 @@ yyreduce: } break; - case 140: -#line 991 "go.y" + case 141: + +/* Line 1806 of yacc.c */ +#line 1002 "go.y" { (yyval.node) = nod(OKEY, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 141: -#line 997 "go.y" + case 142: + +/* Line 1806 of yacc.c */ +#line 1008 "go.y" { // These nodes do not carry line numbers. // Since a composite literal commonly spans several lines, @@ -3512,24 +3839,30 @@ yyreduce: } break; - case 142: -#line 1014 "go.y" + case 143: + +/* Line 1806 of yacc.c */ +#line 1025 "go.y" { (yyval.node) = (yyvsp[(2) - (4)].node); (yyval.node)->list = (yyvsp[(3) - (4)].list); } break; - case 144: -#line 1022 "go.y" + case 145: + +/* Line 1806 of yacc.c */ +#line 1033 "go.y" { (yyval.node) = (yyvsp[(2) - (4)].node); (yyval.node)->list = (yyvsp[(3) - (4)].list); } break; - case 146: -#line 1030 "go.y" + case 147: + +/* Line 1806 of yacc.c */ +#line 1041 "go.y" { (yyval.node) = (yyvsp[(2) - (3)].node); @@ -3548,22 +3881,28 @@ yyreduce: } break; - case 150: -#line 1056 "go.y" + case 151: + +/* Line 1806 of yacc.c */ +#line 1067 "go.y" { (yyval.i) = LBODY; } break; - case 151: -#line 1060 "go.y" + case 152: + +/* Line 1806 of yacc.c */ +#line 1071 "go.y" { (yyval.i) = '{'; } break; - case 152: -#line 1071 "go.y" + case 153: + +/* Line 1806 of yacc.c */ +#line 1082 "go.y" { if((yyvsp[(1) - (1)].sym) == S) (yyval.node) = N; @@ -3572,22 +3911,28 @@ yyreduce: } break; - case 153: -#line 1080 "go.y" + case 154: + +/* Line 1806 of yacc.c */ +#line 1091 "go.y" { (yyval.node) = dclname((yyvsp[(1) - (1)].sym)); } break; - case 154: -#line 1085 "go.y" + case 155: + +/* Line 1806 of yacc.c */ +#line 1096 "go.y" { (yyval.node) = N; } break; - case 156: -#line 1092 "go.y" + case 157: + +/* Line 1806 of yacc.c */ +#line 1103 "go.y" { (yyval.sym) = (yyvsp[(1) - (1)].sym); // during imports, unqualified non-exported identifiers are from builtinpkg @@ -3596,15 +3941,19 @@ yyreduce: } break; - case 158: -#line 1100 "go.y" + case 159: + +/* Line 1806 of yacc.c */ +#line 1111 "go.y" { (yyval.sym) = S; } break; - case 159: -#line 1106 "go.y" + case 160: + +/* Line 1806 of yacc.c */ +#line 1117 "go.y" { Pkg *p; @@ -3619,8 +3968,28 @@ yyreduce: } break; - case 160: -#line 1121 "go.y" + case 161: + +/* Line 1806 of yacc.c */ +#line 1130 "go.y" + { + Pkg *p; + + if((yyvsp[(2) - (4)].val).u.sval->len == 0) + p = importpkg; + else { + if(isbadimport((yyvsp[(2) - (4)].val).u.sval)) + errorexit(); + p = mkpkg((yyvsp[(2) - (4)].val).u.sval); + } + (yyval.sym) = pkglookup("?", p); + } + break; + + case 162: + +/* Line 1806 of yacc.c */ +#line 1145 "go.y" { (yyval.node) = oldname((yyvsp[(1) - (1)].sym)); if((yyval.node)->pack != N) @@ -3628,44 +3997,56 @@ yyreduce: } break; - case 162: -#line 1141 "go.y" + case 164: + +/* Line 1806 of yacc.c */ +#line 1165 "go.y" { yyerror("final argument in variadic function missing type"); (yyval.node) = nod(ODDD, typenod(typ(TINTER)), N); } break; - case 163: -#line 1146 "go.y" + case 165: + +/* Line 1806 of yacc.c */ +#line 1170 "go.y" { (yyval.node) = nod(ODDD, (yyvsp[(2) - (2)].node), N); } break; - case 169: -#line 1157 "go.y" + case 171: + +/* Line 1806 of yacc.c */ +#line 1181 "go.y" { (yyval.node) = nod(OTPAREN, (yyvsp[(2) - (3)].node), N); } break; - case 173: -#line 1166 "go.y" + case 175: + +/* Line 1806 of yacc.c */ +#line 1190 "go.y" { (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N); } break; - case 178: -#line 1176 "go.y" + case 180: + +/* Line 1806 of yacc.c */ +#line 1200 "go.y" { (yyval.node) = nod(OTPAREN, (yyvsp[(2) - (3)].node), N); } break; - case 188: -#line 1197 "go.y" + case 190: + +/* Line 1806 of yacc.c */ +#line 1221 "go.y" { if((yyvsp[(1) - (3)].node)->op == OPACK) { Sym *s; @@ -3678,61 +4059,77 @@ yyreduce: } break; - case 189: -#line 1210 "go.y" + case 191: + +/* Line 1806 of yacc.c */ +#line 1234 "go.y" { (yyval.node) = nod(OTARRAY, (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].node)); } break; - case 190: -#line 1214 "go.y" + case 192: + +/* Line 1806 of yacc.c */ +#line 1238 "go.y" { // array literal of nelem (yyval.node) = nod(OTARRAY, nod(ODDD, N, N), (yyvsp[(4) - (4)].node)); } break; - case 191: -#line 1219 "go.y" + case 193: + +/* Line 1806 of yacc.c */ +#line 1243 "go.y" { (yyval.node) = nod(OTCHAN, (yyvsp[(2) - (2)].node), N); (yyval.node)->etype = Cboth; } break; - case 192: -#line 1224 "go.y" + case 194: + +/* Line 1806 of yacc.c */ +#line 1248 "go.y" { (yyval.node) = nod(OTCHAN, (yyvsp[(3) - (3)].node), N); (yyval.node)->etype = Csend; } break; - case 193: -#line 1229 "go.y" + case 195: + +/* Line 1806 of yacc.c */ +#line 1253 "go.y" { (yyval.node) = nod(OTMAP, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node)); } break; - case 196: -#line 1237 "go.y" + case 198: + +/* Line 1806 of yacc.c */ +#line 1261 "go.y" { (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N); } break; - case 197: -#line 1243 "go.y" + case 199: + +/* Line 1806 of yacc.c */ +#line 1267 "go.y" { (yyval.node) = nod(OTCHAN, (yyvsp[(3) - (3)].node), N); (yyval.node)->etype = Crecv; } break; - case 198: -#line 1250 "go.y" + case 200: + +/* Line 1806 of yacc.c */ +#line 1274 "go.y" { (yyval.node) = nod(OTSTRUCT, N, N); (yyval.node)->list = (yyvsp[(3) - (5)].list); @@ -3740,16 +4137,20 @@ yyreduce: } break; - case 199: -#line 1256 "go.y" + case 201: + +/* Line 1806 of yacc.c */ +#line 1280 "go.y" { (yyval.node) = nod(OTSTRUCT, N, N); fixlbrace((yyvsp[(2) - (3)].i)); } break; - case 200: -#line 1263 "go.y" + case 202: + +/* Line 1806 of yacc.c */ +#line 1287 "go.y" { (yyval.node) = nod(OTINTER, N, N); (yyval.node)->list = (yyvsp[(3) - (5)].list); @@ -3757,16 +4158,20 @@ yyreduce: } break; - case 201: -#line 1269 "go.y" + case 203: + +/* Line 1806 of yacc.c */ +#line 1293 "go.y" { (yyval.node) = nod(OTINTER, N, N); fixlbrace((yyvsp[(2) - (3)].i)); } break; - case 202: -#line 1280 "go.y" + case 204: + +/* Line 1806 of yacc.c */ +#line 1304 "go.y" { (yyval.node) = (yyvsp[(2) - (3)].node); if((yyval.node) == N) @@ -3780,8 +4185,10 @@ yyreduce: } break; - case 203: -#line 1294 "go.y" + case 205: + +/* Line 1806 of yacc.c */ +#line 1318 "go.y" { Node *t; @@ -3812,8 +4219,10 @@ yyreduce: } break; - case 204: -#line 1323 "go.y" + case 206: + +/* Line 1806 of yacc.c */ +#line 1347 "go.y" { Node *rcvr, *t; @@ -3853,8 +4262,10 @@ yyreduce: } break; - case 205: -#line 1363 "go.y" + case 207: + +/* Line 1806 of yacc.c */ +#line 1387 "go.y" { Sym *s; Type *t; @@ -3881,8 +4292,10 @@ yyreduce: } break; - case 206: -#line 1388 "go.y" + case 208: + +/* Line 1806 of yacc.c */ +#line 1412 "go.y" { (yyval.node) = methodname1(newname((yyvsp[(4) - (8)].sym)), (yyvsp[(2) - (8)].list)->n->right); (yyval.node)->type = functype((yyvsp[(2) - (8)].list)->n, (yyvsp[(6) - (8)].list), (yyvsp[(8) - (8)].list)); @@ -3900,8 +4313,10 @@ yyreduce: } break; - case 207: -#line 1406 "go.y" + case 209: + +/* Line 1806 of yacc.c */ +#line 1430 "go.y" { (yyvsp[(3) - (5)].list) = checkarglist((yyvsp[(3) - (5)].list), 1); (yyval.node) = nod(OTFUNC, N, N); @@ -3910,15 +4325,19 @@ yyreduce: } break; - case 208: -#line 1414 "go.y" + case 210: + +/* Line 1806 of yacc.c */ +#line 1438 "go.y" { (yyval.list) = nil; } break; - case 209: -#line 1418 "go.y" + case 211: + +/* Line 1806 of yacc.c */ +#line 1442 "go.y" { (yyval.list) = (yyvsp[(2) - (3)].list); if((yyval.list) == nil) @@ -3926,59 +4345,75 @@ yyreduce: } break; - case 210: -#line 1426 "go.y" + case 212: + +/* Line 1806 of yacc.c */ +#line 1450 "go.y" { (yyval.list) = nil; } break; - case 211: -#line 1430 "go.y" + case 213: + +/* Line 1806 of yacc.c */ +#line 1454 "go.y" { (yyval.list) = list1(nod(ODCLFIELD, N, (yyvsp[(1) - (1)].node))); } break; - case 212: -#line 1434 "go.y" + case 214: + +/* Line 1806 of yacc.c */ +#line 1458 "go.y" { (yyvsp[(2) - (3)].list) = checkarglist((yyvsp[(2) - (3)].list), 0); (yyval.list) = (yyvsp[(2) - (3)].list); } break; - case 213: -#line 1441 "go.y" + case 215: + +/* Line 1806 of yacc.c */ +#line 1465 "go.y" { closurehdr((yyvsp[(1) - (1)].node)); } break; - case 214: -#line 1447 "go.y" + case 216: + +/* Line 1806 of yacc.c */ +#line 1471 "go.y" { (yyval.node) = closurebody((yyvsp[(3) - (4)].list)); fixlbrace((yyvsp[(2) - (4)].i)); } break; - case 215: -#line 1452 "go.y" + case 217: + +/* Line 1806 of yacc.c */ +#line 1476 "go.y" { (yyval.node) = closurebody(nil); } break; - case 216: -#line 1463 "go.y" + case 218: + +/* Line 1806 of yacc.c */ +#line 1487 "go.y" { (yyval.list) = nil; } break; - case 217: -#line 1467 "go.y" + case 219: + +/* Line 1806 of yacc.c */ +#line 1491 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(2) - (3)].list)); if(nsyntaxerrors == 0) @@ -3988,68 +4423,84 @@ yyreduce: } break; - case 219: -#line 1478 "go.y" + case 221: + +/* Line 1806 of yacc.c */ +#line 1502 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list)); } break; - case 221: -#line 1485 "go.y" + case 223: + +/* Line 1806 of yacc.c */ +#line 1509 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list)); } break; - case 222: -#line 1491 "go.y" + case 224: + +/* Line 1806 of yacc.c */ +#line 1515 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 223: -#line 1495 "go.y" + case 225: + +/* Line 1806 of yacc.c */ +#line 1519 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 225: -#line 1502 "go.y" + case 227: + +/* Line 1806 of yacc.c */ +#line 1526 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list)); } break; - case 226: -#line 1508 "go.y" + case 228: + +/* Line 1806 of yacc.c */ +#line 1532 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 227: -#line 1512 "go.y" + case 229: + +/* Line 1806 of yacc.c */ +#line 1536 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 228: -#line 1518 "go.y" + case 230: + +/* Line 1806 of yacc.c */ +#line 1542 "go.y" { NodeList *l; Node *n; l = (yyvsp[(1) - (3)].list); - if(l != nil && l->next == nil && l->n == nil) { - // ? symbol, during import + if(l == nil) { + // ? symbol, during import (list1(N) == nil) n = (yyvsp[(2) - (3)].node); if(n->op == OIND) n = n->left; - n = embedded(n->sym); + n = embedded(n->sym, importpkg); n->right = (yyvsp[(2) - (3)].node); n->val = (yyvsp[(3) - (3)].val); (yyval.list) = list1(n); @@ -4063,16 +4514,20 @@ yyreduce: } break; - case 229: -#line 1541 "go.y" + case 231: + +/* Line 1806 of yacc.c */ +#line 1565 "go.y" { (yyvsp[(1) - (2)].node)->val = (yyvsp[(2) - (2)].val); (yyval.list) = list1((yyvsp[(1) - (2)].node)); } break; - case 230: -#line 1546 "go.y" + case 232: + +/* Line 1806 of yacc.c */ +#line 1570 "go.y" { (yyvsp[(2) - (4)].node)->val = (yyvsp[(4) - (4)].val); (yyval.list) = list1((yyvsp[(2) - (4)].node)); @@ -4080,8 +4535,10 @@ yyreduce: } break; - case 231: -#line 1552 "go.y" + case 233: + +/* Line 1806 of yacc.c */ +#line 1576 "go.y" { (yyvsp[(2) - (3)].node)->right = nod(OIND, (yyvsp[(2) - (3)].node)->right, N); (yyvsp[(2) - (3)].node)->val = (yyvsp[(3) - (3)].val); @@ -4089,8 +4546,10 @@ yyreduce: } break; - case 232: -#line 1558 "go.y" + case 234: + +/* Line 1806 of yacc.c */ +#line 1582 "go.y" { (yyvsp[(3) - (5)].node)->right = nod(OIND, (yyvsp[(3) - (5)].node)->right, N); (yyvsp[(3) - (5)].node)->val = (yyvsp[(5) - (5)].val); @@ -4099,8 +4558,10 @@ yyreduce: } break; - case 233: -#line 1565 "go.y" + case 235: + +/* Line 1806 of yacc.c */ +#line 1589 "go.y" { (yyvsp[(3) - (5)].node)->right = nod(OIND, (yyvsp[(3) - (5)].node)->right, N); (yyvsp[(3) - (5)].node)->val = (yyvsp[(5) - (5)].val); @@ -4109,8 +4570,10 @@ yyreduce: } break; - case 234: -#line 1574 "go.y" + case 236: + +/* Line 1806 of yacc.c */ +#line 1598 "go.y" { Node *n; @@ -4121,8 +4584,10 @@ yyreduce: } break; - case 235: -#line 1583 "go.y" + case 237: + +/* Line 1806 of yacc.c */ +#line 1607 "go.y" { Pkg *pkg; @@ -4137,38 +4602,48 @@ yyreduce: } break; - case 236: -#line 1598 "go.y" + case 238: + +/* Line 1806 of yacc.c */ +#line 1622 "go.y" { - (yyval.node) = embedded((yyvsp[(1) - (1)].sym)); + (yyval.node) = embedded((yyvsp[(1) - (1)].sym), localpkg); } break; - case 237: -#line 1604 "go.y" + case 239: + +/* Line 1806 of yacc.c */ +#line 1628 "go.y" { (yyval.node) = nod(ODCLFIELD, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node)); ifacedcl((yyval.node)); } break; - case 238: -#line 1609 "go.y" + case 240: + +/* Line 1806 of yacc.c */ +#line 1633 "go.y" { (yyval.node) = nod(ODCLFIELD, N, oldname((yyvsp[(1) - (1)].sym))); } break; - case 239: -#line 1613 "go.y" + case 241: + +/* Line 1806 of yacc.c */ +#line 1637 "go.y" { (yyval.node) = nod(ODCLFIELD, N, oldname((yyvsp[(2) - (3)].sym))); yyerror("cannot parenthesize embedded type"); } break; - case 240: -#line 1620 "go.y" + case 242: + +/* Line 1806 of yacc.c */ +#line 1644 "go.y" { // without func keyword (yyvsp[(2) - (4)].list) = checkarglist((yyvsp[(2) - (4)].list), 1); @@ -4178,8 +4653,10 @@ yyreduce: } break; - case 242: -#line 1634 "go.y" + case 244: + +/* Line 1806 of yacc.c */ +#line 1658 "go.y" { (yyval.node) = nod(ONONAME, N, N); (yyval.node)->sym = (yyvsp[(1) - (2)].sym); @@ -4187,8 +4664,10 @@ yyreduce: } break; - case 243: -#line 1640 "go.y" + case 245: + +/* Line 1806 of yacc.c */ +#line 1664 "go.y" { (yyval.node) = nod(ONONAME, N, N); (yyval.node)->sym = (yyvsp[(1) - (2)].sym); @@ -4196,65 +4675,83 @@ yyreduce: } break; - case 245: -#line 1649 "go.y" + case 247: + +/* Line 1806 of yacc.c */ +#line 1673 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 246: -#line 1653 "go.y" + case 248: + +/* Line 1806 of yacc.c */ +#line 1677 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 247: -#line 1658 "go.y" + case 249: + +/* Line 1806 of yacc.c */ +#line 1682 "go.y" { (yyval.list) = nil; } break; - case 248: -#line 1662 "go.y" + case 250: + +/* Line 1806 of yacc.c */ +#line 1686 "go.y" { (yyval.list) = (yyvsp[(1) - (2)].list); } break; - case 249: -#line 1670 "go.y" + case 251: + +/* Line 1806 of yacc.c */ +#line 1694 "go.y" { (yyval.node) = N; } break; - case 251: -#line 1675 "go.y" + case 253: + +/* Line 1806 of yacc.c */ +#line 1699 "go.y" { (yyval.node) = liststmt((yyvsp[(1) - (1)].list)); } break; - case 253: -#line 1680 "go.y" + case 255: + +/* Line 1806 of yacc.c */ +#line 1704 "go.y" { (yyval.node) = N; } break; - case 259: -#line 1691 "go.y" + case 261: + +/* Line 1806 of yacc.c */ +#line 1715 "go.y" { (yyvsp[(1) - (2)].node) = nod(OLABEL, (yyvsp[(1) - (2)].node), N); (yyvsp[(1) - (2)].node)->sym = dclstack; // context, for goto restrictions } break; - case 260: -#line 1696 "go.y" + case 262: + +/* Line 1806 of yacc.c */ +#line 1720 "go.y" { NodeList *l; @@ -4266,52 +4763,66 @@ yyreduce: } break; - case 261: -#line 1706 "go.y" + case 263: + +/* Line 1806 of yacc.c */ +#line 1730 "go.y" { // will be converted to OFALL (yyval.node) = nod(OXFALL, N, N); } break; - case 262: -#line 1711 "go.y" + case 264: + +/* Line 1806 of yacc.c */ +#line 1735 "go.y" { (yyval.node) = nod(OBREAK, (yyvsp[(2) - (2)].node), N); } break; - case 263: -#line 1715 "go.y" + case 265: + +/* Line 1806 of yacc.c */ +#line 1739 "go.y" { (yyval.node) = nod(OCONTINUE, (yyvsp[(2) - (2)].node), N); } break; - case 264: -#line 1719 "go.y" + case 266: + +/* Line 1806 of yacc.c */ +#line 1743 "go.y" { (yyval.node) = nod(OPROC, (yyvsp[(2) - (2)].node), N); } break; - case 265: -#line 1723 "go.y" + case 267: + +/* Line 1806 of yacc.c */ +#line 1747 "go.y" { (yyval.node) = nod(ODEFER, (yyvsp[(2) - (2)].node), N); } break; - case 266: -#line 1727 "go.y" + case 268: + +/* Line 1806 of yacc.c */ +#line 1751 "go.y" { (yyval.node) = nod(OGOTO, (yyvsp[(2) - (2)].node), N); (yyval.node)->sym = dclstack; // context, for goto restrictions } break; - case 267: -#line 1732 "go.y" + case 269: + +/* Line 1806 of yacc.c */ +#line 1756 "go.y" { (yyval.node) = nod(ORETURN, N, N); (yyval.node)->list = (yyvsp[(2) - (2)].list); @@ -4330,8 +4841,10 @@ yyreduce: } break; - case 268: -#line 1751 "go.y" + case 270: + +/* Line 1806 of yacc.c */ +#line 1775 "go.y" { (yyval.list) = nil; if((yyvsp[(1) - (1)].node) != N) @@ -4339,8 +4852,10 @@ yyreduce: } break; - case 269: -#line 1757 "go.y" + case 271: + +/* Line 1806 of yacc.c */ +#line 1781 "go.y" { (yyval.list) = (yyvsp[(1) - (3)].list); if((yyvsp[(3) - (3)].node) != N) @@ -4348,190 +4863,244 @@ yyreduce: } break; - case 270: -#line 1765 "go.y" + case 272: + +/* Line 1806 of yacc.c */ +#line 1789 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 271: -#line 1769 "go.y" + case 273: + +/* Line 1806 of yacc.c */ +#line 1793 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 272: -#line 1775 "go.y" + case 274: + +/* Line 1806 of yacc.c */ +#line 1799 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 273: -#line 1779 "go.y" + case 275: + +/* Line 1806 of yacc.c */ +#line 1803 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 274: -#line 1785 "go.y" + case 276: + +/* Line 1806 of yacc.c */ +#line 1809 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 275: -#line 1789 "go.y" + case 277: + +/* Line 1806 of yacc.c */ +#line 1813 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 276: -#line 1795 "go.y" + case 278: + +/* Line 1806 of yacc.c */ +#line 1819 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 277: -#line 1799 "go.y" + case 279: + +/* Line 1806 of yacc.c */ +#line 1823 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 278: -#line 1808 "go.y" + case 280: + +/* Line 1806 of yacc.c */ +#line 1832 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 279: -#line 1812 "go.y" + case 281: + +/* Line 1806 of yacc.c */ +#line 1836 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 280: -#line 1816 "go.y" + case 282: + +/* Line 1806 of yacc.c */ +#line 1840 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 281: -#line 1820 "go.y" + case 283: + +/* Line 1806 of yacc.c */ +#line 1844 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 282: -#line 1825 "go.y" + case 284: + +/* Line 1806 of yacc.c */ +#line 1849 "go.y" { (yyval.list) = nil; } break; - case 283: -#line 1829 "go.y" + case 285: + +/* Line 1806 of yacc.c */ +#line 1853 "go.y" { (yyval.list) = (yyvsp[(1) - (2)].list); } break; - case 288: -#line 1843 "go.y" + case 290: + +/* Line 1806 of yacc.c */ +#line 1867 "go.y" { (yyval.node) = N; } break; - case 290: -#line 1849 "go.y" + case 292: + +/* Line 1806 of yacc.c */ +#line 1873 "go.y" { (yyval.list) = nil; } break; - case 292: -#line 1855 "go.y" + case 294: + +/* Line 1806 of yacc.c */ +#line 1879 "go.y" { (yyval.node) = N; } break; - case 294: -#line 1861 "go.y" + case 296: + +/* Line 1806 of yacc.c */ +#line 1885 "go.y" { (yyval.list) = nil; } break; - case 296: -#line 1867 "go.y" + case 298: + +/* Line 1806 of yacc.c */ +#line 1891 "go.y" { (yyval.list) = nil; } break; - case 298: -#line 1873 "go.y" + case 300: + +/* Line 1806 of yacc.c */ +#line 1897 "go.y" { (yyval.list) = nil; } break; - case 300: -#line 1879 "go.y" + case 302: + +/* Line 1806 of yacc.c */ +#line 1903 "go.y" { (yyval.val).ctype = CTxxx; } break; - case 302: -#line 1889 "go.y" + case 304: + +/* Line 1806 of yacc.c */ +#line 1913 "go.y" { importimport((yyvsp[(2) - (4)].sym), (yyvsp[(3) - (4)].val).u.sval); } break; - case 303: -#line 1893 "go.y" + case 305: + +/* Line 1806 of yacc.c */ +#line 1917 "go.y" { importvar((yyvsp[(2) - (4)].sym), (yyvsp[(3) - (4)].type)); } break; - case 304: -#line 1897 "go.y" + case 306: + +/* Line 1806 of yacc.c */ +#line 1921 "go.y" { importconst((yyvsp[(2) - (5)].sym), types[TIDEAL], (yyvsp[(4) - (5)].node)); } break; - case 305: -#line 1901 "go.y" + case 307: + +/* Line 1806 of yacc.c */ +#line 1925 "go.y" { importconst((yyvsp[(2) - (6)].sym), (yyvsp[(3) - (6)].type), (yyvsp[(5) - (6)].node)); } break; - case 306: -#line 1905 "go.y" + case 308: + +/* Line 1806 of yacc.c */ +#line 1929 "go.y" { importtype((yyvsp[(2) - (4)].type), (yyvsp[(3) - (4)].type)); } break; - case 307: -#line 1909 "go.y" + case 309: + +/* Line 1806 of yacc.c */ +#line 1933 "go.y" { if((yyvsp[(2) - (4)].node) == N) { dclcontext = PEXTERN; // since we skip the funcbody below @@ -4551,31 +5120,39 @@ yyreduce: } break; - case 308: -#line 1929 "go.y" + case 310: + +/* Line 1806 of yacc.c */ +#line 1953 "go.y" { (yyval.sym) = (yyvsp[(1) - (1)].sym); structpkg = (yyval.sym)->pkg; } break; - case 309: -#line 1936 "go.y" + case 311: + +/* Line 1806 of yacc.c */ +#line 1960 "go.y" { (yyval.type) = pkgtype((yyvsp[(1) - (1)].sym)); importsym((yyvsp[(1) - (1)].sym), OTYPE); } break; - case 315: -#line 1956 "go.y" + case 317: + +/* Line 1806 of yacc.c */ +#line 1980 "go.y" { (yyval.type) = pkgtype((yyvsp[(1) - (1)].sym)); } break; - case 316: -#line 1960 "go.y" + case 318: + +/* Line 1806 of yacc.c */ +#line 1984 "go.y" { // predefined name like uint8 (yyvsp[(1) - (1)].sym) = pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg); @@ -4587,50 +5164,64 @@ yyreduce: } break; - case 317: -#line 1970 "go.y" + case 319: + +/* Line 1806 of yacc.c */ +#line 1994 "go.y" { (yyval.type) = aindex(N, (yyvsp[(3) - (3)].type)); } break; - case 318: -#line 1974 "go.y" + case 320: + +/* Line 1806 of yacc.c */ +#line 1998 "go.y" { (yyval.type) = aindex(nodlit((yyvsp[(2) - (4)].val)), (yyvsp[(4) - (4)].type)); } break; - case 319: -#line 1978 "go.y" + case 321: + +/* Line 1806 of yacc.c */ +#line 2002 "go.y" { (yyval.type) = maptype((yyvsp[(3) - (5)].type), (yyvsp[(5) - (5)].type)); } break; - case 320: -#line 1982 "go.y" + case 322: + +/* Line 1806 of yacc.c */ +#line 2006 "go.y" { (yyval.type) = tostruct((yyvsp[(3) - (4)].list)); } break; - case 321: -#line 1986 "go.y" + case 323: + +/* Line 1806 of yacc.c */ +#line 2010 "go.y" { (yyval.type) = tointerface((yyvsp[(3) - (4)].list)); } break; - case 322: -#line 1990 "go.y" + case 324: + +/* Line 1806 of yacc.c */ +#line 2014 "go.y" { (yyval.type) = ptrto((yyvsp[(2) - (2)].type)); } break; - case 323: -#line 1994 "go.y" + case 325: + +/* Line 1806 of yacc.c */ +#line 2018 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(2) - (2)].type); @@ -4638,8 +5229,10 @@ yyreduce: } break; - case 324: -#line 2000 "go.y" + case 326: + +/* Line 1806 of yacc.c */ +#line 2024 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(3) - (4)].type); @@ -4647,8 +5240,10 @@ yyreduce: } break; - case 325: -#line 2006 "go.y" + case 327: + +/* Line 1806 of yacc.c */ +#line 2030 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(3) - (3)].type); @@ -4656,8 +5251,10 @@ yyreduce: } break; - case 326: -#line 2014 "go.y" + case 328: + +/* Line 1806 of yacc.c */ +#line 2038 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(3) - (3)].type); @@ -4665,15 +5262,19 @@ yyreduce: } break; - case 327: -#line 2022 "go.y" + case 329: + +/* Line 1806 of yacc.c */ +#line 2046 "go.y" { (yyval.type) = functype(nil, (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list)); } break; - case 328: -#line 2028 "go.y" + case 330: + +/* Line 1806 of yacc.c */ +#line 2052 "go.y" { (yyval.node) = nod(ODCLFIELD, N, typenod((yyvsp[(2) - (3)].type))); if((yyvsp[(1) - (3)].sym)) @@ -4682,8 +5283,10 @@ yyreduce: } break; - case 329: -#line 2035 "go.y" + case 331: + +/* Line 1806 of yacc.c */ +#line 2059 "go.y" { Type *t; @@ -4699,69 +5302,89 @@ yyreduce: } break; - case 330: -#line 2051 "go.y" + case 332: + +/* Line 1806 of yacc.c */ +#line 2075 "go.y" { Sym *s; + Pkg *p; - if((yyvsp[(1) - (3)].sym) != S) { + if((yyvsp[(1) - (3)].sym) != S && strcmp((yyvsp[(1) - (3)].sym)->name, "?") != 0) { (yyval.node) = nod(ODCLFIELD, newname((yyvsp[(1) - (3)].sym)), typenod((yyvsp[(2) - (3)].type))); (yyval.node)->val = (yyvsp[(3) - (3)].val); } else { s = (yyvsp[(2) - (3)].type)->sym; if(s == S && isptr[(yyvsp[(2) - (3)].type)->etype]) s = (yyvsp[(2) - (3)].type)->type->sym; - (yyval.node) = embedded(s); + p = importpkg; + if((yyvsp[(1) - (3)].sym) != S) + p = (yyvsp[(1) - (3)].sym)->pkg; + (yyval.node) = embedded(s, p); (yyval.node)->right = typenod((yyvsp[(2) - (3)].type)); (yyval.node)->val = (yyvsp[(3) - (3)].val); } } break; - case 331: -#line 2069 "go.y" + case 333: + +/* Line 1806 of yacc.c */ +#line 2097 "go.y" { (yyval.node) = nod(ODCLFIELD, newname((yyvsp[(1) - (5)].sym)), typenod(functype(fakethis(), (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list)))); } break; - case 332: -#line 2073 "go.y" + case 334: + +/* Line 1806 of yacc.c */ +#line 2101 "go.y" { (yyval.node) = nod(ODCLFIELD, N, typenod((yyvsp[(1) - (1)].type))); } break; - case 333: -#line 2078 "go.y" + case 335: + +/* Line 1806 of yacc.c */ +#line 2106 "go.y" { (yyval.list) = nil; } break; - case 335: -#line 2085 "go.y" + case 337: + +/* Line 1806 of yacc.c */ +#line 2113 "go.y" { (yyval.list) = (yyvsp[(2) - (3)].list); } break; - case 336: -#line 2089 "go.y" + case 338: + +/* Line 1806 of yacc.c */ +#line 2117 "go.y" { (yyval.list) = list1(nod(ODCLFIELD, N, typenod((yyvsp[(1) - (1)].type)))); } break; - case 337: -#line 2099 "go.y" + case 339: + +/* Line 1806 of yacc.c */ +#line 2127 "go.y" { (yyval.node) = nodlit((yyvsp[(1) - (1)].val)); } break; - case 338: -#line 2103 "go.y" + case 340: + +/* Line 1806 of yacc.c */ +#line 2131 "go.y" { (yyval.node) = nodlit((yyvsp[(2) - (2)].val)); switch((yyval.node)->val.ctype){ @@ -4778,8 +5401,10 @@ yyreduce: } break; - case 339: -#line 2118 "go.y" + case 341: + +/* Line 1806 of yacc.c */ +#line 2146 "go.y" { (yyval.node) = oldname(pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg)); if((yyval.node)->op != OLITERAL) @@ -4787,8 +5412,10 @@ yyreduce: } break; - case 341: -#line 2127 "go.y" + case 343: + +/* Line 1806 of yacc.c */ +#line 2155 "go.y" { if((yyvsp[(2) - (5)].node)->val.ctype == CTRUNE && (yyvsp[(4) - (5)].node)->val.ctype == CTINT) { (yyval.node) = (yyvsp[(2) - (5)].node); @@ -4801,53 +5428,77 @@ yyreduce: } break; - case 344: -#line 2143 "go.y" + case 346: + +/* Line 1806 of yacc.c */ +#line 2171 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 345: -#line 2147 "go.y" + case 347: + +/* Line 1806 of yacc.c */ +#line 2175 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 346: -#line 2153 "go.y" + case 348: + +/* Line 1806 of yacc.c */ +#line 2181 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 347: -#line 2157 "go.y" + case 349: + +/* Line 1806 of yacc.c */ +#line 2185 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 348: -#line 2163 "go.y" + case 350: + +/* Line 1806 of yacc.c */ +#line 2191 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 349: -#line 2167 "go.y" + case 351: + +/* Line 1806 of yacc.c */ +#line 2195 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; -/* Line 1267 of yacc.c. */ -#line 4850 "y.tab.c" + +/* Line 1806 of yacc.c */ +#line 5490 "y.tab.c" default: break; } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); @@ -4856,7 +5507,6 @@ yyreduce: *++yyvsp = yyval; - /* Now `shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ @@ -4876,6 +5526,10 @@ yyreduce: | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { @@ -4883,37 +5537,36 @@ yyerrlab: #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) { - YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); - if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) - { - YYSIZE_T yyalloc = 2 * yysize; - if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) - yyalloc = YYSTACK_ALLOC_MAXIMUM; - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); - yymsg = (char *) YYSTACK_ALLOC (yyalloc); - if (yymsg) - yymsg_alloc = yyalloc; - else - { - yymsg = yymsgbuf; - yymsg_alloc = sizeof yymsgbuf; - } - } - - if (0 < yysize && yysize <= yymsg_alloc) - { - (void) yysyntax_error (yymsg, yystate, yychar); - yyerror (yymsg); - } - else - { - yyerror (YY_("syntax error")); - if (yysize != 0) - goto yyexhaustedlab; - } + char *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; } +# undef YYSYNTAX_ERROR #endif } @@ -4921,7 +5574,7 @@ yyerrlab: if (yyerrstatus == 3) { - /* If just tried and failed to reuse look-ahead token after an + /* If just tried and failed to reuse lookahead token after an error, discard it. */ if (yychar <= YYEOF) @@ -4938,7 +5591,7 @@ yyerrlab: } } - /* Else will try to reuse look-ahead token after shifting the error + /* Else will try to reuse lookahead token after shifting the error token. */ goto yyerrlab1; @@ -4972,7 +5625,7 @@ yyerrlab1: for (;;) { yyn = yypact[yystate]; - if (yyn != YYPACT_NINF) + if (!yypact_value_is_default (yyn)) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) @@ -4995,9 +5648,6 @@ yyerrlab1: YY_STACK_PRINT (yyss, yyssp); } - if (yyn == YYFINAL) - YYACCEPT; - *++yyvsp = yylval; @@ -5022,7 +5672,7 @@ yyabortlab: yyresult = 1; goto yyreturn; -#ifndef yyoverflow +#if !defined(yyoverflow) || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ @@ -5033,9 +5683,14 @@ yyexhaustedlab: #endif yyreturn: - if (yychar != YYEOF && yychar != YYEMPTY) - yydestruct ("Cleanup: discarding lookahead", - yytoken, &yylval); + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + } /* Do not reclaim the symbols of the rule which action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); @@ -5059,7 +5714,9 @@ yyreturn: } -#line 2171 "go.y" + +/* Line 2067 of yacc.c */ +#line 2199 "go.y" static void diff --git a/src/cmd/gc/y.tab.h b/src/cmd/gc/y.tab.h index d01fbe198..6eeb831b2 100644 --- a/src/cmd/gc/y.tab.h +++ b/src/cmd/gc/y.tab.h @@ -1,24 +1,21 @@ -/* A Bison parser, made by GNU Bison 2.3. */ +/* A Bison parser, made by GNU Bison 2.5. */ -/* Skeleton interface for Bison's Yacc-like parsers in C - - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 - Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ + along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work @@ -29,10 +26,11 @@ special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. - + This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ + /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE @@ -146,22 +144,28 @@ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE -#line 28 "go.y" { + +/* Line 2068 of yacc.c */ +#line 28 "go.y" + Node* node; NodeList* list; Type* type; Sym* sym; struct Val val; int i; -} -/* Line 1529 of yacc.c. */ -#line 160 "y.tab.h" - YYSTYPE; + + + +/* Line 2068 of yacc.c */ +#line 163 "y.tab.h" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 -# define YYSTYPE_IS_TRIVIAL 1 #endif extern YYSTYPE yylval; + diff --git a/src/cmd/gc/yerr.h b/src/cmd/gc/yerr.h index e7eb6516c..1526d8231 100644 --- a/src/cmd/gc/yerr.h +++ b/src/cmd/gc/yerr.h @@ -14,25 +14,28 @@ static struct { // is converted by bisonerrors into the yystate and yychar caused // by that token list. - 221, ',', + 222, ',', "unexpected comma during import block", - 377, ';', - "unexpected semicolon or newline before {", + 32, ';', + "missing import path; require quoted string", - 398, ';', - "unexpected semicolon or newline before {", + 378, ';', + "missing { after if clause", - 237, ';', - "unexpected semicolon or newline before {", + 399, ';', + "missing { after switch clause", - 475, LBODY, - "unexpected semicolon or newline before {", + 238, ';', + "missing { after for clause", + + 476, LBODY, + "missing { after for clause", 22, '{', "unexpected semicolon or newline before {", - 144, ';', + 145, ';', "unexpected semicolon or newline in type declaration", 37, '}', @@ -44,33 +47,33 @@ static struct { 37, ',', "unexpected comma in channel type", - 438, LELSE, + 439, LELSE, "unexpected semicolon or newline before else", - 257, ',', + 258, ',', "name list not allowed in interface type", - 237, LVAR, + 238, LVAR, "var declaration not allowed in for initializer", 65, '{', "unexpected { at end of statement", - 376, '{', + 377, '{', "unexpected { at end of statement", - 125, ';', + 126, ';', "argument to go/defer must be function call", - 425, ';', + 426, ';', "need trailing comma before newline in composite literal", - 436, ';', + 437, ';', "need trailing comma before newline in composite literal", - 112, LNAME, + 113, LNAME, "nested func not allowed", - 642, ';', + 645, ';', "else must be followed by if or statement block" }; diff --git a/src/cmd/go/bootstrap.go b/src/cmd/go/bootstrap.go index 32941404c..dc7ed5f4c 100644 --- a/src/cmd/go/bootstrap.go +++ b/src/cmd/go/bootstrap.go @@ -25,6 +25,6 @@ func httpsOrHTTP(importPath string) (string, io.ReadCloser, error) { return "", nil, errHTTP } -func parseMetaGoImports(r io.Reader) (imports []metaImport) { +func parseMetaGoImports(r io.Reader) ([]metaImport, error) { panic("unreachable") } diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index 025b258bf..f70f778d9 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -94,7 +94,8 @@ in an element in the list, surround it with either single or double quotes. For more about specifying packages, see 'go help packages'. For more about where packages and binaries are installed, -see 'go help gopath'. +run 'go help gopath'. For more about calling between Go and C/C++, +run 'go help c'. See also: go install, go get, go clean. `, @@ -185,6 +186,18 @@ func isSpaceByte(c byte) bool { return c == ' ' || c == '\t' || c == '\n' || c == '\r' } +// fileExtSplit expects a filename and returns the name +// and ext (without the dot). If the file has no +// extension, ext will be empty. +func fileExtSplit(file string) (name, ext string) { + dotExt := filepath.Ext(file) + name = file[:len(file)-len(dotExt)] + if dotExt != "" { + ext = dotExt[1:] + } + return +} + type stringsFlag []string func (v *stringsFlag) Set(s string) error { @@ -299,7 +312,13 @@ func runInstall(cmd *Command, args []string) { for _, p := range pkgs { if p.Target == "" && (!p.Standard || p.ImportPath != "unsafe") { - errorf("go install: no install location for directory %s outside GOPATH", p.Dir) + if p.cmdline { + errorf("go install: no install location for .go files listed on command line (GOBIN not set)") + } else if p.ConflictDir != "" { + errorf("go install: no install location for %s: hidden by %s", p.Dir, p.ConflictDir) + } else { + errorf("go install: no install location for directory %s outside GOPATH", p.Dir) + } } } exitIfErrors() @@ -416,7 +435,7 @@ func (b *builder) init() { fatalf("%s", err) } if buildX || buildWork { - fmt.Printf("WORK=%s\n", b.work) + fmt.Fprintf(os.Stderr, "WORK=%s\n", b.work) } if !buildWork { atexit(func() { os.RemoveAll(b.work) }) @@ -470,6 +489,7 @@ func goFilesPackage(gofiles []string) *Package { bp, err := ctxt.ImportDir(dir, 0) pkg := new(Package) pkg.local = true + pkg.cmdline = true pkg.load(&stk, bp, err) pkg.localPrefix = dirToImportPath(dir) pkg.ImportPath = "command-line-arguments" @@ -558,8 +578,12 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action // Imported via local path. No permanent target. mode = modeBuild } - a.objdir = filepath.Join(b.work, a.p.ImportPath, "_obj") + string(filepath.Separator) - a.objpkg = buildToolchain.pkgpath(b.work, a.p) + work := p.pkgdir + if work == "" { + work = b.work + } + a.objdir = filepath.Join(work, a.p.ImportPath, "_obj") + string(filepath.Separator) + a.objpkg = buildToolchain.pkgpath(work, a.p) a.link = p.Name == "main" switch mode { @@ -727,6 +751,15 @@ func hasString(strings []string, s string) bool { // build is the action for building a single package or command. func (b *builder) build(a *action) (err error) { + // Return an error if the package has CXX files but it's not using + // cgo nor SWIG, since the CXX files can only be processed by cgo + // and SWIG (it's possible to have packages with C files without + // using cgo, they will get compiled with the plan9 C compiler and + // linked with the rest of the package). + if len(a.p.CXXFiles) > 0 && !a.p.usesCgo() && !a.p.usesSwig() { + return fmt.Errorf("can't build package %s because it contains C++ files (%s) but it's not using cgo nor SWIG", + a.p.ImportPath, strings.Join(a.p.CXXFiles, ",")) + } defer func() { if err != nil && err != errPrintedOutput { err = fmt.Errorf("go build %s: %v", a.p.ImportPath, err) @@ -747,7 +780,7 @@ func (b *builder) build(a *action) (err error) { if a.p.Standard && a.p.ImportPath == "runtime" && buildContext.Compiler == "gc" && !hasString(a.p.HFiles, "zasm_"+buildContext.GOOS+"_"+buildContext.GOARCH+".h") { - return fmt.Errorf("%s/%s must be bootstrapped using make.bash", buildContext.GOOS, buildContext.GOARCH) + return fmt.Errorf("%s/%s must be bootstrapped using make%v", buildContext.GOOS, buildContext.GOARCH, defaultSuffix()) } // Make build directory. @@ -765,13 +798,32 @@ func (b *builder) build(a *action) (err error) { } var gofiles, cfiles, sfiles, objects, cgoObjects []string - gofiles = append(gofiles, a.p.GoFiles...) + + // If we're doing coverage, preprocess the .go files and put them in the work directory + if a.p.coverMode != "" { + for _, file := range a.p.GoFiles { + sourceFile := filepath.Join(a.p.Dir, file) + cover := a.p.coverVars[file] + if cover == nil || isTestFile(file) { + // Not covering this file. + gofiles = append(gofiles, file) + continue + } + coverFile := filepath.Join(obj, file) + if err := b.cover(a, coverFile, sourceFile, 0666, cover.Var); err != nil { + return err + } + gofiles = append(gofiles, coverFile) + } + } else { + gofiles = append(gofiles, a.p.GoFiles...) + } cfiles = append(cfiles, a.p.CFiles...) sfiles = append(sfiles, a.p.SFiles...) // Run cgo. - if len(a.p.CgoFiles) > 0 { - // In a package using cgo, cgo compiles the C and assembly files with gcc. + if a.p.usesCgo() { + // In a package using cgo, cgo compiles the C, C++ and assembly files with gcc. // There is one exception: runtime/cgo's job is to bridge the // cgo and non-cgo worlds, so it necessarily has files in both. // In that case gcc only gets the gcc_* files. @@ -799,7 +851,7 @@ func (b *builder) build(a *action) (err error) { if a.cgo != nil && a.cgo.target != "" { cgoExe = a.cgo.target } - outGo, outObj, err := b.cgo(a.p, cgoExe, obj, gccfiles) + outGo, outObj, err := b.cgo(a.p, cgoExe, obj, gccfiles, a.p.CXXFiles) if err != nil { return err } @@ -814,7 +866,7 @@ func (b *builder) build(a *action) (err error) { gccfiles := append(cfiles, sfiles...) cfiles = nil sfiles = nil - outGo, outObj, err := b.swig(a.p, obj, gccfiles) + outGo, outObj, err := b.swig(a.p, obj, gccfiles, a.p.CXXFiles) if err != nil { return err } @@ -843,23 +895,24 @@ func (b *builder) build(a *action) (err error) { // Copy .h files named for goos or goarch or goos_goarch // to names using GOOS and GOARCH. // For example, defs_linux_amd64.h becomes defs_GOOS_GOARCH.h. - _goos_goarch := "_" + goos + "_" + goarch + ".h" - _goos := "_" + goos + ".h" - _goarch := "_" + goarch + ".h" + _goos_goarch := "_" + goos + "_" + goarch + _goos := "_" + goos + _goarch := "_" + goarch for _, file := range a.p.HFiles { + name, ext := fileExtSplit(file) switch { - case strings.HasSuffix(file, _goos_goarch): - targ := file[:len(file)-len(_goos_goarch)] + "_GOOS_GOARCH.h" + case strings.HasSuffix(name, _goos_goarch): + targ := file[:len(name)-len(_goos_goarch)] + "_GOOS_GOARCH." + ext if err := b.copyFile(a, obj+targ, filepath.Join(a.p.Dir, file), 0666); err != nil { return err } - case strings.HasSuffix(file, _goarch): - targ := file[:len(file)-len(_goarch)] + "_GOARCH.h" + case strings.HasSuffix(name, _goarch): + targ := file[:len(name)-len(_goarch)] + "_GOARCH." + ext if err := b.copyFile(a, obj+targ, filepath.Join(a.p.Dir, file), 0666); err != nil { return err } - case strings.HasSuffix(file, _goos): - targ := file[:len(file)-len(_goos)] + "_GOOS.h" + case strings.HasSuffix(name, _goos): + targ := file[:len(name)-len(_goos)] + "_GOOS." + ext if err := b.copyFile(a, obj+targ, filepath.Join(a.p.Dir, file), 0666); err != nil { return err } @@ -955,8 +1008,9 @@ func (b *builder) install(a *action) (err error) { return err } soname := a.p.swigSoname(f) + source := filepath.Join(a.objdir, soname) target := filepath.Join(dir, soname) - if err = b.copyFile(a, target, soname, perm); err != nil { + if err = b.copyFile(a, target, source, perm); err != nil { return err } } @@ -997,8 +1051,8 @@ func (b *builder) includeArgs(flag string, all []*action) []string { dir = filepath.Join(dir, "gccgo_"+goos+"_"+goarch) } else { dir = filepath.Join(dir, goos+"_"+goarch) - if buildRace { - dir += "_race" + if buildContext.InstallSuffix != "" { + dir += "_" + buildContext.InstallSuffix } } inc = append(inc, flag, dir) @@ -1047,7 +1101,7 @@ func (b *builder) copyFile(a *action, dst, src string, perm os.FileMode) error { if err != nil && toolIsWindows { // Windows does not allow deletion of a binary file // while it is executing. Try to move it out of the way. - // If the remove fails, which is likely, we'll try again the + // If the move fails, which is likely, we'll try again the // next time we do an install of this binary. if err := os.Rename(dst, dst+"~"); err == nil { os.Remove(dst + "~") @@ -1067,6 +1121,17 @@ func (b *builder) copyFile(a *action, dst, src string, perm os.FileMode) error { return nil } +// cover runs, in effect, +// go tool cover -mode=b.coverMode -var="varName" -o dst.go src.go +func (b *builder) cover(a *action, dst, src string, perm os.FileMode, varName string) error { + return b.run(a.objdir, "cover "+a.p.ImportPath, nil, + tool("cover"), + "-mode", a.p.coverMode, + "-var", varName, + "-o", dst, + src) +} + var objectMagic = [][]byte{ {'!', '<', 'a', 'r', 'c', 'h', '>', '\n'}, // Package archive {'\x7F', 'E', 'L', 'F'}, // ELF @@ -1454,7 +1519,7 @@ func (gcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, g // so that it can give good error messages about forward declarations. // Exceptions: a few standard packages have forward declarations for // pieces supplied behind-the-scenes by package runtime. - extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles) + extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles) if p.Standard { switch p.ImportPath { case "os", "runtime/pprof", "sync", "time": @@ -1464,6 +1529,9 @@ func (gcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, g if extFiles == 0 { gcargs = append(gcargs, "-complete") } + if buildContext.InstallSuffix != "" { + gcargs = append(gcargs, "-installsuffix", buildContext.InstallSuffix) + } args := stringList(tool(archChar+"g"), "-o", ofile, buildGcflags, gcargs, "-D", p.localPrefix, importArgs) for _, f := range gofiles { @@ -1496,6 +1564,7 @@ func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action, importArgs := b.includeArgs("-L", allactions) swigDirs := make(map[string]bool) swigArg := []string{} + cxx := false for _, a := range allactions { if a.p != nil && a.p.usesSwig() { sd := a.p.swigDir(&buildContext) @@ -1506,9 +1575,59 @@ func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action, swigArg[1] += sd } swigDirs[sd] = true + if a.objdir != "" && !swigDirs[a.objdir] { + swigArg[1] += ":" + swigArg[1] += a.objdir + swigDirs[a.objdir] = true + } + } + if a.p != nil && len(a.p.CXXFiles) > 0 { + cxx = true + } + } + ldflags := buildLdflags + if buildContext.InstallSuffix != "" { + ldflags = append(ldflags, "-installsuffix", buildContext.InstallSuffix) + } + if cxx { + // The program includes C++ code. If the user has not + // specified the -extld option, then default to + // linking with the compiler named by the CXX + // environment variable, or g++ if CXX is not set. + extld := false + for _, f := range ldflags { + if f == "-extld" || strings.HasPrefix(f, "-extld=") { + extld = true + break + } + } + if !extld { + compiler := strings.Fields(os.Getenv("CXX")) + if len(compiler) == 0 { + compiler = []string{"g++"} + } + ldflags = append(ldflags, "-extld="+compiler[0]) + if len(compiler) > 1 { + extldflags := false + add := strings.Join(compiler[1:], " ") + for i, f := range ldflags { + if f == "-extldflags" && i+1 < len(ldflags) { + ldflags[i+1] = add + " " + ldflags[i+1] + extldflags = true + break + } else if strings.HasPrefix(f, "-extldflags=") { + ldflags[i] = "-extldflags=" + add + " " + ldflags[i][len("-extldflags="):] + extldflags = true + break + } + } + if !extldflags { + ldflags = append(ldflags, "-extldflags="+add) + } + } } } - return b.run(".", p.ImportPath, nil, tool(archChar+"l"), "-o", out, importArgs, swigArg, buildLdflags, mainpkg) + return b.run(".", p.ImportPath, nil, tool(archChar+"l"), "-o", out, importArgs, swigArg, ldflags, mainpkg) } func (gcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { @@ -1584,6 +1703,7 @@ func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions [] ldflags := b.gccArchArgs() cgoldflags := []string{} usesCgo := false + cxx := false for _, a := range allactions { if a.p != nil { if !a.p.Standard { @@ -1597,12 +1717,18 @@ func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions [] } if a.p.usesSwig() { sd := a.p.swigDir(&buildContext) + if a.objdir != "" { + sd = a.objdir + } for _, f := range stringList(a.p.SwigFiles, a.p.SwigCXXFiles) { soname := a.p.swigSoname(f) sfiles[a.p] = append(sfiles[a.p], filepath.Join(sd, soname)) } usesCgo = true } + if len(a.p.CXXFiles) > 0 { + cxx = true + } } } for _, afile := range afiles { @@ -1615,6 +1741,9 @@ func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions [] if usesCgo && goos == "linux" { ldflags = append(ldflags, "-Wl,-E") } + if cxx { + ldflags = append(ldflags, "-lstdc++") + } return b.run(".", p.ImportPath, nil, "gccgo", "-o", out, ofiles, "-Wl,-(", ldflags, "-Wl,-)", buildGccgoflags) } @@ -1689,26 +1818,55 @@ func (b *builder) libgcc(p *Package) (string, error) { // gcc runs the gcc C compiler to create an object from a single C file. func (b *builder) gcc(p *Package, out string, flags []string, cfile string) error { - cfile = mkAbs(p.Dir, cfile) - return b.run(p.Dir, p.ImportPath, nil, b.gccCmd(p.Dir), flags, "-o", out, "-c", cfile) + return b.ccompile(p, out, flags, cfile, b.gccCmd(p.Dir)) } -// gccld runs the gcc linker to create an executable from a set of object files +// gxx runs the g++ C++ compiler to create an object from a single C++ file. +func (b *builder) gxx(p *Package, out string, flags []string, cxxfile string) error { + return b.ccompile(p, out, flags, cxxfile, b.gxxCmd(p.Dir)) +} + +// ccompile runs the given C or C++ compiler and creates an object from a single source file. +func (b *builder) ccompile(p *Package, out string, flags []string, file string, compiler []string) error { + file = mkAbs(p.Dir, file) + return b.run(p.Dir, p.ImportPath, nil, compiler, flags, "-o", out, "-c", file) +} + +// gccld runs the gcc linker to create an executable from a set of object files. func (b *builder) gccld(p *Package, out string, flags []string, obj []string) error { - return b.run(p.Dir, p.ImportPath, nil, b.gccCmd(p.Dir), "-o", out, obj, flags) + var cmd []string + if len(p.CXXFiles) > 0 { + cmd = b.gxxCmd(p.Dir) + } else { + cmd = b.gccCmd(p.Dir) + } + return b.run(p.Dir, p.ImportPath, nil, cmd, "-o", out, obj, flags) } // gccCmd returns a gcc command line prefix +// defaultCC is defined in zdefaultcc.go, written by cmd/dist. func (b *builder) gccCmd(objdir string) []string { + return b.ccompilerCmd("CC", defaultCC, objdir) +} + +// gxxCmd returns a g++ command line prefix +// defaultCXX is defined in zdefaultcc.go, written by cmd/dist. +func (b *builder) gxxCmd(objdir string) []string { + return b.ccompilerCmd("CXX", defaultCXX, objdir) +} + +// ccompilerCmd returns a command line prefix for the given environment +// variable and using the default command when the variable is empty +func (b *builder) ccompilerCmd(envvar, defcmd, objdir string) []string { // NOTE: env.go's mkEnv knows that the first three // strings returned are "gcc", "-I", objdir (and cuts them off). - gcc := strings.Fields(os.Getenv("CC")) - if len(gcc) == 0 { - gcc = append(gcc, "gcc") + compiler := strings.Fields(os.Getenv(envvar)) + if len(compiler) == 0 { + compiler = strings.Fields(defcmd) } - a := []string{gcc[0], "-I", objdir, "-g", "-O2"} - a = append(a, gcc[1:]...) + a := []string{compiler[0], "-I", objdir, "-g", "-O2"} + a = append(a, compiler[1:]...) // Definitely want -fPIC but on Windows gcc complains // "-fPIC ignored for target (all code is position independent)" @@ -1727,8 +1885,10 @@ func (b *builder) gccCmd(objdir string) []string { } } - // clang is too smart about command-line arguments if strings.Contains(a[0], "clang") { + // disable ASCII art in clang errors, if possible + a = append(a, "-fno-caret-diagnostics") + // clang is too smart about command-line arguments a = append(a, "-Qunused-arguments") } @@ -1767,12 +1927,14 @@ var ( cgoLibGccFileOnce sync.Once ) -func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, outObj []string, err error) { +func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string, gxxfiles []string) (outGo, outObj []string, err error) { if goos != toolGOOS { return nil, nil, errors.New("cannot use cgo when compiling for a different operating system") } + cgoCPPFLAGS := stringList(envList("CGO_CPPFLAGS"), p.CgoCPPFLAGS) cgoCFLAGS := stringList(envList("CGO_CFLAGS"), p.CgoCFLAGS) + cgoCXXFLAGS := stringList(envList("CGO_CXXFLAGS"), p.CgoCXXFLAGS) cgoLDFLAGS := stringList(envList("CGO_LDFLAGS"), p.CgoLDFLAGS) if pkgs := p.CgoPkgConfig; len(pkgs) > 0 { @@ -1783,7 +1945,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, return nil, nil, errPrintedOutput } if len(out) > 0 { - cgoCFLAGS = append(cgoCFLAGS, strings.Fields(string(out))...) + cgoCPPFLAGS = append(cgoCPPFLAGS, strings.Fields(string(out))...) } out, err = b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--libs", pkgs) if err != nil { @@ -1797,7 +1959,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, } // Allows including _cgo_export.h from .[ch] files in the package. - cgoCFLAGS = append(cgoCFLAGS, "-I", obj) + cgoCPPFLAGS = append(cgoCPPFLAGS, "-I", obj) // cgo // TODO: CGOPKGPATH, CGO_FLAGS? @@ -1839,7 +2001,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, } objExt = "o" } - if err := b.run(p.Dir, p.ImportPath, cgoenv, cgoExe, "-objdir", obj, cgoflags, "--", cgoCFLAGS, p.CgoFiles); err != nil { + if err := b.run(p.Dir, p.ImportPath, cgoenv, cgoExe, "-objdir", obj, cgoflags, "--", cgoCPPFLAGS, cgoCFLAGS, p.CgoFiles); err != nil { return nil, nil, err } outGo = append(outGo, gofiles...) @@ -1855,14 +2017,24 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, var linkobj []string var bareLDFLAGS []string - // filter out -lsomelib, and -framework X if on Darwin + // filter out -lsomelib, -l somelib, *.{so,dll,dylib}, and (on Darwin) -framework X for i := 0; i < len(cgoLDFLAGS); i++ { f := cgoLDFLAGS[i] - if !strings.HasPrefix(f, "-l") { - if goos == "darwin" && f == "-framework" { // skip the -framework X - i += 1 - continue + switch { + // skip "-lc" or "-l somelib" + case strings.HasPrefix(f, "-l"): + if f == "-l" { + i++ } + // skip "-framework X" on Darwin + case goos == "darwin" && f == "-framework": + i++ + // skip "*.{dylib,so,dll}" + case strings.HasSuffix(f, ".dylib"), + strings.HasSuffix(f, ".so"), + strings.HasSuffix(f, ".dll"): + continue + default: bareLDFLAGS = append(bareLDFLAGS, f) } } @@ -1876,16 +2048,18 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, var staticLibs []string if goos == "windows" { - // libmingw32 and libmingwex might also use libgcc, so libgcc must come last - staticLibs = []string{"-lmingwex", "-lmingw32"} + // libmingw32 and libmingwex might also use libgcc, so libgcc must come last, + // and they also have some inter-dependencies, so must use linker groups. + staticLibs = []string{"-Wl,--start-group", "-lmingwex", "-lmingw32", "-Wl,--end-group"} } if cgoLibGccFile != "" { staticLibs = append(staticLibs, cgoLibGccFile) } + cflags := stringList(cgoCPPFLAGS, cgoCFLAGS) for _, cfile := range cfiles { ofile := obj + cfile[:len(cfile)-1] + "o" - if err := b.gcc(p, ofile, cgoCFLAGS, obj+cfile); err != nil { + if err := b.gcc(p, ofile, cflags, obj+cfile); err != nil { return nil, nil, err } linkobj = append(linkobj, ofile) @@ -1893,14 +2067,27 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, outObj = append(outObj, ofile) } } + for _, file := range gccfiles { ofile := obj + cgoRe.ReplaceAllString(file[:len(file)-1], "_") + "o" - if err := b.gcc(p, ofile, cgoCFLAGS, file); err != nil { + if err := b.gcc(p, ofile, cflags, file); err != nil { return nil, nil, err } linkobj = append(linkobj, ofile) outObj = append(outObj, ofile) } + + cxxflags := stringList(cgoCPPFLAGS, cgoCXXFLAGS) + for _, file := range gxxfiles { + // Append .o to the file, just in case the pkg has file.c and file.cpp + ofile := obj + cgoRe.ReplaceAllString(file, "_") + ".o" + if err := b.gxx(p, ofile, cxxflags, file); err != nil { + return nil, nil, err + } + linkobj = append(linkobj, ofile) + outObj = append(outObj, ofile) + } + linkobj = append(linkobj, p.SysoFiles...) dynobj := obj + "_cgo_.o" if goarch == "arm" && goos == "linux" { // we need to use -pie for Linux/ARM to get accurate imported sym @@ -1956,7 +2143,27 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, } // Run SWIG on all SWIG input files. -func (b *builder) swig(p *Package, obj string, gccfiles []string) (outGo, outObj []string, err error) { +// TODO: Don't build a shared library, once SWIG emits the necessary +// pragmas for external linking. +func (b *builder) swig(p *Package, obj string, gccfiles, gxxfiles []string) (outGo, outObj []string, err error) { + + var extraObj []string + for _, file := range gccfiles { + ofile := obj + cgoRe.ReplaceAllString(file[:len(file)-1], "_") + "o" + if err := b.gcc(p, ofile, nil, file); err != nil { + return nil, nil, err + } + extraObj = append(extraObj, ofile) + } + + for _, file := range gxxfiles { + // Append .o to the file, just in case the pkg has file.c and file.cpp + ofile := obj + cgoRe.ReplaceAllString(file, "_") + ".o" + if err := b.gxx(p, ofile, nil, file); err != nil { + return nil, nil, err + } + extraObj = append(extraObj, ofile) + } intgosize, err := b.swigIntSize(obj) if err != nil { @@ -1964,7 +2171,7 @@ func (b *builder) swig(p *Package, obj string, gccfiles []string) (outGo, outObj } for _, f := range p.SwigFiles { - goFile, objFile, err := b.swigOne(p, f, obj, false, intgosize) + goFile, objFile, err := b.swigOne(p, f, obj, false, intgosize, extraObj) if err != nil { return nil, nil, err } @@ -1976,7 +2183,7 @@ func (b *builder) swig(p *Package, obj string, gccfiles []string) (outGo, outObj } } for _, f := range p.SwigCXXFiles { - goFile, objFile, err := b.swigOne(p, f, obj, true, intgosize) + goFile, objFile, err := b.swigOne(p, f, obj, true, intgosize, extraObj) if err != nil { return nil, nil, err } @@ -1999,6 +2206,9 @@ const i int = 1 << 32 // Determine the size of int on the target system for the -intgosize option // of swig >= 2.0.9 func (b *builder) swigIntSize(obj string) (intsize string, err error) { + if buildN { + return "$INTBITS", nil + } src := filepath.Join(b.work, "swig_intsize.go") if err = ioutil.WriteFile(src, []byte(swigIntSizeCode), 0644); err != nil { return @@ -2014,7 +2224,7 @@ func (b *builder) swigIntSize(obj string) (intsize string, err error) { } // Run SWIG on one SWIG input file. -func (b *builder) swigOne(p *Package, file, obj string, cxx bool, intgosize string) (outGo, outObj string, err error) { +func (b *builder) swigOne(p *Package, file, obj string, cxx bool, intgosize string, extraObj []string) (outGo, outObj string, err error) { n := 5 // length of ".swig" if cxx { n = 8 // length of ".swigcxx" @@ -2085,7 +2295,8 @@ func (b *builder) swigOne(p *Package, file, obj string, cxx bool, intgosize stri cxxlib = []string{"-lstdc++"} } ldflags := stringList(osldflags[goos], cxxlib) - b.run(p.Dir, p.ImportPath, nil, b.gccCmd(p.Dir), "-o", soname, gccObj, ldflags) + target := filepath.Join(obj, soname) + b.run(p.Dir, p.ImportPath, nil, b.gccCmd(p.Dir), "-o", target, gccObj, extraObj, ldflags) return obj + goFile, cObj, nil } @@ -2130,3 +2341,16 @@ func raceInit() { buildContext.InstallSuffix += "race" buildContext.BuildTags = append(buildContext.BuildTags, "race") } + +// defaultSuffix returns file extension used for command files in +// current os environment. +func defaultSuffix() string { + switch runtime.GOOS { + case "windows": + return ".bat" + case "plan9": + return ".rc" + default: + return ".bash" + } +} diff --git a/src/cmd/go/clean.go b/src/cmd/go/clean.go index 8345c9af1..16687f72f 100644 --- a/src/cmd/go/clean.go +++ b/src/cmd/go/clean.go @@ -137,22 +137,38 @@ func clean(p *Package) { } _, elem := filepath.Split(p.Dir) - allRemove := []string{ - elem, - elem + ".exe", - elem + ".test", - elem + ".test.exe", + var allRemove []string + + // Remove dir-named executable only if this is package main. + if p.Name == "main" { + allRemove = append(allRemove, + elem, + elem+".exe", + ) } + + // Remove package test executables. + allRemove = append(allRemove, + elem+".test", + elem+".test.exe", + ) + + // Remove a potental executable for each .go file in the directory that + // is not part of the directory's package. for _, dir := range dirs { name := dir.Name() if packageFile[name] { continue } if !dir.IsDir() && strings.HasSuffix(name, ".go") { + // TODO(adg,rsc): check that this .go file is actually + // in "package main", and therefore capable of building + // to an executable file. base := name[:len(name)-len(".go")] allRemove = append(allRemove, base, base+".exe") } } + if cleanN || cleanX { b.showcmd(p.Dir, "rm -f %s", strings.Join(allRemove, " ")) } @@ -221,7 +237,23 @@ func clean(p *Package) { // removeFile tries to remove file f, if error other than file doesn't exist // occurs, it will report the error. func removeFile(f string) { - if err := os.Remove(f); err != nil && !os.IsNotExist(err) { - errorf("go clean: %v", err) + err := os.Remove(f) + if err == nil || os.IsNotExist(err) { + return + } + // Windows does not allow deletion of a binary file while it is executing. + if toolIsWindows { + // Remove lingering ~ file from last attempt. + if _, err2 := os.Stat(f + "~"); err2 == nil { + os.Remove(f + "~") + } + // Try to move it out of the way. If the move fails, + // which is likely, we'll try again the + // next time we do an install of this binary. + if err2 := os.Rename(f, f+"~"); err2 == nil { + os.Remove(f + "~") + return + } } + errorf("go clean: %v", err) } diff --git a/src/cmd/go/discovery.go b/src/cmd/go/discovery.go index 047834050..75228b52a 100644 --- a/src/cmd/go/discovery.go +++ b/src/cmd/go/discovery.go @@ -13,17 +13,35 @@ package main import ( "encoding/xml" + "fmt" "io" "strings" ) +// charsetReader returns a reader for the given charset. Currently +// it only supports UTF-8 and ASCII. Otherwise, it returns a meaningful +// error which is printed by go get, so the user can find why the package +// wasn't downloaded if the encoding is not supported. Note that, in +// order to reduce potential errors, ASCII is treated as UTF-8 (i.e. characters +// greater than 0x7f are not rejected). +func charsetReader(charset string, input io.Reader) (io.Reader, error) { + switch strings.ToLower(charset) { + case "ascii": + return input, nil + default: + return nil, fmt.Errorf("can't decode XML document using charset %q", charset) + } +} + // parseMetaGoImports returns meta imports from the HTML in r. // Parsing ends at the end of the <head> section or the beginning of the <body>. -func parseMetaGoImports(r io.Reader) (imports []metaImport) { +func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) { d := xml.NewDecoder(r) + d.CharsetReader = charsetReader d.Strict = false + var t xml.Token for { - t, err := d.Token() + t, err = d.Token() if err != nil { return } diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go index df82ab45b..ebb2f37fd 100644 --- a/src/cmd/go/doc.go +++ b/src/cmd/go/doc.go @@ -16,7 +16,6 @@ The commands are: build compile packages and dependencies clean remove object files - doc run godoc on package sources env print Go environment information fix run go tool fix on packages fmt run gofmt on package sources @@ -33,9 +32,10 @@ Use "go help [command]" for more information about a command. Additional help topics: + c calling between Go and C gopath GOPATH environment variable + importpath import path syntax packages description of package lists - remote remote import path syntax testflag description of testing flags testfunc description of testing functions @@ -112,7 +112,8 @@ in an element in the list, surround it with either single or double quotes. For more about specifying packages, see 'go help packages'. For more about where packages and binaries are installed, -see 'go help gopath'. +run 'go help gopath'. For more about calling between Go and C/C++, +run 'go help c'. See also: go install, go get, go clean. @@ -162,26 +163,6 @@ The -x flag causes clean to print remove commands as it executes them. For more about specifying packages, see 'go help packages'. -Run godoc on package sources - -Usage: - - go doc [-n] [-x] [packages] - -Doc runs the godoc command on the packages named by the -import paths. - -For more about godoc, see 'godoc godoc'. -For more about specifying packages, see 'go help packages'. - -The -n flag prints commands that would be executed. -The -x flag prints commands as they are executed. - -To run godoc with specific options, run godoc itself. - -See also: go fix, go fmt, go vet. - - Print Go environment information Usage: @@ -229,14 +210,14 @@ The -x flag prints commands as they are executed. To run gofmt with specific options, run gofmt itself. -See also: go doc, go fix, go vet. +See also: go fix, go vet. Download and install packages and dependencies Usage: - go get [-d] [-fix] [-u] [build flags] [packages] + go get [-d] [-fix] [-t] [-u] [build flags] [packages] Get downloads and installs the packages named by the import paths, along with their dependencies. @@ -247,6 +228,9 @@ it instructs get not to install the packages. The -fix flag instructs get to run the fix tool on the downloaded packages before resolving dependencies or building the code. +The -t flag instructs get to also download the packages required to build +the tests for the specified packages. + The -u flag instructs get to use the network to update the named packages and their dependencies. By default, get uses the network to check out missing packages but does not use it to look for updates to existing packages. @@ -263,7 +247,7 @@ retrieves the most recent version of the package. For more about specifying packages, see 'go help packages'. For more about how 'go get' finds source code to -download, see 'go help remote'. +download, see 'go help importpath'. See also: go build, go install, go clean. @@ -287,7 +271,7 @@ List packages Usage: - go list [-e] [-f format] [-json] [-tags 'tag list'] [packages] + go list [-e] [-race] [-f format] [-json] [-tags 'tag list'] [packages] List lists the packages named by the import paths, one per line. @@ -318,14 +302,17 @@ which calls strings.Join. The struct being passed to the template is: CgoFiles []string // .go sources files that import "C" IgnoredGoFiles []string // .go sources ignored due to build constraints CFiles []string // .c source files - HFiles []string // .h source files + CXXFiles []string // .cc, .cxx and .cpp source files + HFiles []string // .h, .hh, .hpp and .hxx source files SFiles []string // .s source files - SysoFiles []string // .syso object files to add to archive SwigFiles []string // .swig files SwigCXXFiles []string // .swigcxx files + SysoFiles []string // .syso object files to add to archive // Cgo directives CgoCFLAGS []string // cgo: flags for C compiler + CgoCPPFLAGS []string // cgo: flags for C preprocessor + CgoCXXFLAGS []string // cgo: flags for C++ compiler CgoLDFLAGS []string // cgo: flags for linker CgoPkgConfig []string // cgo: pkg-config names @@ -360,6 +347,9 @@ a non-nil Error field; other information may or may not be missing The -tags flag specifies a list of build tags, like in the 'go build' command. +The -race flag causes the package data to include the dependencies +required by the race detector. + For more about specifying packages, see 'go help packages'. @@ -370,7 +360,7 @@ Usage: go run [build flags] gofiles... [arguments...] Run compiles and runs the main package comprising the named Go source files. -If no files are named, it compiles and runs all non-test Go source files. +A Go source file is defined to be a file ending in a literal ".go" suffix. For more about build flags, see 'go help build'. @@ -381,7 +371,7 @@ Test packages Usage: - go test [-c] [-i] [build flags] [packages] [flags for test binary] + go test [-c] [-i] [build and test flags] [packages] [flags for test binary] 'Go test' automates testing the packages named by the import paths. It prints a summary of the test results in the format: @@ -394,8 +384,10 @@ It prints a summary of the test results in the format: followed by detailed output for each failed package. 'Go test' recompiles each package along with any files with names matching -the file pattern "*_test.go". These additional files can contain test functions, -benchmark functions, and example functions. See 'go help testfunc' for more. +the file pattern "*_test.go". +Files whose names begin with "_" (including "_test.go") or "." are ignored. +These additional files can contain test functions, benchmark functions, and +example functions. See 'go help testfunc' for more. Each listed package causes the execution of a separate test binary. Test files that declare a package with the suffix "_test" will be compiled as a @@ -419,6 +411,11 @@ In addition to the build flags, the flags handled by 'go test' itself are: The test binary also accepts flags that control execution of the test; these flags are also accessible by 'go test'. See 'go help testflag' for details. +If the test binary needs any other flags, they should be presented after the +package names. The go tool treats as a flag the first argument that begins with +a minus sign that it does not recognize itself; that argument and all subsequent +arguments are passed as arguments to the test binary. + For more about build flags, see 'go help build'. For more about specifying packages, see 'go help packages'. @@ -457,7 +454,7 @@ Usage: Vet runs the Go vet command on the packages named by the import paths. -For more about vet, see 'godoc vet'. +For more about vet, see 'godoc code.google.com/p/go.tools/cmd/vet'. For more about specifying packages, see 'go help packages'. To run the vet tool with specific options, run 'go tool vet'. @@ -468,6 +465,25 @@ The -x flag prints commands as they are executed. See also: go fmt, go fix. +Calling between Go and C + +There are two different ways to call between Go and C/C++ code. + +The first is the cgo tool, which is part of the Go distribution. For +information on how to use it see the cgo documentation (godoc cmd/cgo). + +The second is the SWIG program, which is a general tool for +interfacing between languages. For information on SWIG see +http://swig.org/. When running go build, any file with a .swig +extension will be passed to SWIG. Any file with a .swigcxx extension +will be passed to SWIG with the -c++ option. + +When either cgo or SWIG is used, go build will pass any .c, .s, or .S +files to the C compiler, and any .cc, .cpp, .cxx files to the C++ +compiler. The CC or CXX environment variables may be set to determine +the C or C++ compiler, respectively, to use. + + GOPATH environment variable The Go path is used to resolve import statements. @@ -528,59 +544,40 @@ but new packages are always downloaded into the first directory in the list. -Description of package lists - -Many commands apply to a set of packages: - - go action [packages] - -Usually, [packages] is a list of import paths. - -An import path that is a rooted path or that begins with -a . or .. element is interpreted as a file system path and -denotes the package in that directory. - -Otherwise, the import path P denotes the package found in -the directory DIR/src/P for some DIR listed in the GOPATH -environment variable (see 'go help gopath'). - -If no import paths are given, the action applies to the -package in the current directory. - -The special import path "all" expands to all package directories -found in all the GOPATH trees. For example, 'go list all' -lists all the packages on the local system. - -The special import path "std" is like all but expands to just the -packages in the standard Go library. - -An import path is a pattern if it includes one or more "..." wildcards, -each of which can match any string, including the empty string and -strings containing slashes. Such a pattern expands to all package -directories found in the GOPATH trees with names matching the -patterns. As a special case, x/... matches x as well as x's subdirectories. -For example, net/... expands to net and packages in its subdirectories. - -An import path can also name a package to be downloaded from -a remote repository. Run 'go help remote' for details. - -Every package in a program must have a unique import path. -By convention, this is arranged by starting each path with a -unique prefix that belongs to you. For example, paths used -internally at Google all begin with 'google', and paths -denoting remote repositories begin with the path to the code, -such as 'code.google.com/p/project'. - -As a special case, if the package list is a list of .go files from a -single directory, the command is applied to a single synthesized -package made up of exactly those files, ignoring any build constraints -in those files and ignoring any other files in the directory. - - -Remote import path syntax +Import path syntax An import path (see 'go help packages') denotes a package -stored in the local file system. Certain import paths also +stored in the local file system. In general, an import path denotes +either a standard package (such as "unicode/utf8") or a package +found in one of the work spaces (see 'go help gopath'). + +Relative import paths + +An import path beginning with ./ or ../ is called a relative path. +The toolchain supports relative import paths as a shortcut in two ways. + +First, a relative path can be used as a shorthand on the command line. +If you are working in the directory containing the code imported as +"unicode" and want to run the tests for "unicode/utf8", you can type +"go test ./utf8" instead of needing to specify the full path. +Similarly, in the reverse situation, "go test .." will test "unicode" from +the "unicode/utf8" directory. Relative patterns are also allowed, like +"go test ./..." to test all subdirectories. See 'go help packages' for details +on the pattern syntax. + +Second, if you are compiling a Go program not in a work space, +you can use a relative path in an import statement in that program +to refer to nearby code also not in a work space. +This makes it easy to experiment with small multipackage programs +outside of the usual work spaces, but such programs cannot be +installed with "go install" (there is no work space in which to install them), +so they are rebuilt from scratch each time they are built. +To avoid ambiguity, Go programs cannot use relative import paths +within a work space. + +Remote import paths + +Certain import paths also describe how to obtain the source code for the package using a revision control system. @@ -691,6 +688,62 @@ package appropriate for the Go release being used. Run 'go help install' for more. +Description of package lists + +Many commands apply to a set of packages: + + go action [packages] + +Usually, [packages] is a list of import paths. + +An import path that is a rooted path or that begins with +a . or .. element is interpreted as a file system path and +denotes the package in that directory. + +Otherwise, the import path P denotes the package found in +the directory DIR/src/P for some DIR listed in the GOPATH +environment variable (see 'go help gopath'). + +If no import paths are given, the action applies to the +package in the current directory. + +There are three reserved names for paths that should not be used +for packages to be built with the go tool: + +- "main" denotes the top-level package in a stand-alone executable. + +- "all" expands to all package directories found in all the GOPATH +trees. For example, 'go list all' lists all the packages on the local +system. + +- "std" is like all but expands to just the packages in the standard +Go library. + +An import path is a pattern if it includes one or more "..." wildcards, +each of which can match any string, including the empty string and +strings containing slashes. Such a pattern expands to all package +directories found in the GOPATH trees with names matching the +patterns. As a special case, x/... matches x as well as x's subdirectories. +For example, net/... expands to net and packages in its subdirectories. + +An import path can also name a package to be downloaded from +a remote repository. Run 'go help importpath' for details. + +Every package in a program must have a unique import path. +By convention, this is arranged by starting each path with a +unique prefix that belongs to you. For example, paths used +internally at Google all begin with 'google', and paths +denoting remote repositories begin with the path to the code, +such as 'code.google.com/p/project'. + +As a special case, if the package list is a list of .go files from a +single directory, the command is applied to a single synthesized +package made up of exactly those files, ignoring any build constraints +in those files and ignoring any other files in the directory. + +File names that begin with "." or "_" are ignored by the go tool. + + Description of testing flags The 'go test' command takes both flags that apply to 'go test' itself @@ -730,6 +783,30 @@ control the execution of any test: if -test.blockprofile is set without this flag, all blocking events are recorded, equivalent to -test.blockprofilerate=1. + -cover + Enable coverage analysis. + + -covermode set,count,atomic + Set the mode for coverage analysis for the package[s] + being tested. The default is "set". + The values: + set: bool: does this statement run? + count: int: how many times does this statement run? + atomic: int: count, but correct in multithreaded tests; + significantly more expensive. + Sets -cover. + + -coverpkg pkg1,pkg2,pkg3 + Apply coverage analysis in each test to the given list of packages. + The default is for each test to analyze only the package being tested. + Packages are specified as import paths. + Sets -cover. + + -coverprofile cover.out + Write a coverage profile to the specified file after all tests + have passed. + Sets -cover. + -cpu 1,2,4 Specify a list of GOMAXPROCS values for which the tests or benchmarks should be executed. The default is the current value @@ -750,6 +827,10 @@ control the execution of any test: garbage collector, provided the test can run in the available memory without garbage collection. + -outputdir directory + Place output files from profiling in the specified directory, + by default the directory in which "go test" is running. + -parallel n Allow parallel execution of test functions that call t.Parallel. The value of this flag is the maximum number of tests to run @@ -787,8 +868,8 @@ will compile the test binary and then run it as pkg.test -test.v -test.cpuprofile=prof.out -dir=testdata -update -The test flags that generate profiles also leave the test binary in pkg.test -for use when analyzing the profiles. +The test flags that generate profiles (other than for coverage) also +leave the test binary in pkg.test for use when analyzing the profiles. Flags not recognized by 'go test' must be placed after any specified packages. @@ -837,5 +918,3 @@ See the documentation of the testing package for more information. */ package main - -// NOTE: cmdDoc is in fmt.go. diff --git a/src/cmd/go/env.go b/src/cmd/go/env.go index 00e03e9bd..2db821797 100644 --- a/src/cmd/go/env.go +++ b/src/cmd/go/env.go @@ -45,12 +45,17 @@ func mkEnv() []envVar { {"GORACE", os.Getenv("GORACE")}, {"GOROOT", goroot}, {"GOTOOLDIR", toolDir}, + + // disable escape codes in clang errors + {"TERM", "dumb"}, } if goos != "plan9" { cmd := b.gccCmd(".") env = append(env, envVar{"CC", cmd[0]}) env = append(env, envVar{"GOGCCFLAGS", strings.Join(cmd[3:], " ")}) + cmd = b.gxxCmd(".") + env = append(env, envVar{"CXX", cmd[0]}) } if buildContext.CgoEnabled { diff --git a/src/cmd/go/fmt.go b/src/cmd/go/fmt.go index 9d3c911dd..65dc3ca59 100644 --- a/src/cmd/go/fmt.go +++ b/src/cmd/go/fmt.go @@ -6,7 +6,6 @@ package main func init() { addBuildFlagsNX(cmdFmt) - addBuildFlagsNX(cmdDoc) } var cmdFmt = &Command{ @@ -25,7 +24,7 @@ The -x flag prints commands as they are executed. To run gofmt with specific options, run gofmt itself. -See also: go doc, go fix, go vet. +See also: go fix, go vet. `, } @@ -37,37 +36,3 @@ func runFmt(cmd *Command, args []string) { run(stringList("gofmt", "-l", "-w", relPaths(pkg.allgofiles))) } } - -var cmdDoc = &Command{ - Run: runDoc, - UsageLine: "doc [-n] [-x] [packages]", - Short: "run godoc on package sources", - Long: ` -Doc runs the godoc command on the packages named by the -import paths. - -For more about godoc, see 'godoc godoc'. -For more about specifying packages, see 'go help packages'. - -The -n flag prints commands that would be executed. -The -x flag prints commands as they are executed. - -To run godoc with specific options, run godoc itself. - -See also: go fix, go fmt, go vet. - `, -} - -func runDoc(cmd *Command, args []string) { - for _, pkg := range packages(args) { - if pkg.ImportPath == "command-line arguments" { - errorf("go doc: cannot use package file list") - continue - } - if pkg.local { - run("godoc", pkg.Dir) - } else { - run("godoc", pkg.ImportPath) - } - } -} diff --git a/src/cmd/go/get.go b/src/cmd/go/get.go index 8c08ab261..e61da7e2a 100644 --- a/src/cmd/go/get.go +++ b/src/cmd/go/get.go @@ -18,7 +18,7 @@ import ( ) var cmdGet = &Command{ - UsageLine: "get [-d] [-fix] [-u] [build flags] [packages]", + UsageLine: "get [-d] [-fix] [-t] [-u] [build flags] [packages]", Short: "download and install packages and dependencies", Long: ` Get downloads and installs the packages named by the import paths, @@ -30,6 +30,9 @@ it instructs get not to install the packages. The -fix flag instructs get to run the fix tool on the downloaded packages before resolving dependencies or building the code. +The -t flag instructs get to also download the packages required to build +the tests for the specified packages. + The -u flag instructs get to use the network to update the named packages and their dependencies. By default, get uses the network to check out missing packages but does not use it to look for updates to existing packages. @@ -46,13 +49,14 @@ retrieves the most recent version of the package. For more about specifying packages, see 'go help packages'. For more about how 'go get' finds source code to -download, see 'go help remote'. +download, see 'go help importpath'. See also: go build, go install, go clean. `, } var getD = cmdGet.Flag.Bool("d", false, "") +var getT = cmdGet.Flag.Bool("t", false, "") var getU = cmdGet.Flag.Bool("u", false, "") var getFix = cmdGet.Flag.Bool("fix", false, "") @@ -65,7 +69,7 @@ func runGet(cmd *Command, args []string) { // Phase 1. Download/update. var stk importStack for _, arg := range downloadPaths(args) { - download(arg, &stk) + download(arg, &stk, *getT) } exitIfErrors() @@ -137,7 +141,7 @@ var downloadRootCache = map[string]bool{} // download runs the download half of the get command // for the package named by the argument. -func download(arg string, stk *importStack) { +func download(arg string, stk *importStack, getTestDeps bool) { p := loadPackage(arg, stk) // There's nothing to do if this is a package in the standard library. @@ -153,6 +157,7 @@ func download(arg string, stk *importStack) { pkgs := []*Package{p} wildcardOkay := len(*stk) == 0 + isWildcard := false // Download if the package is missing, or update if we're using -u. if p.Dir == "" || *getU { @@ -175,6 +180,7 @@ func download(arg string, stk *importStack) { } else { args = matchPackages(arg) } + isWildcard = true } // Clear all relevant package cache entries before @@ -214,9 +220,30 @@ func download(arg string, stk *importStack) { } } + if isWildcard { + // Report both the real package and the + // wildcard in any error message. + stk.push(p.ImportPath) + } + // Process dependencies, now that we know what they are. for _, dep := range p.deps { - download(dep.ImportPath, stk) + // Don't get test dependencies recursively. + download(dep.ImportPath, stk, false) + } + if getTestDeps { + // Process test dependencies when -t is specified. + // (Don't get test dependencies for test dependencies.) + for _, path := range p.TestImports { + download(path, stk, false) + } + for _, path := range p.XTestImports { + download(path, stk, false) + } + } + + if isWildcard { + stk.pop() } } } @@ -286,7 +313,7 @@ func downloadPackage(p *Package) error { } // Some version control tools require the parent of the target to exist. parent, _ := filepath.Split(root) - if err := os.MkdirAll(parent, 0777); err != nil { + if err = os.MkdirAll(parent, 0777); err != nil { return err } if err = vcs.create(root, repo); err != nil { diff --git a/src/cmd/go/help.go b/src/cmd/go/help.go index c70a25fdd..71e55175a 100644 --- a/src/cmd/go/help.go +++ b/src/cmd/go/help.go @@ -4,6 +4,28 @@ package main +var helpC = &Command{ + UsageLine: "c", + Short: "calling between Go and C", + Long: ` +There are two different ways to call between Go and C/C++ code. + +The first is the cgo tool, which is part of the Go distribution. For +information on how to use it see the cgo documentation (godoc cmd/cgo). + +The second is the SWIG program, which is a general tool for +interfacing between languages. For information on SWIG see +http://swig.org/. When running go build, any file with a .swig +extension will be passed to SWIG. Any file with a .swigcxx extension +will be passed to SWIG with the -c++ option. + +When either cgo or SWIG is used, go build will pass any .c, .s, or .S +files to the C compiler, and any .cc, .cpp, .cxx files to the C++ +compiler. The CC or CXX environment variables may be set to determine +the C or C++ compiler, respectively, to use. + `, +} + var helpPackages = &Command{ UsageLine: "packages", Short: "description of package lists", @@ -25,12 +47,17 @@ environment variable (see 'go help gopath'). If no import paths are given, the action applies to the package in the current directory. -The special import path "all" expands to all package directories -found in all the GOPATH trees. For example, 'go list all' -lists all the packages on the local system. +There are three reserved names for paths that should not be used +for packages to be built with the go tool: + +- "main" denotes the top-level package in a stand-alone executable. -The special import path "std" is like all but expands to just the -packages in the standard Go library. +- "all" expands to all package directories found in all the GOPATH +trees. For example, 'go list all' lists all the packages on the local +system. + +- "std" is like all but expands to just the packages in the standard +Go library. An import path is a pattern if it includes one or more "..." wildcards, each of which can match any string, including the empty string and @@ -40,7 +67,7 @@ patterns. As a special case, x/... matches x as well as x's subdirectories. For example, net/... expands to net and packages in its subdirectories. An import path can also name a package to be downloaded from -a remote repository. Run 'go help remote' for details. +a remote repository. Run 'go help importpath' for details. Every package in a program must have a unique import path. By convention, this is arranged by starting each path with a @@ -53,16 +80,48 @@ As a special case, if the package list is a list of .go files from a single directory, the command is applied to a single synthesized package made up of exactly those files, ignoring any build constraints in those files and ignoring any other files in the directory. + +File names that begin with "." or "_" are ignored by the go tool. `, } -var helpRemote = &Command{ - UsageLine: "remote", - Short: "remote import path syntax", +var helpImportPath = &Command{ + UsageLine: "importpath", + Short: "import path syntax", Long: ` An import path (see 'go help packages') denotes a package -stored in the local file system. Certain import paths also +stored in the local file system. In general, an import path denotes +either a standard package (such as "unicode/utf8") or a package +found in one of the work spaces (see 'go help gopath'). + +Relative import paths + +An import path beginning with ./ or ../ is called a relative path. +The toolchain supports relative import paths as a shortcut in two ways. + +First, a relative path can be used as a shorthand on the command line. +If you are working in the directory containing the code imported as +"unicode" and want to run the tests for "unicode/utf8", you can type +"go test ./utf8" instead of needing to specify the full path. +Similarly, in the reverse situation, "go test .." will test "unicode" from +the "unicode/utf8" directory. Relative patterns are also allowed, like +"go test ./..." to test all subdirectories. See 'go help packages' for details +on the pattern syntax. + +Second, if you are compiling a Go program not in a work space, +you can use a relative path in an import statement in that program +to refer to nearby code also not in a work space. +This makes it easy to experiment with small multipackage programs +outside of the usual work spaces, but such programs cannot be +installed with "go install" (there is no work space in which to install them), +so they are rebuilt from scratch each time they are built. +To avoid ambiguity, Go programs cannot use relative import paths +within a work space. + +Remote import paths + +Certain import paths also describe how to obtain the source code for the package using a revision control system. diff --git a/src/cmd/go/list.go b/src/cmd/go/list.go index 2d23d077e..f56ebed38 100644 --- a/src/cmd/go/list.go +++ b/src/cmd/go/list.go @@ -14,7 +14,7 @@ import ( ) var cmdList = &Command{ - UsageLine: "list [-e] [-f format] [-json] [-tags 'tag list'] [packages]", + UsageLine: "list [-e] [-race] [-f format] [-json] [-tags 'tag list'] [packages]", Short: "list packages", Long: ` List lists the packages named by the import paths, one per line. @@ -46,14 +46,17 @@ which calls strings.Join. The struct being passed to the template is: CgoFiles []string // .go sources files that import "C" IgnoredGoFiles []string // .go sources ignored due to build constraints CFiles []string // .c source files - HFiles []string // .h source files + CXXFiles []string // .cc, .cxx and .cpp source files + HFiles []string // .h, .hh, .hpp and .hxx source files SFiles []string // .s source files - SysoFiles []string // .syso object files to add to archive SwigFiles []string // .swig files SwigCXXFiles []string // .swigcxx files + SysoFiles []string // .syso object files to add to archive // Cgo directives CgoCFLAGS []string // cgo: flags for C compiler + CgoCPPFLAGS []string // cgo: flags for C preprocessor + CgoCXXFLAGS []string // cgo: flags for C++ compiler CgoLDFLAGS []string // cgo: flags for linker CgoPkgConfig []string // cgo: pkg-config names @@ -88,6 +91,9 @@ a non-nil Error field; other information may or may not be missing The -tags flag specifies a list of build tags, like in the 'go build' command. +The -race flag causes the package data to include the dependencies +required by the race detector. + For more about specifying packages, see 'go help packages'. `, } @@ -101,12 +107,17 @@ func init() { var listE = cmdList.Flag.Bool("e", false, "") var listFmt = cmdList.Flag.String("f", "{{.ImportPath}}", "") var listJson = cmdList.Flag.Bool("json", false, "") +var listRace = cmdList.Flag.Bool("race", false, "") var nl = []byte{'\n'} func runList(cmd *Command, args []string) { out := newTrackingWriter(os.Stdout) defer out.w.Flush() + if *listRace { + buildRace = true + } + var do func(*Package) if *listJson { do = func(p *Package) { diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go index 3180dbeed..df0cf1b3f 100644 --- a/src/cmd/go/main.go +++ b/src/cmd/go/main.go @@ -76,7 +76,6 @@ func (c *Command) Runnable() bool { var commands = []*Command{ cmdBuild, cmdClean, - cmdDoc, cmdEnv, cmdFix, cmdFmt, @@ -89,9 +88,10 @@ var commands = []*Command{ cmdVersion, cmdVet, + helpC, helpGopath, + helpImportPath, helpPackages, - helpRemote, helpTestflag, helpTestfunc, } @@ -144,6 +144,11 @@ func main() { } } + if fi, err := os.Stat(goroot); err != nil || !fi.IsDir() { + fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: %v\n", goroot) + os.Exit(2) + } + for _, cmd := range commands { if cmd.Name() == args[0] && cmd.Run != nil { cmd.Flag.Usage = func() { cmd.Usage() } @@ -208,8 +213,6 @@ var documentationTemplate = `// Copyright 2011 The Go Authors. All rights reser {{end}}*/ package main - -// NOTE: cmdDoc is in fmt.go. ` // tmpl executes the given template text on data, writing the result to w. @@ -358,7 +361,7 @@ func exitIfErrors() { func run(cmdargs ...interface{}) { cmdline := stringList(cmdargs...) - if buildN || buildV { + if buildN || buildX { fmt.Printf("%s\n", strings.Join(cmdline, " ")) if buildN { return @@ -432,6 +435,37 @@ func matchPattern(pattern string) func(name string) bool { } } +// hasPathPrefix reports whether the path s begins with the +// elements in prefix. +func hasPathPrefix(s, prefix string) bool { + switch { + default: + return false + case len(s) == len(prefix): + return s == prefix + case len(s) > len(prefix): + if prefix != "" && prefix[len(prefix)-1] == '/' { + return strings.HasPrefix(s, prefix) + } + return s[len(prefix)] == '/' && s[:len(prefix)] == prefix + } +} + +// treeCanMatchPattern(pattern)(name) reports whether +// name or children of name can possibly match pattern. +// Pattern is the same limited glob accepted by matchPattern. +func treeCanMatchPattern(pattern string) func(name string) bool { + wildCard := false + if i := strings.Index(pattern, "..."); i >= 0 { + wildCard = true + pattern = pattern[:i] + } + return func(name string) bool { + return len(name) <= len(pattern) && hasPathPrefix(pattern, name) || + wildCard && strings.HasPrefix(name, pattern) + } +} + // allPackages returns all the packages that can be found // under the $GOPATH directories and $GOROOT matching pattern. // The pattern is either "all" (all packages), "std" (standard packages) @@ -446,8 +480,10 @@ func allPackages(pattern string) []string { func matchPackages(pattern string) []string { match := func(string) bool { return true } + treeCanMatch := func(string) bool { return true } if pattern != "all" && pattern != "std" { match = matchPattern(pattern) + treeCanMatch = treeCanMatchPattern(pattern) } have := map[string]bool{ @@ -465,6 +501,9 @@ func matchPackages(pattern string) []string { return nil } name := path[len(cmd):] + if !treeCanMatch(name) { + return filepath.SkipDir + } // Commands are all in cmd/, not in subdirectories. if strings.Contains(name, string(filepath.Separator)) { return filepath.SkipDir @@ -481,6 +520,9 @@ func matchPackages(pattern string) []string { } _, err = buildContext.ImportDir(path, 0) if err != nil { + if _, noGo := err.(*build.NoGoError); !noGo { + log.Print(err) + } return nil } pkgs = append(pkgs, name) @@ -507,6 +549,9 @@ func matchPackages(pattern string) []string { if pattern == "std" && strings.Contains(name, ".") { return filepath.SkipDir } + if !treeCanMatch(name) { + return filepath.SkipDir + } if have[name] { return nil } @@ -515,8 +560,10 @@ func matchPackages(pattern string) []string { return nil } _, err = buildContext.ImportDir(path, 0) - if err != nil && strings.Contains(err.Error(), "no Go source files") { - return nil + if err != nil { + if _, noGo := err.(*build.NoGoError); noGo { + return nil + } } pkgs = append(pkgs, name) return nil @@ -583,6 +630,9 @@ func matchPackagesInFS(pattern string) []string { return nil } if _, err = build.ImportDir(path, 0); err != nil { + if _, noGo := err.(*build.NoGoError); !noGo { + log.Print(err) + } return nil } pkgs = append(pkgs, name) diff --git a/src/cmd/go/match_test.go b/src/cmd/go/match_test.go index f058f235a..38b9b115e 100644 --- a/src/cmd/go/match_test.go +++ b/src/cmd/go/match_test.go @@ -6,11 +6,7 @@ package main import "testing" -var matchTests = []struct { - pattern string - path string - match bool -}{ +var matchPatternTests = []stringPairTest{ {"...", "foo", true}, {"net", "net", true}, {"net", "net/http", false}, @@ -27,10 +23,66 @@ var matchTests = []struct { } func TestMatchPattern(t *testing.T) { - for _, tt := range matchTests { - match := matchPattern(tt.pattern)(tt.path) - if match != tt.match { - t.Errorf("matchPattern(%q)(%q) = %v, want %v", tt.pattern, tt.path, match, tt.match) + testStringPairs(t, "matchPattern", matchPatternTests, func(pattern, name string) bool { + return matchPattern(pattern)(name) + }) +} + +var treeCanMatchPatternTests = []stringPairTest{ + {"...", "foo", true}, + {"net", "net", true}, + {"net", "net/http", false}, + {"net/http", "net", true}, + {"net/http", "net/http", true}, + {"net...", "netchan", true}, + {"net...", "net", true}, + {"net...", "net/http", true}, + {"net...", "not/http", false}, + {"net/...", "netchan", false}, + {"net/...", "net", true}, + {"net/...", "net/http", true}, + {"net/...", "not/http", false}, + {"abc.../def", "abcxyz", true}, + {"abc.../def", "xyxabc", false}, + {"x/y/z/...", "x", true}, + {"x/y/z/...", "x/y", true}, + {"x/y/z/...", "x/y/z", true}, + {"x/y/z/...", "x/y/z/w", true}, + {"x/y/z", "x", true}, + {"x/y/z", "x/y", true}, + {"x/y/z", "x/y/z", true}, + {"x/y/z", "x/y/z/w", false}, + {"x/.../y/z", "x/a/b/c", true}, + {"x/.../y/z", "y/x/a/b/c", false}, +} + +func TestChildrenCanMatchPattern(t *testing.T) { + testStringPairs(t, "treeCanMatchPattern", treeCanMatchPatternTests, func(pattern, name string) bool { + return treeCanMatchPattern(pattern)(name) + }) +} + +var hasPathPrefixTests = []stringPairTest{ + {"abc", "a", false}, + {"a/bc", "a", true}, + {"a", "a", true}, + {"a/bc", "a/", true}, +} + +func TestHasPathPrefix(t *testing.T) { + testStringPairs(t, "hasPathPrefix", hasPathPrefixTests, hasPathPrefix) +} + +type stringPairTest struct { + in1 string + in2 string + out bool +} + +func testStringPairs(t *testing.T, name string, tests []stringPairTest, f func(string, string) bool) { + for _, tt := range tests { + if out := f(tt.in1, tt.in2); out != tt.out { + t.Errorf("%s(%q, %q) = %v, want %v", name, tt.in1, tt.in2, out, tt.out) } } } diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index b33d800bf..71f14c74a 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -25,29 +25,33 @@ type Package struct { // Note: These fields are part of the go command's public API. // See list.go. It is okay to add fields, but not to change or // remove existing ones. Keep in sync with list.go - Dir string `json:",omitempty"` // directory containing package sources - ImportPath string `json:",omitempty"` // import path of package in dir - Name string `json:",omitempty"` // package name - Doc string `json:",omitempty"` // package documentation string - Target string `json:",omitempty"` // install path - Goroot bool `json:",omitempty"` // is this package found in the Go root? - Standard bool `json:",omitempty"` // is this package part of the standard Go library? - Stale bool `json:",omitempty"` // would 'go install' do anything for this package? - Root string `json:",omitempty"` // Go root or Go path dir containing this package + Dir string `json:",omitempty"` // directory containing package sources + ImportPath string `json:",omitempty"` // import path of package in dir + Name string `json:",omitempty"` // package name + Doc string `json:",omitempty"` // package documentation string + Target string `json:",omitempty"` // install path + Goroot bool `json:",omitempty"` // is this package found in the Go root? + Standard bool `json:",omitempty"` // is this package part of the standard Go library? + Stale bool `json:",omitempty"` // would 'go install' do anything for this package? + Root string `json:",omitempty"` // Go root or Go path dir containing this package + ConflictDir string `json:",omitempty"` // Dir is hidden by this other directory // Source files GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) CgoFiles []string `json:",omitempty"` // .go sources files that import "C" IgnoredGoFiles []string `json:",omitempty"` // .go sources ignored due to build constraints CFiles []string `json:",omitempty"` // .c source files - HFiles []string `json:",omitempty"` // .h source files + CXXFiles []string `json:",omitempty"` // .cc, .cpp and .cxx source files + HFiles []string `json:",omitempty"` // .h, .hh, .hpp and .hxx source files SFiles []string `json:",omitempty"` // .s source files - SysoFiles []string `json:",omitempty"` // .syso system object files added to package SwigFiles []string `json:",omitempty"` // .swig files SwigCXXFiles []string `json:",omitempty"` // .swigcxx files + SysoFiles []string `json:",omitempty"` // .syso system object files added to package // Cgo directives CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler + CgoCPPFLAGS []string `json:",omitempty"` // cgo: flags for C preprocessor + CgoCXXFLAGS []string `json:",omitempty"` // cgo: flags for C++ compiler CgoLDFLAGS []string `json:",omitempty"` // cgo: flags for linker CgoPkgConfig []string `json:",omitempty"` // cgo: pkg-config names @@ -73,14 +77,23 @@ type Package struct { deps []*Package gofiles []string // GoFiles+CgoFiles+TestGoFiles+XTestGoFiles files, absolute paths sfiles []string - allgofiles []string // gofiles + IgnoredGoFiles, absolute paths - target string // installed file for this package (may be executable) - fake bool // synthesized package - forceBuild bool // this package must be rebuilt - forceLibrary bool // this package is a library (even if named "main") - local bool // imported via local path (./ or ../) - localPrefix string // interpret ./ and ../ imports relative to this prefix - exeName string // desired name for temporary executable + allgofiles []string // gofiles + IgnoredGoFiles, absolute paths + target string // installed file for this package (may be executable) + fake bool // synthesized package + forceBuild bool // this package must be rebuilt + forceLibrary bool // this package is a library (even if named "main") + cmdline bool // defined by files listed on command line + local bool // imported via local path (./ or ../) + localPrefix string // interpret ./ and ../ imports relative to this prefix + exeName string // desired name for temporary executable + coverMode string // preprocess Go source files with the coverage tool in this mode + coverVars map[string]*CoverVar // variables created by coverage analysis +} + +// CoverVar holds the name of the generated coverage variables targeting the named file. +type CoverVar struct { + File string // local file name + Var string // name of count struct } func (p *Package) copyBuild(pp *build.Package) { @@ -91,6 +104,7 @@ func (p *Package) copyBuild(pp *build.Package) { p.Name = pp.Name p.Doc = pp.Doc p.Root = pp.Root + p.ConflictDir = pp.ConflictDir // TODO? Target p.Goroot = pp.Goroot p.Standard = p.Goroot && p.ImportPath != "" && !strings.Contains(p.ImportPath, ".") @@ -98,12 +112,15 @@ func (p *Package) copyBuild(pp *build.Package) { p.CgoFiles = pp.CgoFiles p.IgnoredGoFiles = pp.IgnoredGoFiles p.CFiles = pp.CFiles + p.CXXFiles = pp.CXXFiles p.HFiles = pp.HFiles p.SFiles = pp.SFiles - p.SysoFiles = pp.SysoFiles p.SwigFiles = pp.SwigFiles p.SwigCXXFiles = pp.SwigCXXFiles + p.SysoFiles = pp.SysoFiles p.CgoCFLAGS = pp.CgoCFLAGS + p.CgoCPPFLAGS = pp.CgoCPPFLAGS + p.CgoCXXFLAGS = pp.CgoCXXFLAGS p.CgoLDFLAGS = pp.CgoLDFLAGS p.CgoPkgConfig = pp.CgoPkgConfig p.Imports = pp.Imports @@ -115,12 +132,17 @@ func (p *Package) copyBuild(pp *build.Package) { // A PackageError describes an error loading information about a package. type PackageError struct { - ImportStack []string // shortest path from package named on command line to this one - Pos string // position of error - Err string // the error itself + ImportStack []string // shortest path from package named on command line to this one + Pos string // position of error + Err string // the error itself + isImportCycle bool // the error is an import cycle } func (p *PackageError) Error() string { + // Import cycles deserve special treatment. + if p.isImportCycle { + return fmt.Sprintf("%s: %s\npackage %s\n", p.Pos, p.Err, strings.Join(p.ImportStack, "\n\timports ")) + } if p.Pos != "" { // Omit import stack. The full path to the file where the error // is the most important thing. @@ -257,26 +279,38 @@ func reusePackage(p *Package, stk *importStack) *Package { if p.imports == nil { if p.Error == nil { p.Error = &PackageError{ - ImportStack: stk.copy(), - Err: "import cycle not allowed", + ImportStack: stk.copy(), + Err: "import cycle not allowed", + isImportCycle: true, } } p.Incomplete = true } - if p.Error != nil && stk.shorterThan(p.Error.ImportStack) { + // Don't rewrite the import stack in the error if we have an import cycle. + // If we do, we'll lose the path that describes the cycle. + if p.Error != nil && !p.Error.isImportCycle && stk.shorterThan(p.Error.ImportStack) { p.Error.ImportStack = stk.copy() } return p } -// isGoTool is the list of directories for Go programs that are installed in -// $GOROOT/pkg/tool. -var isGoTool = map[string]bool{ - "cmd/api": true, - "cmd/cgo": true, - "cmd/fix": true, - "cmd/vet": true, - "cmd/yacc": true, +type targetDir int + +const ( + toRoot targetDir = iota // to bin dir inside package root (default) + toTool // GOROOT/pkg/tool + toBin // GOROOT/bin +) + +// goTools is a map of Go program import path to install target directory. +var goTools = map[string]targetDir{ + "cmd/api": toTool, + "cmd/cgo": toTool, + "cmd/fix": toTool, + "cmd/yacc": toTool, + "code.google.com/p/go.tools/cmd/cover": toTool, + "code.google.com/p/go.tools/cmd/godoc": toBin, + "code.google.com/p/go.tools/cmd/vet": toTool, } // expandScanner expands a scanner.List error into all the errors in the list. @@ -300,6 +334,23 @@ func expandScanner(err error) error { return err } +var raceExclude = map[string]bool{ + "runtime/race": true, + "runtime/cgo": true, + "cmd/cgo": true, + "syscall": true, + "errors": true, +} + +var cgoExclude = map[string]bool{ + "runtime/cgo": true, +} + +var cgoSyscallExclude = map[string]bool{ + "runtime/cgo": true, + "runtime/race": true, +} + // load populates p using information from bp, err, which should // be the result of calling build.Context.Import. func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package { @@ -326,10 +377,18 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package // Install cross-compiled binaries to subdirectories of bin. elem = full } - if p.build.BinDir != "" { + if p.build.BinDir != gobin && goTools[p.ImportPath] == toBin { + // Override BinDir. + // This is from a subrepo but installs to $GOROOT/bin + // by default anyway (like godoc). + p.target = filepath.Join(gorootBin, elem) + } else if p.build.BinDir != "" { + // Install to GOBIN or bin of GOPATH entry. p.target = filepath.Join(p.build.BinDir, elem) } - if p.Goroot && (isGoTool[p.ImportPath] || strings.HasPrefix(p.ImportPath, "exp/")) { + if goTools[p.ImportPath] == toTool { + // This is for 'go tool'. + // Override all the usual logic and force it into the tool directory. p.target = filepath.Join(gorootPkg, "tool", full) } if p.target != "" && buildContext.GOOS == "windows" { @@ -344,17 +403,22 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package } importPaths := p.Imports - // Packages that use cgo import runtime/cgo implicitly, - // except runtime/cgo itself. - if len(p.CgoFiles) > 0 && (!p.Standard || p.ImportPath != "runtime/cgo") { + // Packages that use cgo import runtime/cgo implicitly. + // Packages that use cgo also import syscall implicitly, + // to wrap errno. + // Exclude certain packages to avoid circular dependencies. + if len(p.CgoFiles) > 0 && (!p.Standard || !cgoExclude[p.ImportPath]) { importPaths = append(importPaths, "runtime/cgo") } + if len(p.CgoFiles) > 0 && (!p.Standard || !cgoSyscallExclude[p.ImportPath]) { + importPaths = append(importPaths, "syscall") + } // Everything depends on runtime, except runtime and unsafe. if !p.Standard || (p.ImportPath != "runtime" && p.ImportPath != "unsafe") { importPaths = append(importPaths, "runtime") // When race detection enabled everything depends on runtime/race. - // Exclude runtime/cgo and cmd/cgo to avoid circular dependencies. - if buildRace && (!p.Standard || (p.ImportPath != "runtime/race" && p.ImportPath != "runtime/cgo" && p.ImportPath != "cmd/cgo")) { + // Exclude certain packages to avoid circular dependencies. + if buildRace && (!p.Standard || !raceExclude[p.ImportPath]) { importPaths = append(importPaths, "runtime/race") } } @@ -389,6 +453,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package p.CgoFiles, p.IgnoredGoFiles, p.CFiles, + p.CXXFiles, p.HFiles, p.SFiles, p.SysoFiles, @@ -476,11 +541,16 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package return p } -// usesSwig returns whether the package needs to run SWIG. +// usesSwig reports whether the package needs to run SWIG. func (p *Package) usesSwig() bool { return len(p.SwigFiles) > 0 || len(p.SwigCXXFiles) > 0 } +// usesCgo reports whether the package needs to run cgo +func (p *Package) usesCgo() bool { + return len(p.CgoFiles) > 0 +} + // swigSoname returns the name of the shared library we create for a // SWIG input file. func (p *Package) swigSoname(file string) string { @@ -611,28 +681,13 @@ func isStale(p *Package, topRoot map[string]bool) bool { return false } - srcs := stringList(p.GoFiles, p.CFiles, p.HFiles, p.SFiles, p.CgoFiles, p.SysoFiles) + srcs := stringList(p.GoFiles, p.CFiles, p.CXXFiles, p.HFiles, p.SFiles, p.CgoFiles, p.SysoFiles, p.SwigFiles, p.SwigCXXFiles) for _, src := range srcs { if olderThan(filepath.Join(p.Dir, src)) { return true } } - for _, src := range stringList(p.SwigFiles, p.SwigCXXFiles) { - if olderThan(filepath.Join(p.Dir, src)) { - return true - } - soname := p.swigSoname(src) - fi, err := os.Stat(soname) - if err != nil { - return true - } - fiSrc, err := os.Stat(src) - if err != nil || fiSrc.ModTime().After(fi.ModTime()) { - return true - } - } - return false } diff --git a/src/cmd/go/run.go b/src/cmd/go/run.go index 91bdc1be2..e6dadd229 100644 --- a/src/cmd/go/run.go +++ b/src/cmd/go/run.go @@ -16,7 +16,7 @@ var cmdRun = &Command{ Short: "compile and run Go program", Long: ` Run compiles and runs the main package comprising the named Go source files. -If no files are named, it compiles and runs all non-test Go source files. +A Go source file is defined to be a file ending in a literal ".go" suffix. For more about build flags, see 'go help build'. diff --git a/src/cmd/go/signal_notunix.go b/src/cmd/go/signal_notunix.go index ef13c1919..29aa9d8c2 100644 --- a/src/cmd/go/signal_notunix.go +++ b/src/cmd/go/signal_notunix.go @@ -11,3 +11,7 @@ import ( ) var signalsToIgnore = []os.Signal{os.Interrupt} + +// signalTrace is the signal to send to make a Go program +// crash with a stack trace. +var signalTrace os.Signal = nil diff --git a/src/cmd/go/signal_unix.go b/src/cmd/go/signal_unix.go index 489a73b83..00c71657f 100644 --- a/src/cmd/go/signal_unix.go +++ b/src/cmd/go/signal_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd package main @@ -12,3 +12,7 @@ import ( ) var signalsToIgnore = []os.Signal{os.Interrupt, syscall.SIGQUIT} + +// signalTrace is the signal to send to make a Go program +// crash with a stack trace. +var signalTrace os.Signal = syscall.SIGQUIT diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index e2264a46e..f71d67818 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -5,15 +5,44 @@ set -e go build -o testgo +go() { + echo TEST ERROR: ran go, not testgo: go "$@" >&2 + exit 2 +} + +started=false +TEST() { + if $started; then + stop + fi + echo TEST: "$@" + started=true + ok=true +} +stop() { + if ! $started; then + echo TEST ERROR: stop missing start >&2 + exit 2 + fi + started=false + if $ok; then + echo PASS + else + echo FAIL + allok=false + fi +} ok=true +allok=true unset GOPATH unset GOBIN +TEST 'file:line in error messages' # Test that error messages have file:line information at beginning of # the line. Also test issue 4917: that the error is on stderr. -d=$(mktemp -d -t testgoXXX) +d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) fn=$d/err.go echo "package main" > $fn echo 'import "bar"' >> $fn @@ -28,6 +57,7 @@ rm -r $d # Test local (./) imports. testlocal() { local="$1" + TEST local imports $2 '(easy)' ./testgo build -o hello "testdata/$local/easy.go" ./hello >hello.out if ! grep -q '^easysub\.Hello' hello.out; then @@ -36,6 +66,7 @@ testlocal() { ok=false fi + TEST local imports $2 '(easysub)' ./testgo build -o hello "testdata/$local/easysub/main.go" ./hello >hello.out if ! grep -q '^easysub\.Hello' hello.out; then @@ -44,6 +75,7 @@ testlocal() { ok=false fi + TEST local imports $2 '(hard)' ./testgo build -o hello "testdata/$local/hard.go" ./hello >hello.out if ! grep -q '^sub\.Hello' hello.out || ! grep -q '^subsub\.Hello' hello.out ; then @@ -55,6 +87,7 @@ testlocal() { rm -f hello.out hello # Test that go install x.go fails. + TEST local imports $2 '(go install should fail)' if ./testgo install "testdata/$local/easy.go" >/dev/null 2>&1; then echo "go install testdata/$local/easy.go succeeded" ok=false @@ -62,22 +95,53 @@ testlocal() { } # Test local imports -testlocal local +testlocal local '' # Test local imports again, with bad characters in the directory name. bad='#$%:, &()*;<=>?\^{}' rm -rf "testdata/$bad" cp -R testdata/local "testdata/$bad" -testlocal "$bad" +testlocal "$bad" 'with bad characters in path' rm -rf "testdata/$bad" +TEST error message for syntax error in test go file says FAIL +export GOPATH=$(pwd)/testdata +if ./testgo test syntaxerror 2>testdata/err; then + echo 'go test syntaxerror succeeded' + ok=false +elif ! grep FAIL testdata/err >/dev/null; then + echo 'go test did not say FAIL:' + cat testdata/err + ok=false +fi +rm -f ./testdata/err +unset GOPATH + +TEST wildcards do not look in useless directories +export GOPATH=$(pwd)/testdata +if ./testgo list ... >testdata/err 2>&1; then + echo "go list ... succeeded" + ok=false +elif ! grep badpkg testdata/err >/dev/null; then + echo "go list ... failure does not mention badpkg" + cat testdata/err + ok=false +elif ! ./testgo list m... >testdata/err 2>&1; then + echo "go list m... failed" + ok=false +fi +rm -rf ./testdata/err +unset GOPATH + # Test tests with relative imports. +TEST relative imports '(go test)' if ! ./testgo test ./testdata/testimport; then echo "go test ./testdata/testimport failed" ok=false fi # Test installation with relative imports. +TEST relative imports '(go test -i)' if ! ./testgo test -i ./testdata/testimport; then echo "go test -i ./testdata/testimport failed" ok=false @@ -85,13 +149,40 @@ fi # Test tests with relative imports in packages synthesized # from Go files named on the command line. +TEST relative imports in command-line package if ! ./testgo test ./testdata/testimport/*.go; then echo "go test ./testdata/testimport/*.go failed" ok=false fi +TEST version control error message includes correct directory +export GOPATH=$(pwd)/testdata/shadow/root1 +if ./testgo get -u foo 2>testdata/err; then + echo "go get -u foo succeeded unexpectedly" + ok=false +elif ! grep testdata/shadow/root1/src/foo testdata/err >/dev/null; then + echo "go get -u error does not mention shadow/root1/src/foo:" + cat testdata/err + ok=false +fi +unset GOPATH + +TEST go install fails with no buildable files +export GOPATH=$(pwd)/testdata +export CGO_ENABLED=0 +if ./testgo install cgotest 2>testdata/err; then + echo "go install cgotest succeeded unexpectedly" +elif ! grep 'no buildable Go source files' testdata/err >/dev/null; then + echo "go install cgotest did not report 'no buildable Go source files'" + cat testdata/err + ok=false +fi +unset CGO_ENABLED +unset GOPATH + # Test that without $GOBIN set, binaries get installed # into the GOPATH bin directory. +TEST install into GOPATH rm -rf testdata/bin if ! GOPATH=$(pwd)/testdata ./testgo install go-cmd-test; then echo "go install go-cmd-test failed" @@ -101,7 +192,29 @@ elif ! test -x testdata/bin/go-cmd-test; then ok=false fi +TEST package main_test imports archive not binary +export GOBIN=$(pwd)/testdata/bin +mkdir -p $GOBIN +export GOPATH=$(pwd)/testdata +touch ./testdata/src/main_test/m.go +if ! ./testgo test main_test; then + echo "go test main_test failed without install" + ok=false +elif ! ./testgo install main_test; then + echo "go test main_test failed" + ok=false +elif [ "$(./testgo list -f '{{.Stale}}' main_test)" != false ]; then + echo "after go install, main listed as stale" + ok=false +elif ! ./testgo test main_test; then + echo "go test main_test failed after install" + ok=false +fi +rm -rf $GOBIN +unset GOBIN + # And with $GOBIN set, binaries get installed to $GOBIN. +TEST install into GOBIN if ! GOBIN=$(pwd)/testdata/bin1 GOPATH=$(pwd)/testdata ./testgo install go-cmd-test; then echo "go install go-cmd-test failed" ok=false @@ -112,12 +225,19 @@ fi # Without $GOBIN set, installing a program outside $GOPATH should fail # (there is nowhere to install it). -if ./testgo install testdata/src/go-cmd-test/helloworld.go; then +TEST install without destination fails +if ./testgo install testdata/src/go-cmd-test/helloworld.go 2>testdata/err; then echo "go install testdata/src/go-cmd-test/helloworld.go should have failed, did not" ok=false +elif ! grep 'no install location for .go files listed on command line' testdata/err; then + echo "wrong error:" + cat testdata/err + ok=false fi +rm -f testdata/err # With $GOBIN set, should install there. +TEST install to GOBIN '(command-line package)' if ! GOBIN=$(pwd)/testdata/bin1 ./testgo install testdata/src/go-cmd-test/helloworld.go; then echo "go install testdata/src/go-cmd-test/helloworld.go failed" ok=false @@ -126,24 +246,88 @@ elif ! test -x testdata/bin1/helloworld; then ok=false fi +TEST godoc installs into GOBIN +d=$(mktemp -d -t testgoXXX) +export GOPATH=$d +mkdir $d/gobin +GOBIN=$d/gobin ./testgo get code.google.com/p/go.tools/cmd/godoc +if [ ! -x $d/gobin/godoc ]; then + echo did not install godoc to '$GOBIN' + GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' code.google.com/p/go.tools/cmd/godoc + ok=false +fi + +TEST godoc installs into GOROOT +rm -f $GOROOT/bin/godoc +./testgo install code.google.com/p/go.tools/cmd/godoc +if [ ! -x $GOROOT/bin/godoc ]; then + echo did not install godoc to '$GOROOT/bin' + ./testgo list -f 'Target: {{.Target}}' code.google.com/p/go.tools/cmd/godoc + ok=false +fi + +TEST cmd/fix installs into tool +GOOS=$(./testgo env GOOS) +GOARCH=$(./testgo env GOARCH) +rm -f $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix +./testgo install cmd/fix +if [ ! -x $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix ]; then + echo 'did not install cmd/fix to $GOROOT/pkg/tool' + GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' cmd/fix + ok=false +fi +rm -f $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix +GOBIN=$d/gobin ./testgo install cmd/fix +if [ ! -x $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix ]; then + echo 'did not install cmd/fix to $GOROOT/pkg/tool with $GOBIN set' + GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' cmd/fix + ok=false +fi + +TEST gopath program installs into GOBIN +mkdir $d/src/progname +echo 'package main; func main() {}' >$d/src/progname/p.go +GOBIN=$d/gobin ./testgo install progname +if [ ! -x $d/gobin/progname ]; then + echo 'did not install progname to $GOBIN/progname' + ./testgo list -f 'Target: {{.Target}}' cmd/api + ok=false +fi +rm -f $d/gobin/progname $d/bin/progname + +TEST gopath program installs into GOPATH/bin +./testgo install progname +if [ ! -x $d/bin/progname ]; then + echo 'did not install progname to $GOPATH/bin/progname' + ./testgo list -f 'Target: {{.Target}}' progname + ok=false +fi + +unset GOPATH +rm -rf $d + # Reject relative paths in GOPATH. +TEST reject relative paths in GOPATH '(command-line package)' if GOPATH=. ./testgo build testdata/src/go-cmd-test/helloworld.go; then echo 'GOPATH="." go build should have failed, did not' ok=false fi +TEST reject relative paths in GOPATH if GOPATH=:$(pwd)/testdata:. ./testgo build go-cmd-test; then echo 'GOPATH=":$(pwd)/testdata:." go build should have failed, did not' ok=false fi # issue 4104 +TEST go test with package listed multiple times if [ $(./testgo test fmt fmt fmt fmt fmt | wc -l) -ne 1 ] ; then echo 'go test fmt fmt fmt fmt fmt tested the same package multiple times' ok=false fi # ensure that output of 'go list' is consistent between runs +TEST go list is consistent ./testgo list std > test_std.list if ! ./testgo list std | cmp -s test_std.list - ; then echo "go list std ordering is inconsistent" @@ -151,32 +335,38 @@ if ! ./testgo list std | cmp -s test_std.list - ; then fi rm -f test_std.list -# issue 4096. Validate the output of unsucessful go install foo/quxx +# issue 4096. Validate the output of unsuccessful go install foo/quxx +TEST unsuccessful go install should mention missing package if [ $(./testgo install 'foo/quxx' 2>&1 | grep -c 'cannot find package "foo/quxx" in any of') -ne 1 ] ; then echo 'go install foo/quxx expected error: .*cannot find package "foo/quxx" in any of' ok=false fi # test GOROOT search failure is reported +TEST GOROOT search failure reporting if [ $(./testgo install 'foo/quxx' 2>&1 | egrep -c 'foo/quxx \(from \$GOROOT\)$') -ne 1 ] ; then echo 'go install foo/quxx expected error: .*foo/quxx (from $GOROOT)' ok=false fi # test multiple GOPATH entries are reported separately +TEST multiple GOPATH entries reported separately if [ $(GOPATH=$(pwd)/testdata/a:$(pwd)/testdata/b ./testgo install 'foo/quxx' 2>&1 | egrep -c 'testdata/./src/foo/quxx') -ne 2 ] ; then echo 'go install foo/quxx expected error: .*testdata/a/src/foo/quxx (from $GOPATH)\n.*testdata/b/src/foo/quxx' ok=false fi # test (from $GOPATH) annotation is reported for the first GOPATH entry +TEST mention GOPATH in first GOPATH entry if [ $(GOPATH=$(pwd)/testdata/a:$(pwd)/testdata/b ./testgo install 'foo/quxx' 2>&1 | egrep -c 'testdata/a/src/foo/quxx \(from \$GOPATH\)$') -ne 1 ] ; then echo 'go install foo/quxx expected error: .*testdata/a/src/foo/quxx (from $GOPATH)' ok=false fi # but not on the second +TEST but not the second entry if [ $(GOPATH=$(pwd)/testdata/a:$(pwd)/testdata/b ./testgo install 'foo/quxx' 2>&1 | egrep -c 'testdata/b/src/foo/quxx$') -ne 1 ] ; then echo 'go install foo/quxx expected error: .*testdata/b/src/foo/quxx' ok=false fi # test missing GOPATH is reported +TEST missing GOPATH is reported if [ $(GOPATH= ./testgo install 'foo/quxx' 2>&1 | egrep -c '\(\$GOPATH not set\)$') -ne 1 ] ; then echo 'go install foo/quxx expected error: ($GOPATH not set)' ok=false @@ -184,6 +374,7 @@ fi # issue 4186. go get cannot be used to download packages to $GOROOT # Test that without GOPATH set, go get should fail +TEST without GOPATH, go get fails d=$(mktemp -d -t testgoXXX) mkdir -p $d/src/pkg if GOPATH= GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ; then @@ -191,7 +382,9 @@ if GOPATH= GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ok=false fi rm -rf $d + # Test that with GOPATH=$GOROOT, go get should fail +TEST with GOPATH=GOROOT, go get fails d=$(mktemp -d -t testgoXXX) mkdir -p $d/src/pkg if GOPATH=$d GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ; then @@ -200,7 +393,7 @@ if GOPATH=$d GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpat fi rm -rf $d -# issue 3941: args with spaces +TEST ldflags arguments with spaces '(issue 3941)' d=$(mktemp -d -t testgoXXX) cat >$d/main.go<<EOF package main @@ -215,9 +408,9 @@ if ! grep -q '^hello world' hello.out; then cat hello.out ok=false fi -rm -rf $d +rm -rf $d hello.out -# test that go test -cpuprofile leaves binary behind +TEST go test -cpuprofile leaves binary behind ./testgo test -cpuprofile strings.prof strings || ok=false if [ ! -x strings.test ]; then echo "go test -cpuprofile did not create strings.test" @@ -225,9 +418,10 @@ if [ ! -x strings.test ]; then fi rm -f strings.prof strings.test -# issue 4568. test that symlinks don't screw things up too badly. +TEST symlinks do not confuse go list '(issue 4568)' old=$(pwd) -d=$(mktemp -d -t testgoXXX) +tmp=$(cd /tmp && pwd -P) +d=$(TMPDIR=$tmp mktemp -d -t testgoXXX) mkdir -p $d/src ( ln -s $d $d/src/dir1 @@ -235,8 +429,8 @@ mkdir -p $d/src echo package p >p.go export GOPATH=$d if [ "$($old/testgo list -f '{{.Root}}' .)" != "$d" ]; then - echo got lost in symlink tree: - pwd + echo Confused by symlinks. + echo "Package in current directory $(pwd) should have Root $d" env|grep WD $old/testgo list -json . dir1 touch $d/failed @@ -247,8 +441,8 @@ if [ -f $d/failed ]; then fi rm -rf $d -# issue 4515. -d=$(mktemp -d -t testgoXXX) +TEST 'install with tags (issue 4515)' +d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) mkdir -p $d/src/example/a $d/src/example/b $d/bin cat >$d/src/example/a/main.go <<EOF package main @@ -280,8 +474,8 @@ fi unset GOPATH rm -rf $d -# issue 4773. case-insensitive collisions -d=$(mktemp -d -t testgoXXX) +TEST case collisions '(issue 4773)' +d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) export GOPATH=$d mkdir -p $d/src/example/a $d/src/example/b cat >$d/src/example/a/a.go <<EOF @@ -318,17 +512,120 @@ elif ! grep "case-insensitive file name collision" $d/out >/dev/null; then echo go list example/b did not report file name collision. ok=false fi + +TEST go get cover +./testgo get code.google.com/p/go.tools/cmd/cover || ok=false + unset GOPATH rm -rf $d +TEST shadowing logic +export GOPATH=$(pwd)/testdata/shadow/root1:$(pwd)/testdata/shadow/root2 + +# The math in root1 is not "math" because the standard math is. +cdir=$(./testgo list -f '({{.ImportPath}}) ({{.ConflictDir}})' ./testdata/shadow/root1/src/math) +if [ "$cdir" != "(_$(pwd)/testdata/shadow/root1/src/math) ($GOROOT/src/pkg/math)" ]; then + echo shadowed math is not shadowed: "$cdir" + ok=false +fi + +# The foo in root1 is "foo". +cdir=$(./testgo list -f '({{.ImportPath}}) ({{.ConflictDir}})' ./testdata/shadow/root1/src/foo) +if [ "$cdir" != "(foo) ()" ]; then + echo unshadowed foo is shadowed: "$cdir" + ok=false +fi + +# The foo in root2 is not "foo" because the foo in root1 got there first. +cdir=$(./testgo list -f '({{.ImportPath}}) ({{.ConflictDir}})' ./testdata/shadow/root2/src/foo) +if [ "$cdir" != "(_$(pwd)/testdata/shadow/root2/src/foo) ($(pwd)/testdata/shadow/root1/src/foo)" ]; then + echo shadowed foo is not shadowed: "$cdir" + ok=false +fi + +# The error for go install should mention the conflicting directory. +err=$(! ./testgo install ./testdata/shadow/root2/src/foo 2>&1) +if [ "$err" != "go install: no install location for directory $(pwd)/testdata/shadow/root2/src/foo hidden by $(pwd)/testdata/shadow/root1/src/foo" ]; then + echo wrong shadowed install error: "$err" + ok=false +fi + # Only succeeds if source order is preserved. -./testgo test testdata/example[12]_test.go +TEST source file name order preserved +./testgo test testdata/example[12]_test.go || ok=false + +# Check that coverage analysis works at all. +# Don't worry about the exact numbers +TEST coverage runs +./testgo test -short -coverpkg=strings strings regexp || ok=false +./testgo test -short -cover strings math regexp || ok=false + +TEST cgo depends on syscall +rm -rf $GOROOT/pkg/*_race +d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) +export GOPATH=$d +mkdir -p $d/src/foo +echo ' +package foo +//#include <stdio.h> +import "C" +' >$d/src/foo/foo.go +./testgo build -race foo || ok=false +rm -rf $d +unset GOPATH + +TEST cgo shows full path names +d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) +export GOPATH=$d +mkdir -p $d/src/x/y/dirname +echo ' +package foo +import "C" +func f() { +' >$d/src/x/y/dirname/foo.go +if ./testgo build x/y/dirname >$d/err 2>&1; then + echo build succeeded unexpectedly. + ok=false +elif ! grep x/y/dirname $d/err >/dev/null; then + echo error did not use full path. + cat $d/err + ok=false +fi +rm -rf $d +unset GOPATH + +TEST 'cgo handles -Wl,$ORIGIN' +d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) +export GOPATH=$d +mkdir -p $d/src/origin +echo ' +package origin +// #cgo LDFLAGS: -Wl,-rpath -Wl,$ORIGIN +// void f(void) {} +import "C" + +func f() { C.f() } +' >$d/src/origin/origin.go +if ! ./testgo build origin; then + echo build failed + ok=false +fi +rm -rf $d +unset GOPATH + +TEST 'Issue 6480: "go test -c -test.bench=XXX fmt" should not hang' +if ! ./testgo test -c -test.bench=XXX fmt; then + echo build test failed + ok=false +fi +rm -f fmt.test # clean up +if $started; then stop; fi rm -rf testdata/bin testdata/bin1 rm -f testgo -if $ok; then +if $allok; then echo PASS else echo FAIL diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go index b1db16f77..06ac9d206 100644 --- a/src/cmd/go/test.go +++ b/src/cmd/go/test.go @@ -12,10 +12,12 @@ import ( "go/doc" "go/parser" "go/token" + "log" "os" "os/exec" "path" "path/filepath" + "regexp" "runtime" "sort" "strings" @@ -32,7 +34,7 @@ func init() { var cmdTest = &Command{ CustomFlags: true, - UsageLine: "test [-c] [-i] [build flags] [packages] [flags for test binary]", + UsageLine: "test [-c] [-i] [build and test flags] [packages] [flags for test binary]", Short: "test packages", Long: ` 'Go test' automates testing the packages named by the import paths. @@ -46,8 +48,10 @@ It prints a summary of the test results in the format: followed by detailed output for each failed package. 'Go test' recompiles each package along with any files with names matching -the file pattern "*_test.go". These additional files can contain test functions, -benchmark functions, and example functions. See 'go help testfunc' for more. +the file pattern "*_test.go". +Files whose names begin with "_" (including "_test.go") or "." are ignored. +These additional files can contain test functions, benchmark functions, and +example functions. See 'go help testfunc' for more. Each listed package causes the execution of a separate test binary. Test files that declare a package with the suffix "_test" will be compiled as a @@ -71,6 +75,11 @@ In addition to the build flags, the flags handled by 'go test' itself are: The test binary also accepts flags that control execution of the test; these flags are also accessible by 'go test'. See 'go help testflag' for details. +If the test binary needs any other flags, they should be presented after the +package names. The go tool treats as a flag the first argument that begins with +a minus sign that it does not recognize itself; that argument and all subsequent +arguments are passed as arguments to the test binary. + For more about build flags, see 'go help build'. For more about specifying packages, see 'go help packages'. @@ -119,6 +128,30 @@ control the execution of any test: if -test.blockprofile is set without this flag, all blocking events are recorded, equivalent to -test.blockprofilerate=1. + -cover + Enable coverage analysis. + + -covermode set,count,atomic + Set the mode for coverage analysis for the package[s] + being tested. The default is "set". + The values: + set: bool: does this statement run? + count: int: how many times does this statement run? + atomic: int: count, but correct in multithreaded tests; + significantly more expensive. + Sets -cover. + + -coverpkg pkg1,pkg2,pkg3 + Apply coverage analysis in each test to the given list of packages. + The default is for each test to analyze only the package being tested. + Packages are specified as import paths. + Sets -cover. + + -coverprofile cover.out + Write a coverage profile to the specified file after all tests + have passed. + Sets -cover. + -cpu 1,2,4 Specify a list of GOMAXPROCS values for which the tests or benchmarks should be executed. The default is the current value @@ -139,6 +172,10 @@ control the execution of any test: garbage collector, provided the test can run in the available memory without garbage collection. + -outputdir directory + Place output files from profiling in the specified directory, + by default the directory in which "go test" is running. + -parallel n Allow parallel execution of test functions that call t.Parallel. The value of this flag is the maximum number of tests to run @@ -176,8 +213,8 @@ will compile the test binary and then run it as pkg.test -test.v -test.cpuprofile=prof.out -dir=testdata -update -The test flags that generate profiles also leave the test binary in pkg.test -for use when analyzing the profiles. +The test flags that generate profiles (other than for coverage) also +leave the test binary in pkg.test for use when analyzing the profiles. Flags not recognized by 'go test' must be placed after any specified packages. `, @@ -229,12 +266,17 @@ See the documentation of the testing package for more information. } var ( - testC bool // -c flag - testProfile bool // some profiling flag - testI bool // -i flag - testV bool // -v flag - testFiles []string // -file flag(s) TODO: not respected - testTimeout string // -timeout flag + testC bool // -c flag + testCover bool // -cover flag + testCoverMode string // -covermode flag + testCoverPaths []string // -coverpkg flag + testCoverPkgs []*Package // -coverpkg flag + testProfile bool // some profiling flag + testNeedBinary bool // profile needs to keep binary around + testI bool // -i flag + testV bool // -v flag + testFiles []string // -file flag(s) TODO: not respected + testTimeout string // -timeout flag testArgs []string testBench bool testStreamOutput bool // show output as it is generated @@ -243,6 +285,12 @@ var ( testKillTimeout = 10 * time.Minute ) +var testMainDeps = map[string]bool{ + // Dependencies for testmain. + "testing": true, + "regexp": true, +} + func runTest(cmd *Command, args []string) { var pkgArgs []string pkgArgs, testArgs = testFlags(args) @@ -289,11 +337,11 @@ func runTest(cmd *Command, args []string) { if testI { buildV = testV - deps := map[string]bool{ - // Dependencies for testmain. - "testing": true, - "regexp": true, + deps := make(map[string]bool) + for dep := range testMainDeps { + deps[dep] = true } + for _, p := range pkgs { // Dependencies for each test. for _, path := range p.Imports { @@ -331,7 +379,7 @@ func runTest(cmd *Command, args []string) { a.deps = append(a.deps, b.action(modeInstall, modeInstall, p)) } b.do(a) - if !testC { + if !testC || a.failed { return } b.init() @@ -339,6 +387,34 @@ func runTest(cmd *Command, args []string) { var builds, runs, prints []*action + if testCoverPaths != nil { + // Load packages that were asked about for coverage. + // packagesForBuild exits if the packages cannot be loaded. + testCoverPkgs = packagesForBuild(testCoverPaths) + + // Warn about -coverpkg arguments that are not actually used. + used := make(map[string]bool) + for _, p := range pkgs { + used[p.ImportPath] = true + for _, dep := range p.Deps { + used[dep] = true + } + } + for _, p := range testCoverPkgs { + if !used[p.ImportPath] { + log.Printf("warning: no packages being tested depend on %s", p.ImportPath) + } + } + + // Mark all the coverage packages for rebuilding with coverage. + for _, p := range testCoverPkgs { + p.Stale = true // rebuild + p.fake = true // do not warn about rebuild + p.coverMode = testCoverMode + p.coverVars = declareCoverVars(p.ImportPath, p.GoFiles...) + } + } + // Prepare build + run + print actions for all packages being tested. for _, p := range pkgs { buildTest, runTest, printTest, err := b.test(p) @@ -347,10 +423,12 @@ func runTest(cmd *Command, args []string) { if strings.HasPrefix(str, "\n") { str = str[1:] } + failed := fmt.Sprintf("FAIL\t%s [setup failed]\n", p.ImportPath) + if p.ImportPath != "" { - errorf("# %s\n%s", p.ImportPath, str) + errorf("# %s\n%s\n%s", p.ImportPath, str, failed) } else { - errorf("%s", str) + errorf("%s\n%s", str, failed) } continue } @@ -370,16 +448,15 @@ func runTest(cmd *Command, args []string) { } } - // If we are benchmarking, force everything to - // happen in serial. Could instead allow all the - // builds to run before any benchmarks start, - // but try this for now. - if testBench { - for i, a := range builds { - if i > 0 { - // Make build of test i depend on - // completing the run of test i-1. - a.deps = append(a.deps, runs[i-1]) + // Force benchmarks to run in serial. + if !testC && testBench { + // The first run must wait for all builds. + // Later runs must wait for the previous run's print. + for i, run := range runs { + if i == 0 { + run.deps = append(run.deps, builds...) + } else { + run.deps = append(run.deps, prints[i-1]) } } } @@ -390,11 +467,22 @@ func runTest(cmd *Command, args []string) { for _, p := range pkgs { okBuild[p] = true } - warned := false for _, a := range actionList(root) { - if a.p != nil && a.f != nil && !okBuild[a.p] && !a.p.fake && !a.p.local { - okBuild[a.p] = true // don't warn again + if a.p == nil || okBuild[a.p] { + continue + } + okBuild[a.p] = true // warn at most once + + // Don't warn about packages being rebuilt because of + // things like coverage analysis. + for _, p1 := range a.p.imports { + if p1.fake { + a.p.fake = true + } + } + + if a.f != nil && !okBuild[a.p] && !a.p.fake && !a.p.local { if !warned { fmt.Fprintf(os.Stderr, "warning: building out-of-date packages:\n") warned = true @@ -417,11 +505,20 @@ func runTest(cmd *Command, args []string) { b.do(root) } +func contains(x []string, s string) bool { + for _, t := range x { + if t == s { + return true + } + } + return false +} + func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, err error) { if len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 { build := &action{p: p} - run := &action{p: p} - print := &action{f: (*builder).notest, p: p, deps: []*action{build}} + run := &action{p: p, deps: []*action{build}} + print := &action{f: (*builder).notest, p: p, deps: []*action{run}} return build, run, print, nil } @@ -487,12 +584,15 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, if err := b.mkdir(ptestDir); err != nil { return nil, nil, nil, err } - if err := writeTestmain(filepath.Join(testDir, "_testmain.go"), p); err != nil { - return nil, nil, nil, err - } + + // Should we apply coverage analysis locally, + // only for this package and only for this test? + // Yes, if -cover is on but -coverpkg has not specified + // a list of packages for global coverage. + localCover := testCover && testCoverPaths == nil // Test package. - if len(p.TestGoFiles) > 0 { + if len(p.TestGoFiles) > 0 || localCover || p.Name == "main" { ptest = new(Package) *ptest = *p ptest.GoFiles = nil @@ -515,6 +615,11 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, m[k] = append(m[k], v...) } ptest.build.ImportPos = m + + if localCover { + ptest.coverMode = testCoverMode + ptest.coverVars = declareCoverVars(ptest.ImportPath, ptest.GoFiles...) + } } else { ptest = p } @@ -548,6 +653,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, Root: p.Root, imports: []*Package{ptest}, build: &build.Package{Name: "main"}, + pkgdir: testDir, fake: true, Stale: true, } @@ -557,15 +663,50 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, // The generated main also imports testing and regexp. stk.push("testmain") - ptesting := loadImport("testing", "", &stk, nil) - if ptesting.Error != nil { - return nil, nil, nil, ptesting.Error + for dep := range testMainDeps { + if ptest.ImportPath != dep { + p1 := loadImport("testing", "", &stk, nil) + if p1.Error != nil { + return nil, nil, nil, p1.Error + } + pmain.imports = append(pmain.imports, p1) + } + } + + if testCoverPkgs != nil { + // Add imports, but avoid duplicates. + seen := map[*Package]bool{p: true, ptest: true} + for _, p1 := range pmain.imports { + seen[p1] = true + } + for _, p1 := range testCoverPkgs { + if !seen[p1] { + seen[p1] = true + pmain.imports = append(pmain.imports, p1) + } + } } - pregexp := loadImport("regexp", "", &stk, nil) - if pregexp.Error != nil { - return nil, nil, nil, pregexp.Error + + if ptest != p && localCover { + // We have made modifications to the package p being tested + // and are rebuilding p (as ptest), writing it to the testDir tree. + // Arrange to rebuild, writing to that same tree, all packages q + // such that the test depends on q, and q depends on p. + // This makes sure that q sees the modifications to p. + // Strictly speaking, the rebuild is only necessary if the + // modifications to p change its export metadata, but + // determining that is a bit tricky, so we rebuild always. + // + // This will cause extra compilation, so for now we only do it + // when testCover is set. The conditions are more general, though, + // and we may find that we need to do it always in the future. + recompileForTest(pmain, p, ptest, testDir) } - pmain.imports = append(pmain.imports, ptesting, pregexp) + + if err := writeTestmain(filepath.Join(testDir, "_testmain.go"), pmain, ptest); err != nil { + return nil, nil, nil, err + } + computeStale(pmain) if ptest != p { @@ -589,7 +730,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, a.target = filepath.Join(testDir, testBinary) + exeSuffix pmainAction := a - if testC || testProfile { + if testC || testNeedBinary { // -c or profiling flag: create action to copy binary to ./test.out. runAction = &action{ f: (*builder).install, @@ -624,6 +765,75 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, return pmainAction, runAction, printAction, nil } +func recompileForTest(pmain, preal, ptest *Package, testDir string) { + // The "test copy" of preal is ptest. + // For each package that depends on preal, make a "test copy" + // that depends on ptest. And so on, up the dependency tree. + testCopy := map[*Package]*Package{preal: ptest} + for _, p := range packageList([]*Package{pmain}) { + // Copy on write. + didSplit := false + split := func() { + if didSplit { + return + } + didSplit = true + if p.pkgdir != testDir { + p1 := new(Package) + testCopy[p] = p1 + *p1 = *p + p1.imports = make([]*Package, len(p.imports)) + copy(p1.imports, p.imports) + p = p1 + p.pkgdir = testDir + p.target = "" + p.fake = true + p.Stale = true + } + } + + // Update p.deps and p.imports to use at test copies. + for i, dep := range p.deps { + if p1 := testCopy[dep]; p1 != nil && p1 != dep { + split() + p.deps[i] = p1 + } + } + for i, imp := range p.imports { + if p1 := testCopy[imp]; p1 != nil && p1 != imp { + split() + p.imports[i] = p1 + } + } + } +} + +var coverIndex = 0 + +// isTestFile reports whether the source file is a set of tests and should therefore +// be excluded from coverage analysis. +func isTestFile(file string) bool { + // We don't cover tests, only the code they test. + return strings.HasSuffix(file, "_test.go") +} + +// declareCoverVars attaches the required cover variables names +// to the files, to be used when annotating the files. +func declareCoverVars(importPath string, files ...string) map[string]*CoverVar { + coverVars := make(map[string]*CoverVar) + for _, file := range files { + if isTestFile(file) { + continue + } + coverVars[file] = &CoverVar{ + File: filepath.Join(importPath, file), + Var: fmt.Sprintf("GoCover_%d", coverIndex), + } + coverIndex++ + } + return coverVars +} + // runTest is the action for running a test binary. func (b *builder) runTest(a *action) error { args := stringList(a.deps[0].target, testArgs) @@ -688,10 +898,23 @@ func (b *builder) runTest(a *action) error { go func() { done <- cmd.Wait() }() + Outer: select { case err = <-done: // ok case <-tick.C: + if signalTrace != nil { + // Send a quit signal in the hope that the program will print + // a stack trace and exit. Give it five seconds before resorting + // to Kill. + cmd.Process.Signal(signalTrace) + select { + case err = <-done: + fmt.Fprintf(&buf, "*** Test killed with %v: ran too long (%v).\n", signalTrace, testKillTimeout) + break Outer + case <-time.After(5 * time.Second): + } + } cmd.Process.Kill() err = <-done fmt.Fprintf(&buf, "*** Test killed: ran too long (%v).\n", testKillTimeout) @@ -704,7 +927,7 @@ func (b *builder) runTest(a *action) error { if testShowPass { a.testOutput.Write(out) } - fmt.Fprintf(a.testOutput, "ok \t%s\t%s\n", a.p.ImportPath, t) + fmt.Fprintf(a.testOutput, "ok \t%s\t%s%s\n", a.p.ImportPath, t, coveragePercentage(out)) return nil } @@ -720,6 +943,25 @@ func (b *builder) runTest(a *action) error { return nil } +// coveragePercentage returns the coverage results (if enabled) for the +// test. It uncovers the data by scanning the output from the test run. +func coveragePercentage(out []byte) string { + if !testCover { + return "" + } + // The string looks like + // test coverage for encoding/binary: 79.9% of statements + // Extract the piece from the percentage to the end of the line. + re := regexp.MustCompile(`coverage: (.*)\n`) + matches := re.FindSubmatch(out) + if matches == nil { + // Probably running "go test -cover" not "go test -cover fmt". + // The coverage output will appear in the output directly. + return "" + } + return fmt.Sprintf("\tcoverage: %s", matches[1]) +} + // cleanTest is the action for cleaning up after a test. func (b *builder) cleanTest(a *action) error { if buildWork { @@ -760,11 +1002,24 @@ func isTest(name, prefix string) bool { return !unicode.IsLower(rune) } +type coverInfo struct { + Package *Package + Vars map[string]*CoverVar +} + // writeTestmain writes the _testmain.go file for package p to // the file named out. -func writeTestmain(out string, p *Package) error { +func writeTestmain(out string, pmain, p *Package) error { + var cover []coverInfo + for _, cp := range pmain.imports { + if len(cp.coverVars) > 0 { + cover = append(cover, coverInfo{cp, cp.coverVars}) + } + } + t := &testFuncs{ Package: p, + Cover: cover, } for _, file := range p.TestGoFiles { if err := t.load(filepath.Join(p.Dir, file), "_test", &t.NeedTest); err != nil { @@ -797,6 +1052,31 @@ type testFuncs struct { Package *Package NeedTest bool NeedXtest bool + Cover []coverInfo +} + +func (t *testFuncs) CoverMode() string { + return testCoverMode +} + +func (t *testFuncs) CoverEnabled() bool { + return testCover +} + +// Covered returns a string describing which packages are being tested for coverage. +// If the covered package is the same as the tested package, it returns the empty string. +// Otherwise it is a comma-separated human-readable list of packages beginning with +// " in", ready for use in the coverage message. +func (t *testFuncs) Covered() string { + if testCoverPaths == nil { + return "" + } + return " in " + strings.Join(testCoverPaths, ", ") +} + +// Tested returns the name of the package being tested. +func (t *testFuncs) Tested() string { + return t.Package.Name } type testFunc struct { @@ -862,6 +1142,9 @@ import ( {{if .NeedXtest}} _xtest {{.Package.ImportPath | printf "%s_test" | printf "%q"}} {{end}} +{{range $i, $p := .Cover}} + _cover{{$i}} {{$p.Package.ImportPath | printf "%q"}} +{{end}} ) var tests = []testing.InternalTest{ @@ -896,7 +1179,54 @@ func matchString(pat, str string) (result bool, err error) { return matchRe.MatchString(str), nil } +{{if .CoverEnabled}} + +// Only updated by init functions, so no need for atomicity. +var ( + coverCounters = make(map[string][]uint32) + coverBlocks = make(map[string][]testing.CoverBlock) +) + +func init() { + {{range $i, $p := .Cover}} + {{range $file, $cover := $p.Vars}} + coverRegisterFile({{printf "%q" $cover.File}}, _cover{{$i}}.{{$cover.Var}}.Count[:], _cover{{$i}}.{{$cover.Var}}.Pos[:], _cover{{$i}}.{{$cover.Var}}.NumStmt[:]) + {{end}} + {{end}} +} + +func coverRegisterFile(fileName string, counter []uint32, pos []uint32, numStmts []uint16) { + if 3*len(counter) != len(pos) || len(counter) != len(numStmts) { + panic("coverage: mismatched sizes") + } + if coverCounters[fileName] != nil { + // Already registered. + return + } + coverCounters[fileName] = counter + block := make([]testing.CoverBlock, len(counter)) + for i := range counter { + block[i] = testing.CoverBlock{ + Line0: pos[3*i+0], + Col0: uint16(pos[3*i+2]), + Line1: pos[3*i+1], + Col1: uint16(pos[3*i+2]>>16), + Stmts: numStmts[i], + } + } + coverBlocks[fileName] = block +} +{{end}} + func main() { +{{if .CoverEnabled}} + testing.RegisterCover(testing.Cover{ + Mode: {{printf "%q" .CoverMode}}, + Counters: coverCounters, + Blocks: coverBlocks, + CoveredPackages: {{printf "%q" .Covered}}, + }) +{{end}} testing.Main(matchString, tests, benchmarks, examples) } diff --git a/src/cmd/go/testdata/shadow/root1/src/foo/foo.go b/src/cmd/go/testdata/shadow/root1/src/foo/foo.go new file mode 100644 index 000000000..f52652b1b --- /dev/null +++ b/src/cmd/go/testdata/shadow/root1/src/foo/foo.go @@ -0,0 +1 @@ +package foo diff --git a/src/cmd/go/testdata/shadow/root1/src/math/math.go b/src/cmd/go/testdata/shadow/root1/src/math/math.go new file mode 100644 index 000000000..c91c24e96 --- /dev/null +++ b/src/cmd/go/testdata/shadow/root1/src/math/math.go @@ -0,0 +1 @@ +package math diff --git a/src/cmd/go/testdata/shadow/root2/src/foo/foo.go b/src/cmd/go/testdata/shadow/root2/src/foo/foo.go new file mode 100644 index 000000000..f52652b1b --- /dev/null +++ b/src/cmd/go/testdata/shadow/root2/src/foo/foo.go @@ -0,0 +1 @@ +package foo diff --git a/src/cmd/go/testdata/src/badpkg/x.go b/src/cmd/go/testdata/src/badpkg/x.go new file mode 100644 index 000000000..dda35e8ed --- /dev/null +++ b/src/cmd/go/testdata/src/badpkg/x.go @@ -0,0 +1 @@ +pkg badpkg diff --git a/src/cmd/go/testdata/src/cgotest/m.go b/src/cmd/go/testdata/src/cgotest/m.go new file mode 100644 index 000000000..4d68307cf --- /dev/null +++ b/src/cmd/go/testdata/src/cgotest/m.go @@ -0,0 +1,5 @@ +package cgotest + +import "C" + +var _ C.int diff --git a/src/cmd/go/testdata/src/main_test/m.go b/src/cmd/go/testdata/src/main_test/m.go new file mode 100644 index 000000000..c682f030b --- /dev/null +++ b/src/cmd/go/testdata/src/main_test/m.go @@ -0,0 +1,4 @@ +package main + +func F() {} +func main() {} diff --git a/src/cmd/go/testdata/src/main_test/m_test.go b/src/cmd/go/testdata/src/main_test/m_test.go new file mode 100644 index 000000000..f865b7734 --- /dev/null +++ b/src/cmd/go/testdata/src/main_test/m_test.go @@ -0,0 +1,10 @@ +package main_test + +import ( + . "main_test" + "testing" +) + +func Test1(t *testing.T) { + F() +} diff --git a/src/cmd/go/testdata/src/syntaxerror/x.go b/src/cmd/go/testdata/src/syntaxerror/x.go new file mode 100644 index 000000000..c89cd18d0 --- /dev/null +++ b/src/cmd/go/testdata/src/syntaxerror/x.go @@ -0,0 +1 @@ +package p diff --git a/src/cmd/go/testdata/src/syntaxerror/x_test.go b/src/cmd/go/testdata/src/syntaxerror/x_test.go new file mode 100644 index 000000000..2460743e5 --- /dev/null +++ b/src/cmd/go/testdata/src/syntaxerror/x_test.go @@ -0,0 +1,4 @@ +package p + +func f() (x.y, z int) { +} diff --git a/src/cmd/go/testflag.go b/src/cmd/go/testflag.go index b2ca66b09..aea81d8f8 100644 --- a/src/cmd/go/testflag.go +++ b/src/cmd/go/testflag.go @@ -27,12 +27,17 @@ var usageMessage = `Usage of go test: -bench="": passes -test.bench to test -benchmem=false: print memory allocation statistics for benchmarks -benchtime=1s: passes -test.benchtime to test + -cover=false: enable coverage analysis + -covermode="set": specifies mode for coverage analysis + -coverpkg="": comma-separated list of packages for coverage analysis + -coverprofile="": passes -test.coverprofile to test if -cover -cpu="": passes -test.cpu to test -cpuprofile="": passes -test.cpuprofile to test -memprofile="": passes -test.memprofile to test -memprofilerate=0: passes -test.memprofilerate to test -blockprofile="": pases -test.blockprofile to test -blockprofilerate=0: passes -test.blockprofilerate to test + -outputdir=$PWD: passes -test.outputdir to test -parallel=0: passes -test.parallel to test -run="": passes -test.run to test -short=false: passes -test.short to test @@ -62,6 +67,8 @@ var testFlagDefn = []*testFlagSpec{ {name: "c", boolVar: &testC}, {name: "file", multiOK: true}, {name: "i", boolVar: &testI}, + {name: "cover", boolVar: &testCover}, + {name: "coverpkg"}, // build flags. {name: "a", boolVar: &buildA}, @@ -75,17 +82,21 @@ var testFlagDefn = []*testFlagSpec{ {name: "tags"}, {name: "compiler"}, {name: "race", boolVar: &buildRace}, + {name: "installsuffix"}, // passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v. {name: "bench", passToTest: true}, {name: "benchmem", boolVar: new(bool), passToTest: true}, {name: "benchtime", passToTest: true}, + {name: "covermode"}, + {name: "coverprofile", passToTest: true}, {name: "cpu", passToTest: true}, {name: "cpuprofile", passToTest: true}, {name: "memprofile", passToTest: true}, {name: "memprofilerate", passToTest: true}, {name: "blockprofile", passToTest: true}, {name: "blockprofilerate", passToTest: true}, + {name: "outputdir", passToTest: true}, {name: "parallel", passToTest: true}, {name: "run", passToTest: true}, {name: "short", boolVar: new(bool), passToTest: true}, @@ -104,6 +115,8 @@ var testFlagDefn = []*testFlagSpec{ // go test -x math func testFlags(args []string) (packageNames, passToTest []string) { inPkg := false + outputDir := "" + testCoverMode = "set" for i := 0; i < len(args); i++ { if !strings.HasPrefix(args[i], "-") { if !inPkg && packageNames == nil { @@ -137,7 +150,7 @@ func testFlags(args []string) (packageNames, passToTest []string) { var err error switch f.name { // bool flags. - case "a", "c", "i", "n", "x", "v", "work", "race": + case "a", "c", "i", "n", "x", "v", "race", "cover", "work": setBoolFlag(f.boolVar, value) case "p": setIntFlag(&buildP, value) @@ -169,6 +182,27 @@ func testFlags(args []string) (packageNames, passToTest []string) { testTimeout = value case "blockprofile", "cpuprofile", "memprofile": testProfile = true + testNeedBinary = true + case "coverpkg": + testCover = true + if value == "" { + testCoverPaths = nil + } else { + testCoverPaths = strings.Split(value, ",") + } + case "coverprofile": + testCover = true + testProfile = true + case "covermode": + switch value { + case "set", "count", "atomic": + testCoverMode = value + default: + fatalf("invalid flag argument for -cover: %q", value) + } + testCover = true + case "outputdir": + outputDir = value } if extraWord { i++ @@ -177,6 +211,15 @@ func testFlags(args []string) (packageNames, passToTest []string) { passToTest = append(passToTest, "-test."+f.name+"="+value) } } + + // Tell the test what directory we're running in, so it can write the profiles there. + if testProfile && outputDir == "" { + dir, err := os.Getwd() + if err != nil { + fatalf("error from os.Getwd: %s", err) + } + passToTest = append(passToTest, "-test.outputdir", dir) + } return } @@ -215,13 +258,13 @@ func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool) extra = equals < 0 if extra { if i+1 >= len(args) { - usage() + testSyntaxError("missing argument for flag " + f.name) } value = args[i+1] } } if f.present && !f.multiOK { - usage() + testSyntaxError(f.name + " flag may be set only once") } f.present = true return @@ -235,8 +278,7 @@ func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool) func setBoolFlag(flag *bool, value string) { x, err := strconv.ParseBool(value) if err != nil { - fmt.Fprintf(os.Stderr, "go test: illegal bool flag value %s\n", value) - usage() + testSyntaxError("illegal bool flag value " + value) } *flag = x } @@ -245,8 +287,13 @@ func setBoolFlag(flag *bool, value string) { func setIntFlag(flag *int, value string) { x, err := strconv.Atoi(value) if err != nil { - fmt.Fprintf(os.Stderr, "go test: illegal int flag value %s\n", value) - usage() + testSyntaxError("illegal int flag value " + value) } *flag = x } + +func testSyntaxError(msg string) { + fmt.Fprintf(os.Stderr, "go test: %s\n", msg) + fmt.Fprintf(os.Stderr, `run "go help test" or "go help testflag" for more information`+"\n") + os.Exit(2) +} diff --git a/src/cmd/go/tool.go b/src/cmd/go/tool.go index 299b94cb3..6d26f7a4b 100644 --- a/src/cmd/go/tool.go +++ b/src/cmd/go/tool.go @@ -45,12 +45,30 @@ func init() { const toolWindowsExtension = ".exe" -func tool(name string) string { - p := filepath.Join(toolDir, name) - if toolIsWindows && name != "pprof" { - p += toolWindowsExtension +func tool(toolName string) string { + toolPath := filepath.Join(toolDir, toolName) + if toolIsWindows && toolName != "pprof" { + toolPath += toolWindowsExtension } - return p + // Give a nice message if there is no tool with that name. + if _, err := os.Stat(toolPath); err != nil { + if isInGoToolsRepo(toolName) { + fmt.Fprintf(os.Stderr, "go tool: no such tool %q; to install:\n\tgo get code.google.com/p/go.tools/cmd/%s\n", toolName, toolName) + } else { + fmt.Fprintf(os.Stderr, "go tool: no such tool %q\n", toolName) + } + setExitStatus(3) + exit() + } + return toolPath +} + +func isInGoToolsRepo(toolName string) bool { + switch toolName { + case "cover", "vet": + return true + } + return false } func runTool(cmd *Command, args []string) { @@ -70,10 +88,7 @@ func runTool(cmd *Command, args []string) { } } toolPath := tool(toolName) - // Give a nice message if there is no tool with that name. - if _, err := os.Stat(toolPath); err != nil { - fmt.Fprintf(os.Stderr, "go tool: no such tool %q\n", toolName) - setExitStatus(3) + if toolPath == "" { return } if toolIsWindows && toolName == "pprof" { @@ -86,7 +101,6 @@ func runTool(cmd *Command, args []string) { return } } - if toolN { fmt.Printf("%s %s\n", toolPath, strings.Join(args[1:], " ")) return diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go index 39881a6dc..22d5ebc24 100644 --- a/src/cmd/go/vcs.go +++ b/src/cmd/go/vcs.go @@ -91,7 +91,7 @@ var vcsGit = &vcsCmd{ cmd: "git", createCmd: "clone {repo} {dir}", - downloadCmd: "fetch", + downloadCmd: "pull --ff-only", tagCmd: []tagCmd{ // tags/xxx matches a git tag named xxx @@ -102,7 +102,7 @@ var vcsGit = &vcsCmd{ {"show-ref tags/{tag} origin/{tag}", `((?:tags|origin)/\S+)$`}, }, tagSyncCmd: "checkout {tag}", - tagSyncDefault: "checkout origin/master", + tagSyncDefault: "checkout master", scheme: []string{"git", "https", "http", "git+ssh"}, pingCmd: "ls-remote {scheme}://{repo}", @@ -223,9 +223,36 @@ func (v *vcsCmd) create(dir, repo string) error { // download downloads any new changes for the repo in dir. func (v *vcsCmd) download(dir string) error { + if err := v.fixDetachedHead(dir); err != nil { + return err + } return v.run(dir, v.downloadCmd) } +// fixDetachedHead switches a Git repository in dir from a detached head to the master branch. +// Go versions before 1.2 downloaded Git repositories in an unfortunate way +// that resulted in the working tree state being on a detached head. +// That meant the repository was not usable for normal Git operations. +// Go 1.2 fixed that, but we can't pull into a detached head, so if this is +// a Git repository we check for being on a detached head and switch to the +// real branch, almost always called "master". +// TODO(dsymonds): Consider removing this for Go 1.3. +func (v *vcsCmd) fixDetachedHead(dir string) error { + if v != vcsGit { + return nil + } + + // "git symbolic-ref HEAD" succeeds iff we are not on a detached head. + if err := v.runVerboseOnly(dir, "symbolic-ref HEAD"); err == nil { + // not on a detached head + return nil + } + if buildV { + log.Printf("%s on detached head; repairing", dir) + } + return v.run(dir, "checkout master") +} + // tags returns the list of available tags for the repo in dir. func (v *vcsCmd) tags(dir string) ([]string, error) { var tags []string @@ -294,6 +321,7 @@ func vcsForDir(p *Package) (vcs *vcsCmd, root string, err error) { return nil, "", fmt.Errorf("directory %q is outside source root %q", dir, srcRoot) } + origDir := dir for len(dir) > len(srcRoot) { for _, vcs := range vcsList { if fi, err := os.Stat(filepath.Join(dir, "."+vcs.cmd)); err == nil && fi.IsDir() { @@ -310,7 +338,7 @@ func vcsForDir(p *Package) (vcs *vcsCmd, root string, err error) { dir = ndir } - return nil, "", fmt.Errorf("directory %q is not using a known version control system", dir) + return nil, "", fmt.Errorf("directory %q is not using a known version control system", origDir) } // repoRoot represents a version control system, a repo, and a root of @@ -442,7 +470,11 @@ func repoRootForImportDynamic(importPath string) (*repoRoot, error) { return nil, fmt.Errorf("http/https fetch: %v", err) } defer body.Close() - metaImport, err := matchGoImport(parseMetaGoImports(body), importPath) + imports, err := parseMetaGoImports(body) + if err != nil { + return nil, fmt.Errorf("parsing %s: %v", importPath, err) + } + metaImport, err := matchGoImport(imports, importPath) if err != nil { if err != errNoMatch { return nil, fmt.Errorf("parse %s: %v", urlStr, err) @@ -467,7 +499,10 @@ func repoRootForImportDynamic(importPath string) (*repoRoot, error) { if err != nil { return nil, fmt.Errorf("fetch %s: %v", urlStr, err) } - imports := parseMetaGoImports(body) + imports, err := parseMetaGoImports(body) + if err != nil { + return nil, fmt.Errorf("parsing %s: %v", importPath, err) + } if len(imports) == 0 { return nil, fmt.Errorf("fetch %s: no go-import meta tag", urlStr) } diff --git a/src/cmd/go/vet.go b/src/cmd/go/vet.go index 503e16362..ffb431837 100644 --- a/src/cmd/go/vet.go +++ b/src/cmd/go/vet.go @@ -15,7 +15,7 @@ var cmdVet = &Command{ Long: ` Vet runs the Go vet command on the packages named by the import paths. -For more about vet, see 'godoc vet'. +For more about vet, see 'godoc code.google.com/p/go.tools/cmd/vet'. For more about specifying packages, see 'go help packages'. To run the vet tool with specific options, run 'go tool vet'. diff --git a/src/cmd/godoc/README.godoc-app b/src/cmd/godoc/README.godoc-app deleted file mode 100644 index cff7d387c..000000000 --- a/src/cmd/godoc/README.godoc-app +++ /dev/null @@ -1,61 +0,0 @@ -Copyright 2011 The Go Authors. All rights reserved. -Use of this source code is governed by a BSD-style -license that can be found in the LICENSE file. - -godoc on appengine ------------------- - -Prerequisites -------------- - -* Go appengine SDK - https://developers.google.com/appengine/downloads#Google_App_Engine_SDK_for_Go - -* Go sources at tip under $GOROOT - - -Directory structure -------------------- - -* Let $APPDIR be the directory containing the app engine files. - (e.g., $APPDIR=$HOME/godoc-app) - -* $APPDIR contains the following entries (this may change depending on - app-engine release and version of godoc): - - app.yaml - godoc.zip - godoc/ - index.split.* - -* The app.yaml file is set up per app engine documentation. - For instance: - - application: godoc-app - version: 1 - runtime: go - api_version: go1 - - handlers: - - url: /.* - script: _go_app - -* The godoc/ directory contains a copy of the files under $GOROOT/src/cmd/godoc - with doc.go excluded (it belongs to pseudo-package "documentation") - - -Configuring and running godoc ------------------------------ - -To configure godoc, run - - bash setup-godoc-app.bash - -to create the godoc.zip, index.split.*, and godoc/appconfig.go files -based on $GOROOT and $APPDIR. See the script for details on usage. - -To run godoc locally, using the app-engine emulator, run - - <path to google_appengine>/dev_appserver.py $APPDIR - -godoc should come up at http://localhost:8080 . diff --git a/src/cmd/godoc/appinit.go b/src/cmd/godoc/appinit.go deleted file mode 100644 index 996b2b850..000000000 --- a/src/cmd/godoc/appinit.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build appengine - -package main - -// This file replaces main.go when running godoc under app-engine. -// See README.godoc-app for details. - -import ( - "archive/zip" - "log" - "net/http" - "path" -) - -func serveError(w http.ResponseWriter, r *http.Request, relpath string, err error) { - w.WriteHeader(http.StatusNotFound) - servePage(w, Page{ - Title: "File " + relpath, - Subtitle: relpath, - Body: applyTemplate(errorHTML, "errorHTML", err), // err may contain an absolute path! - }) -} - -func init() { - log.Println("initializing godoc ...") - log.Printf(".zip file = %s", zipFilename) - log.Printf(".zip GOROOT = %s", zipGoroot) - log.Printf("index files = %s", indexFilenames) - - // initialize flags for app engine - *goroot = path.Join("/", zipGoroot) // fsHttp paths are relative to '/' - *indexEnabled = true - *indexFiles = indexFilenames - *maxResults = 100 // reduce latency by limiting the number of fulltext search results - *indexThrottle = 0.3 // in case *indexFiles is empty (and thus the indexer is run) - *showPlayground = true - - // read .zip file and set up file systems - const zipfile = zipFilename - rc, err := zip.OpenReader(zipfile) - if err != nil { - log.Fatalf("%s: %s\n", zipfile, err) - } - // rc is never closed (app running forever) - fs.Bind("/", NewZipFS(rc, zipFilename), *goroot, bindReplace) - - // initialize http handlers - readTemplates() - initHandlers() - registerPublicHandlers(http.DefaultServeMux) - registerPlaygroundHandlers(http.DefaultServeMux) - - // initialize default directory tree with corresponding timestamp. - initFSTree() - - // Immediately update metadata. - updateMetadata() - - // initialize search index - if *indexEnabled { - go indexer() - } - - log.Println("godoc initialization complete") -} diff --git a/src/cmd/godoc/codewalk.go b/src/cmd/godoc/codewalk.go deleted file mode 100644 index e68c0fa6b..000000000 --- a/src/cmd/godoc/codewalk.go +++ /dev/null @@ -1,494 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// The /doc/codewalk/ tree is synthesized from codewalk descriptions, -// files named $GOROOT/doc/codewalk/*.xml. -// For an example and a description of the format, see -// http://golang.org/doc/codewalk/codewalk or run godoc -http=:6060 -// and see http://localhost:6060/doc/codewalk/codewalk . -// That page is itself a codewalk; the source code for it is -// $GOROOT/doc/codewalk/codewalk.xml. - -package main - -import ( - "encoding/xml" - "errors" - "fmt" - "io" - "log" - "net/http" - "os" - "regexp" - "sort" - "strconv" - "strings" - "text/template" - "unicode/utf8" -) - -// Handler for /doc/codewalk/ and below. -func codewalk(w http.ResponseWriter, r *http.Request) { - relpath := r.URL.Path[len("/doc/codewalk/"):] - abspath := r.URL.Path - - r.ParseForm() - if f := r.FormValue("fileprint"); f != "" { - codewalkFileprint(w, r, f) - return - } - - // If directory exists, serve list of code walks. - dir, err := fs.Lstat(abspath) - if err == nil && dir.IsDir() { - codewalkDir(w, r, relpath, abspath) - return - } - - // If file exists, serve using standard file server. - if err == nil { - serveFile(w, r) - return - } - - // Otherwise append .xml and hope to find - // a codewalk description, but before trim - // the trailing /. - abspath = strings.TrimRight(abspath, "/") - cw, err := loadCodewalk(abspath + ".xml") - if err != nil { - log.Print(err) - serveError(w, r, relpath, err) - return - } - - // Canonicalize the path and redirect if changed - if redirect(w, r) { - return - } - - servePage(w, Page{ - Title: "Codewalk: " + cw.Title, - Tabtitle: cw.Title, - Body: applyTemplate(codewalkHTML, "codewalk", cw), - }) -} - -// A Codewalk represents a single codewalk read from an XML file. -type Codewalk struct { - Title string `xml:"title,attr"` - File []string `xml:"file"` - Step []*Codestep `xml:"step"` -} - -// A Codestep is a single step in a codewalk. -type Codestep struct { - // Filled in from XML - Src string `xml:"src,attr"` - Title string `xml:"title,attr"` - XML string `xml:",innerxml"` - - // Derived from Src; not in XML. - Err error - File string - Lo int - LoByte int - Hi int - HiByte int - Data []byte -} - -// String method for printing in template. -// Formats file address nicely. -func (st *Codestep) String() string { - s := st.File - if st.Lo != 0 || st.Hi != 0 { - s += fmt.Sprintf(":%d", st.Lo) - if st.Lo != st.Hi { - s += fmt.Sprintf(",%d", st.Hi) - } - } - return s -} - -// loadCodewalk reads a codewalk from the named XML file. -func loadCodewalk(filename string) (*Codewalk, error) { - f, err := fs.Open(filename) - if err != nil { - return nil, err - } - defer f.Close() - cw := new(Codewalk) - d := xml.NewDecoder(f) - d.Entity = xml.HTMLEntity - err = d.Decode(cw) - if err != nil { - return nil, &os.PathError{Op: "parsing", Path: filename, Err: err} - } - - // Compute file list, evaluate line numbers for addresses. - m := make(map[string]bool) - for _, st := range cw.Step { - i := strings.Index(st.Src, ":") - if i < 0 { - i = len(st.Src) - } - filename := st.Src[0:i] - data, err := ReadFile(fs, filename) - if err != nil { - st.Err = err - continue - } - if i < len(st.Src) { - lo, hi, err := addrToByteRange(st.Src[i+1:], 0, data) - if err != nil { - st.Err = err - continue - } - // Expand match to line boundaries. - for lo > 0 && data[lo-1] != '\n' { - lo-- - } - for hi < len(data) && (hi == 0 || data[hi-1] != '\n') { - hi++ - } - st.Lo = byteToLine(data, lo) - st.Hi = byteToLine(data, hi-1) - } - st.Data = data - st.File = filename - m[filename] = true - } - - // Make list of files - cw.File = make([]string, len(m)) - i := 0 - for f := range m { - cw.File[i] = f - i++ - } - sort.Strings(cw.File) - - return cw, nil -} - -// codewalkDir serves the codewalk directory listing. -// It scans the directory for subdirectories or files named *.xml -// and prepares a table. -func codewalkDir(w http.ResponseWriter, r *http.Request, relpath, abspath string) { - type elem struct { - Name string - Title string - } - - dir, err := fs.ReadDir(abspath) - if err != nil { - log.Print(err) - serveError(w, r, relpath, err) - return - } - var v []interface{} - for _, fi := range dir { - name := fi.Name() - if fi.IsDir() { - v = append(v, &elem{name + "/", ""}) - } else if strings.HasSuffix(name, ".xml") { - cw, err := loadCodewalk(abspath + "/" + name) - if err != nil { - continue - } - v = append(v, &elem{name[0 : len(name)-len(".xml")], cw.Title}) - } - } - - servePage(w, Page{ - Title: "Codewalks", - Body: applyTemplate(codewalkdirHTML, "codewalkdir", v), - }) -} - -// codewalkFileprint serves requests with ?fileprint=f&lo=lo&hi=hi. -// The filename f has already been retrieved and is passed as an argument. -// Lo and hi are the numbers of the first and last line to highlight -// in the response. This format is used for the middle window pane -// of the codewalk pages. It is a separate iframe and does not get -// the usual godoc HTML wrapper. -func codewalkFileprint(w http.ResponseWriter, r *http.Request, f string) { - abspath := f - data, err := ReadFile(fs, abspath) - if err != nil { - log.Print(err) - serveError(w, r, f, err) - return - } - lo, _ := strconv.Atoi(r.FormValue("lo")) - hi, _ := strconv.Atoi(r.FormValue("hi")) - if hi < lo { - hi = lo - } - lo = lineToByte(data, lo) - hi = lineToByte(data, hi+1) - - // Put the mark 4 lines before lo, so that the iframe - // shows a few lines of context before the highlighted - // section. - n := 4 - mark := lo - for ; mark > 0 && n > 0; mark-- { - if data[mark-1] == '\n' { - if n--; n == 0 { - break - } - } - } - - io.WriteString(w, `<style type="text/css">@import "/doc/codewalk/codewalk.css";</style><pre>`) - template.HTMLEscape(w, data[0:mark]) - io.WriteString(w, "<a name='mark'></a>") - template.HTMLEscape(w, data[mark:lo]) - if lo < hi { - io.WriteString(w, "<div class='codewalkhighlight'>") - template.HTMLEscape(w, data[lo:hi]) - io.WriteString(w, "</div>") - } - template.HTMLEscape(w, data[hi:]) - io.WriteString(w, "</pre>") -} - -// addrToByte evaluates the given address starting at offset start in data. -// It returns the lo and hi byte offset of the matched region within data. -// See http://plan9.bell-labs.com/sys/doc/sam/sam.html Table II -// for details on the syntax. -func addrToByteRange(addr string, start int, data []byte) (lo, hi int, err error) { - var ( - dir byte - prevc byte - charOffset bool - ) - lo = start - hi = start - for addr != "" && err == nil { - c := addr[0] - switch c { - default: - err = errors.New("invalid address syntax near " + string(c)) - case ',': - if len(addr) == 1 { - hi = len(data) - } else { - _, hi, err = addrToByteRange(addr[1:], hi, data) - } - return - - case '+', '-': - if prevc == '+' || prevc == '-' { - lo, hi, err = addrNumber(data, lo, hi, prevc, 1, charOffset) - } - dir = c - - case '$': - lo = len(data) - hi = len(data) - if len(addr) > 1 { - dir = '+' - } - - case '#': - charOffset = true - - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - var i int - for i = 1; i < len(addr); i++ { - if addr[i] < '0' || addr[i] > '9' { - break - } - } - var n int - n, err = strconv.Atoi(addr[0:i]) - if err != nil { - break - } - lo, hi, err = addrNumber(data, lo, hi, dir, n, charOffset) - dir = 0 - charOffset = false - prevc = c - addr = addr[i:] - continue - - case '/': - var i, j int - Regexp: - for i = 1; i < len(addr); i++ { - switch addr[i] { - case '\\': - i++ - case '/': - j = i + 1 - break Regexp - } - } - if j == 0 { - j = i - } - pattern := addr[1:i] - lo, hi, err = addrRegexp(data, lo, hi, dir, pattern) - prevc = c - addr = addr[j:] - continue - } - prevc = c - addr = addr[1:] - } - - if err == nil && dir != 0 { - lo, hi, err = addrNumber(data, lo, hi, dir, 1, charOffset) - } - if err != nil { - return 0, 0, err - } - return lo, hi, nil -} - -// addrNumber applies the given dir, n, and charOffset to the address lo, hi. -// dir is '+' or '-', n is the count, and charOffset is true if the syntax -// used was #n. Applying +n (or +#n) means to advance n lines -// (or characters) after hi. Applying -n (or -#n) means to back up n lines -// (or characters) before lo. -// The return value is the new lo, hi. -func addrNumber(data []byte, lo, hi int, dir byte, n int, charOffset bool) (int, int, error) { - switch dir { - case 0: - lo = 0 - hi = 0 - fallthrough - - case '+': - if charOffset { - pos := hi - for ; n > 0 && pos < len(data); n-- { - _, size := utf8.DecodeRune(data[pos:]) - pos += size - } - if n == 0 { - return pos, pos, nil - } - break - } - // find next beginning of line - if hi > 0 { - for hi < len(data) && data[hi-1] != '\n' { - hi++ - } - } - lo = hi - if n == 0 { - return lo, hi, nil - } - for ; hi < len(data); hi++ { - if data[hi] != '\n' { - continue - } - switch n--; n { - case 1: - lo = hi + 1 - case 0: - return lo, hi + 1, nil - } - } - - case '-': - if charOffset { - // Scan backward for bytes that are not UTF-8 continuation bytes. - pos := lo - for ; pos > 0 && n > 0; pos-- { - if data[pos]&0xc0 != 0x80 { - n-- - } - } - if n == 0 { - return pos, pos, nil - } - break - } - // find earlier beginning of line - for lo > 0 && data[lo-1] != '\n' { - lo-- - } - hi = lo - if n == 0 { - return lo, hi, nil - } - for ; lo >= 0; lo-- { - if lo > 0 && data[lo-1] != '\n' { - continue - } - switch n--; n { - case 1: - hi = lo - case 0: - return lo, hi, nil - } - } - } - - return 0, 0, errors.New("address out of range") -} - -// addrRegexp searches for pattern in the given direction starting at lo, hi. -// The direction dir is '+' (search forward from hi) or '-' (search backward from lo). -// Backward searches are unimplemented. -func addrRegexp(data []byte, lo, hi int, dir byte, pattern string) (int, int, error) { - re, err := regexp.Compile(pattern) - if err != nil { - return 0, 0, err - } - if dir == '-' { - // Could implement reverse search using binary search - // through file, but that seems like overkill. - return 0, 0, errors.New("reverse search not implemented") - } - m := re.FindIndex(data[hi:]) - if len(m) > 0 { - m[0] += hi - m[1] += hi - } else if hi > 0 { - // No match. Wrap to beginning of data. - m = re.FindIndex(data) - } - if len(m) == 0 { - return 0, 0, errors.New("no match for " + pattern) - } - return m[0], m[1], nil -} - -// lineToByte returns the byte index of the first byte of line n. -// Line numbers begin at 1. -func lineToByte(data []byte, n int) int { - if n <= 1 { - return 0 - } - n-- - for i, c := range data { - if c == '\n' { - if n--; n == 0 { - return i + 1 - } - } - } - return len(data) -} - -// byteToLine returns the number of the line containing the byte at index i. -func byteToLine(data []byte, i int) int { - l := 1 - for j, c := range data { - if j == i { - return l - } - if c == '\n' { - l++ - } - } - return l -} diff --git a/src/cmd/godoc/dirtrees.go b/src/cmd/godoc/dirtrees.go deleted file mode 100644 index fda7adce5..000000000 --- a/src/cmd/godoc/dirtrees.go +++ /dev/null @@ -1,320 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains the code dealing with package directory trees. - -package main - -import ( - "bytes" - "go/doc" - "go/parser" - "go/token" - "log" - "os" - pathpkg "path" - "strings" -) - -// Conventional name for directories containing test data. -// Excluded from directory trees. -// -const testdataDirName = "testdata" - -type Directory struct { - Depth int - Path string // directory path; includes Name - Name string // directory name - HasPkg bool // true if the directory contains at least one package - Synopsis string // package documentation, if any - Dirs []*Directory // subdirectories -} - -func isGoFile(fi os.FileInfo) bool { - name := fi.Name() - return !fi.IsDir() && - len(name) > 0 && name[0] != '.' && // ignore .files - pathpkg.Ext(name) == ".go" -} - -func isPkgFile(fi os.FileInfo) bool { - return isGoFile(fi) && - !strings.HasSuffix(fi.Name(), "_test.go") // ignore test files -} - -func isPkgDir(fi os.FileInfo) bool { - name := fi.Name() - return fi.IsDir() && len(name) > 0 && - name[0] != '_' && name[0] != '.' // ignore _files and .files -} - -type treeBuilder struct { - maxDepth int -} - -func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth int) *Directory { - if name == testdataDirName { - return nil - } - - if depth >= b.maxDepth { - // return a dummy directory so that the parent directory - // doesn't get discarded just because we reached the max - // directory depth - return &Directory{ - Depth: depth, - Path: path, - Name: name, - } - } - - list, _ := fs.ReadDir(path) - - // determine number of subdirectories and if there are package files - ndirs := 0 - hasPkgFiles := false - var synopses [3]string // prioritized package documentation (0 == highest priority) - for _, d := range list { - switch { - case isPkgDir(d): - ndirs++ - case isPkgFile(d): - // looks like a package file, but may just be a file ending in ".go"; - // don't just count it yet (otherwise we may end up with hasPkgFiles even - // though the directory doesn't contain any real package files - was bug) - if synopses[0] == "" { - // no "optimal" package synopsis yet; continue to collect synopses - file, err := parseFile(fset, pathpkg.Join(path, d.Name()), - parser.ParseComments|parser.PackageClauseOnly) - if err == nil { - hasPkgFiles = true - if file.Doc != nil { - // prioritize documentation - i := -1 - switch file.Name.Name { - case name: - i = 0 // normal case: directory name matches package name - case "main": - i = 1 // directory contains a main package - default: - i = 2 // none of the above - } - if 0 <= i && i < len(synopses) && synopses[i] == "" { - synopses[i] = doc.Synopsis(file.Doc.Text()) - } - } - } - } - } - } - - // create subdirectory tree - var dirs []*Directory - if ndirs > 0 { - dirs = make([]*Directory, ndirs) - i := 0 - for _, d := range list { - if isPkgDir(d) { - name := d.Name() - dd := b.newDirTree(fset, pathpkg.Join(path, name), name, depth+1) - if dd != nil { - dirs[i] = dd - i++ - } - } - } - dirs = dirs[0:i] - } - - // if there are no package files and no subdirectories - // containing package files, ignore the directory - if !hasPkgFiles && len(dirs) == 0 { - return nil - } - - // select the highest-priority synopsis for the directory entry, if any - synopsis := "" - for _, synopsis = range synopses { - if synopsis != "" { - break - } - } - - return &Directory{ - Depth: depth, - Path: path, - Name: name, - HasPkg: hasPkgFiles, - Synopsis: synopsis, - Dirs: dirs, - } -} - -// newDirectory creates a new package directory tree with at most maxDepth -// levels, anchored at root. The result tree is pruned such that it only -// contains directories that contain package files or that contain -// subdirectories containing package files (transitively). If a non-nil -// pathFilter is provided, directory paths additionally must be accepted -// by the filter (i.e., pathFilter(path) must be true). If a value >= 0 is -// provided for maxDepth, nodes at larger depths are pruned as well; they -// are assumed to contain package files even if their contents are not known -// (i.e., in this case the tree may contain directories w/o any package files). -// -func newDirectory(root string, maxDepth int) *Directory { - // The root could be a symbolic link so use Stat not Lstat. - d, err := fs.Stat(root) - // If we fail here, report detailed error messages; otherwise - // is is hard to see why a directory tree was not built. - switch { - case err != nil: - log.Printf("newDirectory(%s): %s", root, err) - return nil - case !isPkgDir(d): - log.Printf("newDirectory(%s): not a package directory", root) - return nil - } - if maxDepth < 0 { - maxDepth = 1e6 // "infinity" - } - b := treeBuilder{maxDepth} - // the file set provided is only for local parsing, no position - // information escapes and thus we don't need to save the set - return b.newDirTree(token.NewFileSet(), root, d.Name(), 0) -} - -func (dir *Directory) writeLeafs(buf *bytes.Buffer) { - if dir != nil { - if len(dir.Dirs) == 0 { - buf.WriteString(dir.Path) - buf.WriteByte('\n') - return - } - - for _, d := range dir.Dirs { - d.writeLeafs(buf) - } - } -} - -func (dir *Directory) walk(c chan<- *Directory, skipRoot bool) { - if dir != nil { - if !skipRoot { - c <- dir - } - for _, d := range dir.Dirs { - d.walk(c, false) - } - } -} - -func (dir *Directory) iter(skipRoot bool) <-chan *Directory { - c := make(chan *Directory) - go func() { - dir.walk(c, skipRoot) - close(c) - }() - return c -} - -func (dir *Directory) lookupLocal(name string) *Directory { - for _, d := range dir.Dirs { - if d.Name == name { - return d - } - } - return nil -} - -func splitPath(p string) []string { - p = strings.TrimPrefix(p, "/") - if p == "" { - return nil - } - return strings.Split(p, "/") -} - -// lookup looks for the *Directory for a given path, relative to dir. -func (dir *Directory) lookup(path string) *Directory { - d := splitPath(dir.Path) - p := splitPath(path) - i := 0 - for i < len(d) { - if i >= len(p) || d[i] != p[i] { - return nil - } - i++ - } - for dir != nil && i < len(p) { - dir = dir.lookupLocal(p[i]) - i++ - } - return dir -} - -// DirEntry describes a directory entry. The Depth and Height values -// are useful for presenting an entry in an indented fashion. -// -type DirEntry struct { - Depth int // >= 0 - Height int // = DirList.MaxHeight - Depth, > 0 - Path string // directory path; includes Name, relative to DirList root - Name string // directory name - HasPkg bool // true if the directory contains at least one package - Synopsis string // package documentation, if any -} - -type DirList struct { - MaxHeight int // directory tree height, > 0 - List []DirEntry -} - -// listing creates a (linear) directory listing from a directory tree. -// If skipRoot is set, the root directory itself is excluded from the list. -// -func (root *Directory) listing(skipRoot bool) *DirList { - if root == nil { - return nil - } - - // determine number of entries n and maximum height - n := 0 - minDepth := 1 << 30 // infinity - maxDepth := 0 - for d := range root.iter(skipRoot) { - n++ - if minDepth > d.Depth { - minDepth = d.Depth - } - if maxDepth < d.Depth { - maxDepth = d.Depth - } - } - maxHeight := maxDepth - minDepth + 1 - - if n == 0 { - return nil - } - - // create list - list := make([]DirEntry, n) - i := 0 - for d := range root.iter(skipRoot) { - p := &list[i] - p.Depth = d.Depth - minDepth - p.Height = maxHeight - p.Depth - // the path is relative to root.Path - remove the root.Path - // prefix (the prefix should always be present but avoid - // crashes and check) - path := strings.TrimPrefix(d.Path, root.Path) - // remove leading separator if any - path must be relative - path = strings.TrimPrefix(path, "/") - p.Path = path - p.Name = d.Name - p.HasPkg = d.HasPkg - p.Synopsis = d.Synopsis - i++ - } - - return &DirList{maxHeight, list} -} diff --git a/src/cmd/godoc/doc.go b/src/cmd/godoc/doc.go deleted file mode 100644 index 1fa57a8b3..000000000 --- a/src/cmd/godoc/doc.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* - -Godoc extracts and generates documentation for Go programs. - -It has two modes. - -Without the -http flag, it runs in command-line mode and prints plain text -documentation to standard output and exits. If both a library package and -a command with the same name exists, using the prefix cmd/ will force -documentation on the command rather than the library package. If the -src -flag is specified, godoc prints the exported interface of a package in Go -source form, or the implementation of a specific exported language entity: - - godoc fmt # documentation for package fmt - godoc fmt Printf # documentation for fmt.Printf - godoc cmd/go # force documentation for the go command - godoc -src fmt # fmt package interface in Go source form - godoc -src fmt Printf # implementation of fmt.Printf - -In command-line mode, the -q flag enables search queries against a godoc running -as a webserver. If no explicit server address is specified with the -server flag, -godoc first tries localhost:6060 and then http://golang.org. - - godoc -q Reader - godoc -q math.Sin - godoc -server=:6060 -q sin - -With the -http flag, it runs as a web server and presents the documentation as a -web page. - - godoc -http=:6060 - -Usage: - godoc [flag] package [name ...] - -The flags are: - -v - verbose mode - -q - arguments are considered search queries: a legal query is a - single identifier (such as ToLower) or a qualified identifier - (such as math.Sin). - -src - print (exported) source in command-line mode - -tabwidth=4 - width of tabs in units of spaces - -timestamps=true - show timestamps with directory listings - -index - enable identifier and full text search index - (no search box is shown if -index is not set) - -index_files="" - glob pattern specifying index files; if not empty, - the index is read from these files in sorted order - -index_throttle=0.75 - index throttle value; a value of 0 means no time is allocated - to the indexer (the indexer will never finish), a value of 1.0 - means that index creation is running at full throttle (other - goroutines may get no time while the index is built) - -links=true: - link identifiers to their declarations - -write_index=false - write index to a file; the file name must be specified with - -index_files - -maxresults=10000 - maximum number of full text search results shown - (no full text index is built if maxresults <= 0) - -notes="BUG" - regular expression matching note markers to show - (e.g., "BUG|TODO", ".*") - -html - print HTML in command-line mode - -goroot=$GOROOT - Go root directory - -http=addr - HTTP service address (e.g., '127.0.0.1:6060' or just ':6060') - -server=addr - webserver address for command line searches - -templates="" - directory containing alternate template files; if set, - the directory may provide alternative template files - for the files in $GOROOT/lib/godoc - -url=path - print to standard output the data that would be served by - an HTTP request for path - -zip="" - zip file providing the file system to serve; disabled if empty - -By default, godoc looks at the packages it finds via $GOROOT and $GOPATH (if set). -This behavior can be altered by providing an alternative $GOROOT with the -goroot -flag. - -When godoc runs as a web server and -index is set, a search index is maintained. -The index is created at startup. - -The index contains both identifier and full text search information (searchable -via regular expressions). The maximum number of full text search results shown -can be set with the -maxresults flag; if set to 0, no full text results are -shown, and only an identifier index but no full text search index is created. - -The presentation mode of web pages served by godoc can be controlled with the -"m" URL parameter; it accepts a comma-separated list of flag names as value: - - all show documentation for all declarations, not just the exported ones - methods show all embedded methods, not just those of unexported anonymous fields - src show the original source code rather then the extracted documentation - text present the page in textual (command-line) form rather than HTML - flat present flat (not indented) directory listings using full paths - -For instance, http://golang.org/pkg/math/big/?m=all,text shows the documentation -for all (not just the exported) declarations of package big, in textual form (as -it would appear when using godoc from the command line: "godoc -src math/big .*"). - -By default, godoc serves files from the file system of the underlying OS. -Instead, a .zip file may be provided via the -zip flag, which contains -the file system to serve. The file paths stored in the .zip file must use -slash ('/') as path separator; and they must be unrooted. $GOROOT (or -goroot) -must be set to the .zip file directory path containing the Go root directory. -For instance, for a .zip file created by the command: - - zip go.zip $HOME/go - -one may run godoc as follows: - - godoc -http=:6060 -zip=go.zip -goroot=$HOME/go - -See "Godoc: documenting Go code" for how to write good comments for godoc: -http://golang.org/doc/articles/godoc_documenting_go_code.html - -*/ -package main diff --git a/src/cmd/godoc/filesystem.go b/src/cmd/godoc/filesystem.go deleted file mode 100644 index 0309d7cab..000000000 --- a/src/cmd/godoc/filesystem.go +++ /dev/null @@ -1,562 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file defines types for abstract file system access and -// provides an implementation accessing the file system of the -// underlying OS. - -package main - -import ( - "fmt" - "io" - "io/ioutil" - "net/http" - "os" - pathpkg "path" - "path/filepath" - "sort" - "strings" - "time" -) - -// fs is the file system that godoc reads from and serves. -// It is a virtual file system that operates on slash-separated paths, -// and its root corresponds to the Go distribution root: /src/pkg -// holds the source tree, and so on. This means that the URLs served by -// the godoc server are the same as the paths in the virtual file -// system, which helps keep things simple. -// -// New file trees - implementations of FileSystem - can be added to -// the virtual file system using nameSpace's Bind method. -// The usual setup is to bind OS(runtime.GOROOT) to the root -// of the name space and then bind any GOPATH/src directories -// on top of /src/pkg, so that all sources are in /src/pkg. -// -// For more about name spaces, see the nameSpace type's -// documentation below. -// -// The use of this virtual file system means that most code processing -// paths can assume they are slash-separated and should be using -// package path (often imported as pathpkg) to manipulate them, -// even on Windows. -// -var fs = nameSpace{} // the underlying file system for godoc - -// Setting debugNS = true will enable debugging prints about -// name space translations. -const debugNS = false - -// The FileSystem interface specifies the methods godoc is using -// to access the file system for which it serves documentation. -type FileSystem interface { - Open(path string) (readSeekCloser, error) - Lstat(path string) (os.FileInfo, error) - Stat(path string) (os.FileInfo, error) - ReadDir(path string) ([]os.FileInfo, error) - String() string -} - -type readSeekCloser interface { - io.Reader - io.Seeker - io.Closer -} - -// ReadFile reads the file named by path from fs and returns the contents. -func ReadFile(fs FileSystem, path string) ([]byte, error) { - rc, err := fs.Open(path) - if err != nil { - return nil, err - } - defer rc.Close() - return ioutil.ReadAll(rc) -} - -// OS returns an implementation of FileSystem reading from the -// tree rooted at root. Recording a root is convenient everywhere -// but necessary on Windows, because the slash-separated path -// passed to Open has no way to specify a drive letter. Using a root -// lets code refer to OS(`c:\`), OS(`d:\`) and so on. -func OS(root string) FileSystem { - return osFS(root) -} - -type osFS string - -func (root osFS) String() string { return "os(" + string(root) + ")" } - -func (root osFS) resolve(path string) string { - // Clean the path so that it cannot possibly begin with ../. - // If it did, the result of filepath.Join would be outside the - // tree rooted at root. We probably won't ever see a path - // with .. in it, but be safe anyway. - path = pathpkg.Clean("/" + path) - - return filepath.Join(string(root), path) -} - -func (root osFS) Open(path string) (readSeekCloser, error) { - f, err := os.Open(root.resolve(path)) - if err != nil { - return nil, err - } - fi, err := f.Stat() - if err != nil { - return nil, err - } - if fi.IsDir() { - return nil, fmt.Errorf("Open: %s is a directory", path) - } - return f, nil -} - -func (root osFS) Lstat(path string) (os.FileInfo, error) { - return os.Lstat(root.resolve(path)) -} - -func (root osFS) Stat(path string) (os.FileInfo, error) { - return os.Stat(root.resolve(path)) -} - -func (root osFS) ReadDir(path string) ([]os.FileInfo, error) { - return ioutil.ReadDir(root.resolve(path)) // is sorted -} - -// hasPathPrefix returns true if x == y or x == y + "/" + more -func hasPathPrefix(x, y string) bool { - return x == y || strings.HasPrefix(x, y) && (strings.HasSuffix(y, "/") || strings.HasPrefix(x[len(y):], "/")) -} - -// A nameSpace is a file system made up of other file systems -// mounted at specific locations in the name space. -// -// The representation is a map from mount point locations -// to the list of file systems mounted at that location. A traditional -// Unix mount table would use a single file system per mount point, -// but we want to be able to mount multiple file systems on a single -// mount point and have the system behave as if the union of those -// file systems were present at the mount point. -// For example, if the OS file system has a Go installation in -// c:\Go and additional Go path trees in d:\Work1 and d:\Work2, then -// this name space creates the view we want for the godoc server: -// -// nameSpace{ -// "/": { -// {old: "/", fs: OS(`c:\Go`), new: "/"}, -// }, -// "/src/pkg": { -// {old: "/src/pkg", fs: OS(`c:\Go`), new: "/src/pkg"}, -// {old: "/src/pkg", fs: OS(`d:\Work1`), new: "/src"}, -// {old: "/src/pkg", fs: OS(`d:\Work2`), new: "/src"}, -// }, -// } -// -// This is created by executing: -// -// ns := nameSpace{} -// ns.Bind("/", OS(`c:\Go`), "/", bindReplace) -// ns.Bind("/src/pkg", OS(`d:\Work1`), "/src", bindAfter) -// ns.Bind("/src/pkg", OS(`d:\Work2`), "/src", bindAfter) -// -// A particular mount point entry is a triple (old, fs, new), meaning that to -// operate on a path beginning with old, replace that prefix (old) with new -// and then pass that path to the FileSystem implementation fs. -// -// Given this name space, a ReadDir of /src/pkg/code will check each prefix -// of the path for a mount point (first /src/pkg/code, then /src/pkg, then /src, -// then /), stopping when it finds one. For the above example, /src/pkg/code -// will find the mount point at /src/pkg: -// -// {old: "/src/pkg", fs: OS(`c:\Go`), new: "/src/pkg"}, -// {old: "/src/pkg", fs: OS(`d:\Work1`), new: "/src"}, -// {old: "/src/pkg", fs: OS(`d:\Work2`), new: "/src"}, -// -// ReadDir will when execute these three calls and merge the results: -// -// OS(`c:\Go`).ReadDir("/src/pkg/code") -// OS(`d:\Work1').ReadDir("/src/code") -// OS(`d:\Work2').ReadDir("/src/code") -// -// Note that the "/src/pkg" in "/src/pkg/code" has been replaced by -// just "/src" in the final two calls. -// -// OS is itself an implementation of a file system: it implements -// OS(`c:\Go`).ReadDir("/src/pkg/code") as ioutil.ReadDir(`c:\Go\src\pkg\code`). -// -// Because the new path is evaluated by fs (here OS(root)), another way -// to read the mount table is to mentally combine fs+new, so that this table: -// -// {old: "/src/pkg", fs: OS(`c:\Go`), new: "/src/pkg"}, -// {old: "/src/pkg", fs: OS(`d:\Work1`), new: "/src"}, -// {old: "/src/pkg", fs: OS(`d:\Work2`), new: "/src"}, -// -// reads as: -// -// "/src/pkg" -> c:\Go\src\pkg -// "/src/pkg" -> d:\Work1\src -// "/src/pkg" -> d:\Work2\src -// -// An invariant (a redundancy) of the name space representation is that -// ns[mtpt][i].old is always equal to mtpt (in the example, ns["/src/pkg"]'s -// mount table entries always have old == "/src/pkg"). The 'old' field is -// useful to callers, because they receive just a []mountedFS and not any -// other indication of which mount point was found. -// -type nameSpace map[string][]mountedFS - -// A mountedFS handles requests for path by replacing -// a prefix 'old' with 'new' and then calling the fs methods. -type mountedFS struct { - old string - fs FileSystem - new string -} - -// translate translates path for use in m, replacing old with new. -// -// mountedFS{"/src/pkg", fs, "/src"}.translate("/src/pkg/code") == "/src/code". -func (m mountedFS) translate(path string) string { - path = pathpkg.Clean("/" + path) - if !hasPathPrefix(path, m.old) { - panic("translate " + path + " but old=" + m.old) - } - return pathpkg.Join(m.new, path[len(m.old):]) -} - -func (nameSpace) String() string { - return "ns" -} - -// Fprint writes a text representation of the name space to w. -func (ns nameSpace) Fprint(w io.Writer) { - fmt.Fprint(w, "name space {\n") - var all []string - for mtpt := range ns { - all = append(all, mtpt) - } - sort.Strings(all) - for _, mtpt := range all { - fmt.Fprintf(w, "\t%s:\n", mtpt) - for _, m := range ns[mtpt] { - fmt.Fprintf(w, "\t\t%s %s\n", m.fs, m.new) - } - } - fmt.Fprint(w, "}\n") -} - -// clean returns a cleaned, rooted path for evaluation. -// It canonicalizes the path so that we can use string operations -// to analyze it. -func (nameSpace) clean(path string) string { - return pathpkg.Clean("/" + path) -} - -// Bind causes references to old to redirect to the path new in newfs. -// If mode is bindReplace, old redirections are discarded. -// If mode is bindBefore, this redirection takes priority over existing ones, -// but earlier ones are still consulted for paths that do not exist in newfs. -// If mode is bindAfter, this redirection happens only after existing ones -// have been tried and failed. - -const ( - bindReplace = iota - bindBefore - bindAfter -) - -func (ns nameSpace) Bind(old string, newfs FileSystem, new string, mode int) { - old = ns.clean(old) - new = ns.clean(new) - m := mountedFS{old, newfs, new} - var mtpt []mountedFS - switch mode { - case bindReplace: - mtpt = append(mtpt, m) - case bindAfter: - mtpt = append(mtpt, ns.resolve(old)...) - mtpt = append(mtpt, m) - case bindBefore: - mtpt = append(mtpt, m) - mtpt = append(mtpt, ns.resolve(old)...) - } - - // Extend m.old, m.new in inherited mount point entries. - for i := range mtpt { - m := &mtpt[i] - if m.old != old { - if !hasPathPrefix(old, m.old) { - // This should not happen. If it does, panic so - // that we can see the call trace that led to it. - panic(fmt.Sprintf("invalid Bind: old=%q m={%q, %s, %q}", old, m.old, m.fs.String(), m.new)) - } - suffix := old[len(m.old):] - m.old = pathpkg.Join(m.old, suffix) - m.new = pathpkg.Join(m.new, suffix) - } - } - - ns[old] = mtpt -} - -// resolve resolves a path to the list of mountedFS to use for path. -func (ns nameSpace) resolve(path string) []mountedFS { - path = ns.clean(path) - for { - if m := ns[path]; m != nil { - if debugNS { - fmt.Printf("resolve %s: %v\n", path, m) - } - return m - } - if path == "/" { - break - } - path = pathpkg.Dir(path) - } - return nil -} - -// Open implements the FileSystem Open method. -func (ns nameSpace) Open(path string) (readSeekCloser, error) { - var err error - for _, m := range ns.resolve(path) { - if debugNS { - fmt.Printf("tx %s: %v\n", path, m.translate(path)) - } - r, err1 := m.fs.Open(m.translate(path)) - if err1 == nil { - return r, nil - } - if err == nil { - err = err1 - } - } - if err == nil { - err = &os.PathError{Op: "open", Path: path, Err: os.ErrNotExist} - } - return nil, err -} - -// stat implements the FileSystem Stat and Lstat methods. -func (ns nameSpace) stat(path string, f func(FileSystem, string) (os.FileInfo, error)) (os.FileInfo, error) { - var err error - for _, m := range ns.resolve(path) { - fi, err1 := f(m.fs, m.translate(path)) - if err1 == nil { - return fi, nil - } - if err == nil { - err = err1 - } - } - if err == nil { - err = &os.PathError{Op: "stat", Path: path, Err: os.ErrNotExist} - } - return nil, err -} - -func (ns nameSpace) Stat(path string) (os.FileInfo, error) { - return ns.stat(path, FileSystem.Stat) -} - -func (ns nameSpace) Lstat(path string) (os.FileInfo, error) { - return ns.stat(path, FileSystem.Lstat) -} - -// dirInfo is a trivial implementation of os.FileInfo for a directory. -type dirInfo string - -func (d dirInfo) Name() string { return string(d) } -func (d dirInfo) Size() int64 { return 0 } -func (d dirInfo) Mode() os.FileMode { return os.ModeDir | 0555 } -func (d dirInfo) ModTime() time.Time { return startTime } -func (d dirInfo) IsDir() bool { return true } -func (d dirInfo) Sys() interface{} { return nil } - -var startTime = time.Now() - -// ReadDir implements the FileSystem ReadDir method. It's where most of the magic is. -// (The rest is in resolve.) -// -// Logically, ReadDir must return the union of all the directories that are named -// by path. In order to avoid misinterpreting Go packages, of all the directories -// that contain Go source code, we only include the files from the first, -// but we include subdirectories from all. -// -// ReadDir must also return directory entries needed to reach mount points. -// If the name space looks like the example in the type nameSpace comment, -// but c:\Go does not have a src/pkg subdirectory, we still want to be able -// to find that subdirectory, because we've mounted d:\Work1 and d:\Work2 -// there. So if we don't see "src" in the directory listing for c:\Go, we add an -// entry for it before returning. -// -func (ns nameSpace) ReadDir(path string) ([]os.FileInfo, error) { - path = ns.clean(path) - - var ( - haveGo = false - haveName = map[string]bool{} - all []os.FileInfo - err error - first []os.FileInfo - ) - - for _, m := range ns.resolve(path) { - dir, err1 := m.fs.ReadDir(m.translate(path)) - if err1 != nil { - if err == nil { - err = err1 - } - continue - } - - if dir == nil { - dir = []os.FileInfo{} - } - - if first == nil { - first = dir - } - - // If we don't yet have Go files in 'all' and this directory - // has some, add all the files from this directory. - // Otherwise, only add subdirectories. - useFiles := false - if !haveGo { - for _, d := range dir { - if strings.HasSuffix(d.Name(), ".go") { - useFiles = true - haveGo = true - break - } - } - } - - for _, d := range dir { - name := d.Name() - if (d.IsDir() || useFiles) && !haveName[name] { - haveName[name] = true - all = append(all, d) - } - } - } - - // We didn't find any directories containing Go files. - // If some directory returned successfully, use that. - if !haveGo { - for _, d := range first { - if !haveName[d.Name()] { - haveName[d.Name()] = true - all = append(all, d) - } - } - } - - // Built union. Add any missing directories needed to reach mount points. - for old := range ns { - if hasPathPrefix(old, path) && old != path { - // Find next element after path in old. - elem := old[len(path):] - elem = strings.TrimPrefix(elem, "/") - if i := strings.Index(elem, "/"); i >= 0 { - elem = elem[:i] - } - if !haveName[elem] { - haveName[elem] = true - all = append(all, dirInfo(elem)) - } - } - } - - if len(all) == 0 { - return nil, err - } - - sort.Sort(byName(all)) - return all, nil -} - -// byName implements sort.Interface. -type byName []os.FileInfo - -func (f byName) Len() int { return len(f) } -func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() } -func (f byName) Swap(i, j int) { f[i], f[j] = f[j], f[i] } - -// An httpFS implements http.FileSystem using a FileSystem. -type httpFS struct { - fs FileSystem -} - -func (h *httpFS) Open(name string) (http.File, error) { - fi, err := h.fs.Stat(name) - if err != nil { - return nil, err - } - if fi.IsDir() { - return &httpDir{h.fs, name, nil}, nil - } - f, err := h.fs.Open(name) - if err != nil { - return nil, err - } - return &httpFile{h.fs, f, name}, nil -} - -// httpDir implements http.File for a directory in a FileSystem. -type httpDir struct { - fs FileSystem - name string - pending []os.FileInfo -} - -func (h *httpDir) Close() error { return nil } -func (h *httpDir) Stat() (os.FileInfo, error) { return h.fs.Stat(h.name) } -func (h *httpDir) Read([]byte) (int, error) { - return 0, fmt.Errorf("cannot Read from directory %s", h.name) -} - -func (h *httpDir) Seek(offset int64, whence int) (int64, error) { - if offset == 0 && whence == 0 { - h.pending = nil - return 0, nil - } - return 0, fmt.Errorf("unsupported Seek in directory %s", h.name) -} - -func (h *httpDir) Readdir(count int) ([]os.FileInfo, error) { - if h.pending == nil { - d, err := h.fs.ReadDir(h.name) - if err != nil { - return nil, err - } - if d == nil { - d = []os.FileInfo{} // not nil - } - h.pending = d - } - - if len(h.pending) == 0 && count > 0 { - return nil, io.EOF - } - if count <= 0 || count > len(h.pending) { - count = len(h.pending) - } - d := h.pending[:count] - h.pending = h.pending[count:] - return d, nil -} - -// httpFile implements http.File for a file (not directory) in a FileSystem. -type httpFile struct { - fs FileSystem - readSeekCloser - name string -} - -func (h *httpFile) Stat() (os.FileInfo, error) { return h.fs.Stat(h.name) } -func (h *httpFile) Readdir(int) ([]os.FileInfo, error) { - return nil, fmt.Errorf("cannot Readdir from file %s", h.name) -} diff --git a/src/cmd/godoc/format.go b/src/cmd/godoc/format.go deleted file mode 100644 index 59a89c5bf..000000000 --- a/src/cmd/godoc/format.go +++ /dev/null @@ -1,372 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file implements FormatSelections and FormatText. -// FormatText is used to HTML-format Go and non-Go source -// text with line numbers and highlighted sections. It is -// built on top of FormatSelections, a generic formatter -// for "selected" text. - -package main - -import ( - "fmt" - "go/scanner" - "go/token" - "io" - "regexp" - "strconv" - "text/template" -) - -// ---------------------------------------------------------------------------- -// Implementation of FormatSelections - -// A Segment describes a text segment [start, end). -// The zero value of a Segment is a ready-to-use empty segment. -// -type Segment struct { - start, end int -} - -func (seg *Segment) isEmpty() bool { return seg.start >= seg.end } - -// A Selection is an "iterator" function returning a text segment. -// Repeated calls to a selection return consecutive, non-overlapping, -// non-empty segments, followed by an infinite sequence of empty -// segments. The first empty segment marks the end of the selection. -// -type Selection func() Segment - -// A LinkWriter writes some start or end "tag" to w for the text offset offs. -// It is called by FormatSelections at the start or end of each link segment. -// -type LinkWriter func(w io.Writer, offs int, start bool) - -// A SegmentWriter formats a text according to selections and writes it to w. -// The selections parameter is a bit set indicating which selections provided -// to FormatSelections overlap with the text segment: If the n'th bit is set -// in selections, the n'th selection provided to FormatSelections is overlapping -// with the text. -// -type SegmentWriter func(w io.Writer, text []byte, selections int) - -// FormatSelections takes a text and writes it to w using link and segment -// writers lw and sw as follows: lw is invoked for consecutive segment starts -// and ends as specified through the links selection, and sw is invoked for -// consecutive segments of text overlapped by the same selections as specified -// by selections. The link writer lw may be nil, in which case the links -// Selection is ignored. -// -func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection, sw SegmentWriter, selections ...Selection) { - // If we have a link writer, make the links - // selection the last entry in selections - if lw != nil { - selections = append(selections, links) - } - - // compute the sequence of consecutive segment changes - changes := newMerger(selections) - - // The i'th bit in bitset indicates that the text - // at the current offset is covered by selections[i]. - bitset := 0 - lastOffs := 0 - - // Text segments are written in a delayed fashion - // such that consecutive segments belonging to the - // same selection can be combined (peephole optimization). - // last describes the last segment which has not yet been written. - var last struct { - begin, end int // valid if begin < end - bitset int - } - - // flush writes the last delayed text segment - flush := func() { - if last.begin < last.end { - sw(w, text[last.begin:last.end], last.bitset) - } - last.begin = last.end // invalidate last - } - - // segment runs the segment [lastOffs, end) with the selection - // indicated by bitset through the segment peephole optimizer. - segment := func(end int) { - if lastOffs < end { // ignore empty segments - if last.end != lastOffs || last.bitset != bitset { - // the last segment is not adjacent to or - // differs from the new one - flush() - // start a new segment - last.begin = lastOffs - } - last.end = end - last.bitset = bitset - } - } - - for { - // get the next segment change - index, offs, start := changes.next() - if index < 0 || offs > len(text) { - // no more segment changes or the next change - // is past the end of the text - we're done - break - } - // determine the kind of segment change - if lw != nil && index == len(selections)-1 { - // we have a link segment change (see start of this function): - // format the previous selection segment, write the - // link tag and start a new selection segment - segment(offs) - flush() - lastOffs = offs - lw(w, offs, start) - } else { - // we have a selection change: - // format the previous selection segment, determine - // the new selection bitset and start a new segment - segment(offs) - lastOffs = offs - mask := 1 << uint(index) - if start { - bitset |= mask - } else { - bitset &^= mask - } - } - } - segment(len(text)) - flush() -} - -// A merger merges a slice of Selections and produces a sequence of -// consecutive segment change events through repeated next() calls. -// -type merger struct { - selections []Selection - segments []Segment // segments[i] is the next segment of selections[i] -} - -const infinity int = 2e9 - -func newMerger(selections []Selection) *merger { - segments := make([]Segment, len(selections)) - for i, sel := range selections { - segments[i] = Segment{infinity, infinity} - if sel != nil { - if seg := sel(); !seg.isEmpty() { - segments[i] = seg - } - } - } - return &merger{selections, segments} -} - -// next returns the next segment change: index specifies the Selection -// to which the segment belongs, offs is the segment start or end offset -// as determined by the start value. If there are no more segment changes, -// next returns an index value < 0. -// -func (m *merger) next() (index, offs int, start bool) { - // find the next smallest offset where a segment starts or ends - offs = infinity - index = -1 - for i, seg := range m.segments { - switch { - case seg.start < offs: - offs = seg.start - index = i - start = true - case seg.end < offs: - offs = seg.end - index = i - start = false - } - } - if index < 0 { - // no offset found => all selections merged - return - } - // offset found - it's either the start or end offset but - // either way it is ok to consume the start offset: set it - // to infinity so it won't be considered in the following - // next call - m.segments[index].start = infinity - if start { - return - } - // end offset found - consume it - m.segments[index].end = infinity - // advance to the next segment for that selection - seg := m.selections[index]() - if !seg.isEmpty() { - m.segments[index] = seg - } - return -} - -// ---------------------------------------------------------------------------- -// Implementation of FormatText - -// lineSelection returns the line segments for text as a Selection. -func lineSelection(text []byte) Selection { - i, j := 0, 0 - return func() (seg Segment) { - // find next newline, if any - for j < len(text) { - j++ - if text[j-1] == '\n' { - break - } - } - if i < j { - // text[i:j] constitutes a line - seg = Segment{i, j} - i = j - } - return - } -} - -// tokenSelection returns, as a selection, the sequence of -// consecutive occurrences of token sel in the Go src text. -// -func tokenSelection(src []byte, sel token.Token) Selection { - var s scanner.Scanner - fset := token.NewFileSet() - file := fset.AddFile("", fset.Base(), len(src)) - s.Init(file, src, nil, scanner.ScanComments) - return func() (seg Segment) { - for { - pos, tok, lit := s.Scan() - if tok == token.EOF { - break - } - offs := file.Offset(pos) - if tok == sel { - seg = Segment{offs, offs + len(lit)} - break - } - } - return - } -} - -// makeSelection is a helper function to make a Selection from a slice of pairs. -// Pairs describing empty segments are ignored. -// -func makeSelection(matches [][]int) Selection { - i := 0 - return func() Segment { - for i < len(matches) { - m := matches[i] - i++ - if m[0] < m[1] { - // non-empty segment - return Segment{m[0], m[1]} - } - } - return Segment{} - } -} - -// regexpSelection computes the Selection for the regular expression expr in text. -func regexpSelection(text []byte, expr string) Selection { - var matches [][]int - if rx, err := regexp.Compile(expr); err == nil { - matches = rx.FindAllIndex(text, -1) - } - return makeSelection(matches) -} - -var selRx = regexp.MustCompile(`^([0-9]+):([0-9]+)`) - -// rangeSelection computes the Selection for a text range described -// by the argument str; the range description must match the selRx -// regular expression. -// -func rangeSelection(str string) Selection { - m := selRx.FindStringSubmatch(str) - if len(m) >= 2 { - from, _ := strconv.Atoi(m[1]) - to, _ := strconv.Atoi(m[2]) - if from < to { - return makeSelection([][]int{{from, to}}) - } - } - return nil -} - -// Span tags for all the possible selection combinations that may -// be generated by FormatText. Selections are indicated by a bitset, -// and the value of the bitset specifies the tag to be used. -// -// bit 0: comments -// bit 1: highlights -// bit 2: selections -// -var startTags = [][]byte{ - /* 000 */ []byte(``), - /* 001 */ []byte(`<span class="comment">`), - /* 010 */ []byte(`<span class="highlight">`), - /* 011 */ []byte(`<span class="highlight-comment">`), - /* 100 */ []byte(`<span class="selection">`), - /* 101 */ []byte(`<span class="selection-comment">`), - /* 110 */ []byte(`<span class="selection-highlight">`), - /* 111 */ []byte(`<span class="selection-highlight-comment">`), -} - -var endTag = []byte(`</span>`) - -func selectionTag(w io.Writer, text []byte, selections int) { - if selections < len(startTags) { - if tag := startTags[selections]; len(tag) > 0 { - w.Write(tag) - template.HTMLEscape(w, text) - w.Write(endTag) - return - } - } - template.HTMLEscape(w, text) -} - -// FormatText HTML-escapes text and writes it to w. -// Consecutive text segments are wrapped in HTML spans (with tags as -// defined by startTags and endTag) as follows: -// -// - if line >= 0, line number (ln) spans are inserted before each line, -// starting with the value of line -// - if the text is Go source, comments get the "comment" span class -// - each occurrence of the regular expression pattern gets the "highlight" -// span class -// - text segments covered by selection get the "selection" span class -// -// Comments, highlights, and selections may overlap arbitrarily; the respective -// HTML span classes are specified in the startTags variable. -// -func FormatText(w io.Writer, text []byte, line int, goSource bool, pattern string, selection Selection) { - var comments, highlights Selection - if goSource { - comments = tokenSelection(text, token.COMMENT) - } - if pattern != "" { - highlights = regexpSelection(text, pattern) - } - if line >= 0 || comments != nil || highlights != nil || selection != nil { - var lineTag LinkWriter - if line >= 0 { - lineTag = func(w io.Writer, _ int, start bool) { - if start { - fmt.Fprintf(w, "<a id=\"L%d\"></a><span class=\"ln\">%6d</span>\t", line, line) - line++ - } - } - } - FormatSelections(w, text, lineTag, lineSelection(text), selectionTag, comments, highlights, selection) - } else { - template.HTMLEscape(w, text) - } -} diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go deleted file mode 100644 index 26b0b97e1..000000000 --- a/src/cmd/godoc/godoc.go +++ /dev/null @@ -1,1582 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "bytes" - "encoding/json" - "flag" - "fmt" - "go/ast" - "go/build" - "go/doc" - "go/format" - "go/printer" - "go/token" - htmlpkg "html" - "io" - "io/ioutil" - "log" - "net/http" - "net/url" - "os" - pathpkg "path" - "path/filepath" - "regexp" - "runtime" - "sort" - "strings" - "text/template" - "time" - "unicode" - "unicode/utf8" -) - -// ---------------------------------------------------------------------------- -// Globals - -type delayTime struct { - RWValue -} - -func (dt *delayTime) backoff(max time.Duration) { - dt.mutex.Lock() - v := dt.value.(time.Duration) * 2 - if v > max { - v = max - } - dt.value = v - // don't change dt.timestamp - calling backoff indicates an error condition - dt.mutex.Unlock() -} - -var ( - verbose = flag.Bool("v", false, "verbose mode") - - // file system roots - // TODO(gri) consider the invariant that goroot always end in '/' - goroot = flag.String("goroot", runtime.GOROOT(), "Go root directory") - testDir = flag.String("testdir", "", "Go root subdirectory - for testing only (faster startups)") - - // layout control - tabwidth = flag.Int("tabwidth", 4, "tab width") - showTimestamps = flag.Bool("timestamps", false, "show timestamps with directory listings") - templateDir = flag.String("templates", "", "directory containing alternate template files") - showPlayground = flag.Bool("play", false, "enable playground in web interface") - showExamples = flag.Bool("ex", false, "show examples in command line mode") - declLinks = flag.Bool("links", true, "link identifiers to their declarations") - - // search index - indexEnabled = flag.Bool("index", false, "enable search index") - indexFiles = flag.String("index_files", "", "glob pattern specifying index files;"+ - "if not empty, the index is read from these files in sorted order") - maxResults = flag.Int("maxresults", 10000, "maximum number of full text search results shown") - indexThrottle = flag.Float64("index_throttle", 0.75, "index throttle value; 0.0 = no time allocated, 1.0 = full throttle") - - // file system information - fsTree RWValue // *Directory tree of packages, updated with each sync (but sync code is removed now) - fsModified RWValue // timestamp of last call to invalidateIndex - docMetadata RWValue // mapping from paths to *Metadata - - // http handlers - fileServer http.Handler // default file server - cmdHandler docServer - pkgHandler docServer - - // source code notes - notes = flag.String("notes", "BUG", "regular expression matching note markers to show") -) - -func initHandlers() { - fileServer = http.FileServer(&httpFS{fs}) - cmdHandler = docServer{"/cmd/", "/src/cmd"} - pkgHandler = docServer{"/pkg/", "/src/pkg"} -} - -func registerPublicHandlers(mux *http.ServeMux) { - mux.Handle(cmdHandler.pattern, &cmdHandler) - mux.Handle(pkgHandler.pattern, &pkgHandler) - mux.HandleFunc("/doc/codewalk/", codewalk) - mux.Handle("/doc/play/", fileServer) - mux.HandleFunc("/search", search) - mux.Handle("/robots.txt", fileServer) - mux.HandleFunc("/opensearch.xml", serveSearchDesc) - mux.HandleFunc("/", serveFile) -} - -func initFSTree() { - dir := newDirectory(pathpkg.Join("/", *testDir), -1) - if dir == nil { - log.Println("Warning: FSTree is nil") - return - } - fsTree.set(dir) - invalidateIndex() -} - -// ---------------------------------------------------------------------------- -// Tab conversion - -var spaces = []byte(" ") // 32 spaces seems like a good number - -const ( - indenting = iota - collecting -) - -// A tconv is an io.Writer filter for converting leading tabs into spaces. -type tconv struct { - output io.Writer - state int // indenting or collecting - indent int // valid if state == indenting -} - -func (p *tconv) writeIndent() (err error) { - i := p.indent - for i >= len(spaces) { - i -= len(spaces) - if _, err = p.output.Write(spaces); err != nil { - return - } - } - // i < len(spaces) - if i > 0 { - _, err = p.output.Write(spaces[0:i]) - } - return -} - -func (p *tconv) Write(data []byte) (n int, err error) { - if len(data) == 0 { - return - } - pos := 0 // valid if p.state == collecting - var b byte - for n, b = range data { - switch p.state { - case indenting: - switch b { - case '\t': - p.indent += *tabwidth - case '\n': - p.indent = 0 - if _, err = p.output.Write(data[n : n+1]); err != nil { - return - } - case ' ': - p.indent++ - default: - p.state = collecting - pos = n - if err = p.writeIndent(); err != nil { - return - } - } - case collecting: - if b == '\n' { - p.state = indenting - p.indent = 0 - if _, err = p.output.Write(data[pos : n+1]); err != nil { - return - } - } - } - } - n = len(data) - if pos < n && p.state == collecting { - _, err = p.output.Write(data[pos:]) - } - return -} - -// ---------------------------------------------------------------------------- -// Templates - -// Write an AST node to w. -func writeNode(w io.Writer, fset *token.FileSet, x interface{}) { - // convert trailing tabs into spaces using a tconv filter - // to ensure a good outcome in most browsers (there may still - // be tabs in comments and strings, but converting those into - // the right number of spaces is much harder) - // - // TODO(gri) rethink printer flags - perhaps tconv can be eliminated - // with an another printer mode (which is more efficiently - // implemented in the printer than here with another layer) - mode := printer.TabIndent | printer.UseSpaces - err := (&printer.Config{Mode: mode, Tabwidth: *tabwidth}).Fprint(&tconv{output: w}, fset, x) - if err != nil { - log.Print(err) - } -} - -func filenameFunc(path string) string { - _, localname := pathpkg.Split(path) - return localname -} - -func fileInfoNameFunc(fi os.FileInfo) string { - name := fi.Name() - if fi.IsDir() { - name += "/" - } - return name -} - -func fileInfoTimeFunc(fi os.FileInfo) string { - if t := fi.ModTime(); t.Unix() != 0 { - return t.Local().String() - } - return "" // don't return epoch if time is obviously not set -} - -// The strings in infoKinds must be properly html-escaped. -var infoKinds = [nKinds]string{ - PackageClause: "package clause", - ImportDecl: "import decl", - ConstDecl: "const decl", - TypeDecl: "type decl", - VarDecl: "var decl", - FuncDecl: "func decl", - MethodDecl: "method decl", - Use: "use", -} - -func infoKind_htmlFunc(info SpotInfo) string { - return infoKinds[info.Kind()] // infoKind entries are html-escaped -} - -func infoLineFunc(info SpotInfo) int { - line := info.Lori() - if info.IsIndex() { - index, _ := searchIndex.get() - if index != nil { - line = index.(*Index).Snippet(line).Line - } else { - // no line information available because - // we don't have an index - this should - // never happen; be conservative and don't - // crash - line = 0 - } - } - return line -} - -func infoSnippet_htmlFunc(info SpotInfo) string { - if info.IsIndex() { - index, _ := searchIndex.get() - // Snippet.Text was HTML-escaped when it was generated - return index.(*Index).Snippet(info.Lori()).Text - } - return `<span class="alert">no snippet text available</span>` -} - -func nodeFunc(info *PageInfo, node interface{}) string { - var buf bytes.Buffer - writeNode(&buf, info.FSet, node) - return buf.String() -} - -func node_htmlFunc(info *PageInfo, node interface{}, linkify bool) string { - var buf1 bytes.Buffer - writeNode(&buf1, info.FSet, node) - - var buf2 bytes.Buffer - if n, _ := node.(ast.Node); n != nil && linkify && *declLinks { - LinkifyText(&buf2, buf1.Bytes(), n) - } else { - FormatText(&buf2, buf1.Bytes(), -1, true, "", nil) - } - - return buf2.String() -} - -func comment_htmlFunc(comment string) string { - var buf bytes.Buffer - // TODO(gri) Provide list of words (e.g. function parameters) - // to be emphasized by ToHTML. - doc.ToHTML(&buf, comment, nil) // does html-escaping - return buf.String() -} - -// punchCardWidth is the number of columns of fixed-width -// characters to assume when wrapping text. Very few people -// use terminals or cards smaller than 80 characters, so 80 it is. -// We do not try to sniff the environment or the tty to adapt to -// the situation; instead, by using a constant we make sure that -// godoc always produces the same output regardless of context, -// a consistency that is lost otherwise. For example, if we sniffed -// the environment or tty, then http://golang.org/pkg/math/?m=text -// would depend on the width of the terminal where godoc started, -// which is clearly bogus. More generally, the Unix tools that behave -// differently when writing to a tty than when writing to a file have -// a history of causing confusion (compare `ls` and `ls | cat`), and we -// want to avoid that mistake here. -const punchCardWidth = 80 - -func comment_textFunc(comment, indent, preIndent string) string { - var buf bytes.Buffer - doc.ToText(&buf, comment, indent, preIndent, punchCardWidth-2*len(indent)) - return buf.String() -} - -func startsWithUppercase(s string) bool { - r, _ := utf8.DecodeRuneInString(s) - return unicode.IsUpper(r) -} - -var exampleOutputRx = regexp.MustCompile(`(?i)//[[:space:]]*output:`) - -// stripExampleSuffix strips lowercase braz in Foo_braz or Foo_Bar_braz from name -// while keeping uppercase Braz in Foo_Braz. -func stripExampleSuffix(name string) string { - if i := strings.LastIndex(name, "_"); i != -1 { - if i < len(name)-1 && !startsWithUppercase(name[i+1:]) { - name = name[:i] - } - } - return name -} - -func example_textFunc(info *PageInfo, funcName, indent string) string { - if !*showExamples { - return "" - } - - var buf bytes.Buffer - first := true - for _, eg := range info.Examples { - name := stripExampleSuffix(eg.Name) - if name != funcName { - continue - } - - if !first { - buf.WriteString("\n") - } - first = false - - // print code - cnode := &printer.CommentedNode{Node: eg.Code, Comments: eg.Comments} - var buf1 bytes.Buffer - writeNode(&buf1, info.FSet, cnode) - code := buf1.String() - // Additional formatting if this is a function body. - if n := len(code); n >= 2 && code[0] == '{' && code[n-1] == '}' { - // remove surrounding braces - code = code[1 : n-1] - // unindent - code = strings.Replace(code, "\n ", "\n", -1) - } - code = strings.Trim(code, "\n") - code = strings.Replace(code, "\n", "\n\t", -1) - - buf.WriteString(indent) - buf.WriteString("Example:\n\t") - buf.WriteString(code) - buf.WriteString("\n") - } - return buf.String() -} - -func example_htmlFunc(info *PageInfo, funcName string) string { - var buf bytes.Buffer - for _, eg := range info.Examples { - name := stripExampleSuffix(eg.Name) - - if name != funcName { - continue - } - - // print code - cnode := &printer.CommentedNode{Node: eg.Code, Comments: eg.Comments} - code := node_htmlFunc(info, cnode, true) - out := eg.Output - wholeFile := true - - // Additional formatting if this is a function body. - if n := len(code); n >= 2 && code[0] == '{' && code[n-1] == '}' { - wholeFile = false - // remove surrounding braces - code = code[1 : n-1] - // unindent - code = strings.Replace(code, "\n ", "\n", -1) - // remove output comment - if loc := exampleOutputRx.FindStringIndex(code); loc != nil { - code = strings.TrimSpace(code[:loc[0]]) - } - } - - // Write out the playground code in standard Go style - // (use tabs, no comment highlight, etc). - play := "" - if eg.Play != nil && *showPlayground { - var buf bytes.Buffer - if err := format.Node(&buf, info.FSet, eg.Play); err != nil { - log.Print(err) - } else { - play = buf.String() - } - } - - // Drop output, as the output comment will appear in the code. - if wholeFile && play == "" { - out = "" - } - - err := exampleHTML.Execute(&buf, struct { - Name, Doc, Code, Play, Output string - }{eg.Name, eg.Doc, code, play, out}) - if err != nil { - log.Print(err) - } - } - return buf.String() -} - -// example_nameFunc takes an example function name and returns its display -// name. For example, "Foo_Bar_quux" becomes "Foo.Bar (Quux)". -func example_nameFunc(s string) string { - name, suffix := splitExampleName(s) - // replace _ with . for method names - name = strings.Replace(name, "_", ".", 1) - // use "Package" if no name provided - if name == "" { - name = "Package" - } - return name + suffix -} - -// example_suffixFunc takes an example function name and returns its suffix in -// parenthesized form. For example, "Foo_Bar_quux" becomes " (Quux)". -func example_suffixFunc(name string) string { - _, suffix := splitExampleName(name) - return suffix -} - -func noteTitle(note string) string { - return strings.Title(strings.ToLower(note)) -} - -func splitExampleName(s string) (name, suffix string) { - i := strings.LastIndex(s, "_") - if 0 <= i && i < len(s)-1 && !startsWithUppercase(s[i+1:]) { - name = s[:i] - suffix = " (" + strings.Title(s[i+1:]) + ")" - return - } - name = s - return -} - -func pkgLinkFunc(path string) string { - relpath := path[1:] - // because of the irregular mapping under goroot - // we need to correct certain relative paths - relpath = strings.TrimPrefix(relpath, "src/pkg/") - return pkgHandler.pattern[1:] + relpath // remove trailing '/' for relative URL -} - -// n must be an ast.Node or a *doc.Note -func posLink_urlFunc(info *PageInfo, n interface{}) string { - var pos, end token.Pos - - switch n := n.(type) { - case ast.Node: - pos = n.Pos() - end = n.End() - case *doc.Note: - pos = n.Pos - end = n.End - default: - panic(fmt.Sprintf("wrong type for posLink_url template formatter: %T", n)) - } - - var relpath string - var line int - var low, high int // selection offset range - - if pos.IsValid() { - p := info.FSet.Position(pos) - relpath = p.Filename - line = p.Line - low = p.Offset - } - if end.IsValid() { - high = info.FSet.Position(end).Offset - } - - var buf bytes.Buffer - template.HTMLEscape(&buf, []byte(relpath)) - // selection ranges are of form "s=low:high" - if low < high { - fmt.Fprintf(&buf, "?s=%d:%d", low, high) // no need for URL escaping - // if we have a selection, position the page - // such that the selection is a bit below the top - line -= 10 - if line < 1 { - line = 1 - } - } - // line id's in html-printed source are of the - // form "L%d" where %d stands for the line number - if line > 0 { - fmt.Fprintf(&buf, "#L%d", line) // no need for URL escaping - } - - return buf.String() -} - -func srcLinkFunc(s string) string { - return pathpkg.Clean("/" + s) -} - -// fmap describes the template functions installed with all godoc templates. -// Convention: template function names ending in "_html" or "_url" produce -// HTML- or URL-escaped strings; all other function results may -// require explicit escaping in the template. -var fmap = template.FuncMap{ - // various helpers - "filename": filenameFunc, - "repeat": strings.Repeat, - - // access to FileInfos (directory listings) - "fileInfoName": fileInfoNameFunc, - "fileInfoTime": fileInfoTimeFunc, - - // access to search result information - "infoKind_html": infoKind_htmlFunc, - "infoLine": infoLineFunc, - "infoSnippet_html": infoSnippet_htmlFunc, - - // formatting of AST nodes - "node": nodeFunc, - "node_html": node_htmlFunc, - "comment_html": comment_htmlFunc, - "comment_text": comment_textFunc, - - // support for URL attributes - "pkgLink": pkgLinkFunc, - "srcLink": srcLinkFunc, - "posLink_url": posLink_urlFunc, - - // formatting of Examples - "example_html": example_htmlFunc, - "example_text": example_textFunc, - "example_name": example_nameFunc, - "example_suffix": example_suffixFunc, - - // formatting of Notes - "noteTitle": noteTitle, -} - -func readTemplate(name string) *template.Template { - path := "lib/godoc/" + name - - // use underlying file system fs to read the template file - // (cannot use template ParseFile functions directly) - data, err := ReadFile(fs, path) - if err != nil { - log.Fatal("readTemplate: ", err) - } - // be explicit with errors (for app engine use) - t, err := template.New(name).Funcs(fmap).Parse(string(data)) - if err != nil { - log.Fatal("readTemplate: ", err) - } - return t -} - -var ( - codewalkHTML, - codewalkdirHTML, - dirlistHTML, - errorHTML, - exampleHTML, - godocHTML, - packageHTML, - packageText, - searchHTML, - searchText, - searchDescXML *template.Template -) - -func readTemplates() { - // have to delay until after flags processing since paths depend on goroot - codewalkHTML = readTemplate("codewalk.html") - codewalkdirHTML = readTemplate("codewalkdir.html") - dirlistHTML = readTemplate("dirlist.html") - errorHTML = readTemplate("error.html") - exampleHTML = readTemplate("example.html") - godocHTML = readTemplate("godoc.html") - packageHTML = readTemplate("package.html") - packageText = readTemplate("package.txt") - searchHTML = readTemplate("search.html") - searchText = readTemplate("search.txt") - searchDescXML = readTemplate("opensearch.xml") -} - -// ---------------------------------------------------------------------------- -// Generic HTML wrapper - -// Page describes the contents of the top-level godoc webpage. -type Page struct { - Title string - Tabtitle string - Subtitle string - Query string - Body []byte - - // filled in by servePage - SearchBox bool - Playground bool - Version string -} - -func servePage(w http.ResponseWriter, page Page) { - if page.Tabtitle == "" { - page.Tabtitle = page.Title - } - page.SearchBox = *indexEnabled - page.Playground = *showPlayground - page.Version = runtime.Version() - if err := godocHTML.Execute(w, page); err != nil { - log.Printf("godocHTML.Execute: %s", err) - } -} - -func serveText(w http.ResponseWriter, text []byte) { - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - w.Write(text) -} - -// ---------------------------------------------------------------------------- -// Files - -var ( - doctype = []byte("<!DOCTYPE ") - jsonStart = []byte("<!--{") - jsonEnd = []byte("}-->") -) - -func serveHTMLDoc(w http.ResponseWriter, r *http.Request, abspath, relpath string) { - // get HTML body contents - src, err := ReadFile(fs, abspath) - if err != nil { - log.Printf("ReadFile: %s", err) - serveError(w, r, relpath, err) - return - } - - // if it begins with "<!DOCTYPE " assume it is standalone - // html that doesn't need the template wrapping. - if bytes.HasPrefix(src, doctype) { - w.Write(src) - return - } - - // if it begins with a JSON blob, read in the metadata. - meta, src, err := extractMetadata(src) - if err != nil { - log.Printf("decoding metadata %s: %v", relpath, err) - } - - // evaluate as template if indicated - if meta.Template { - tmpl, err := template.New("main").Funcs(templateFuncs).Parse(string(src)) - if err != nil { - log.Printf("parsing template %s: %v", relpath, err) - serveError(w, r, relpath, err) - return - } - var buf bytes.Buffer - if err := tmpl.Execute(&buf, nil); err != nil { - log.Printf("executing template %s: %v", relpath, err) - serveError(w, r, relpath, err) - return - } - src = buf.Bytes() - } - - // if it's the language spec, add tags to EBNF productions - if strings.HasSuffix(abspath, "go_spec.html") { - var buf bytes.Buffer - Linkify(&buf, src) - src = buf.Bytes() - } - - servePage(w, Page{ - Title: meta.Title, - Subtitle: meta.Subtitle, - Body: src, - }) -} - -func applyTemplate(t *template.Template, name string, data interface{}) []byte { - var buf bytes.Buffer - if err := t.Execute(&buf, data); err != nil { - log.Printf("%s.Execute: %s", name, err) - } - return buf.Bytes() -} - -func redirect(w http.ResponseWriter, r *http.Request) (redirected bool) { - canonical := pathpkg.Clean(r.URL.Path) - if !strings.HasSuffix(canonical, "/") { - canonical += "/" - } - if r.URL.Path != canonical { - url := *r.URL - url.Path = canonical - http.Redirect(w, r, url.String(), http.StatusMovedPermanently) - redirected = true - } - return -} - -func redirectFile(w http.ResponseWriter, r *http.Request) (redirected bool) { - c := pathpkg.Clean(r.URL.Path) - c = strings.TrimRight(c, "/") - if r.URL.Path != c { - url := *r.URL - url.Path = c - http.Redirect(w, r, url.String(), http.StatusMovedPermanently) - redirected = true - } - return -} - -func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, title string) { - src, err := ReadFile(fs, abspath) - if err != nil { - log.Printf("ReadFile: %s", err) - serveError(w, r, relpath, err) - return - } - - if r.FormValue("m") == "text" { - serveText(w, src) - return - } - - var buf bytes.Buffer - buf.WriteString("<pre>") - FormatText(&buf, src, 1, pathpkg.Ext(abspath) == ".go", r.FormValue("h"), rangeSelection(r.FormValue("s"))) - buf.WriteString("</pre>") - fmt.Fprintf(&buf, `<p><a href="/%s?m=text">View as plain text</a></p>`, htmlpkg.EscapeString(relpath)) - - servePage(w, Page{ - Title: title + " " + relpath, - Tabtitle: relpath, - Body: buf.Bytes(), - }) -} - -func serveDirectory(w http.ResponseWriter, r *http.Request, abspath, relpath string) { - if redirect(w, r) { - return - } - - list, err := fs.ReadDir(abspath) - if err != nil { - serveError(w, r, relpath, err) - return - } - - servePage(w, Page{ - Title: "Directory " + relpath, - Tabtitle: relpath, - Body: applyTemplate(dirlistHTML, "dirlistHTML", list), - }) -} - -func serveFile(w http.ResponseWriter, r *http.Request) { - relpath := r.URL.Path - - // Check to see if we need to redirect or serve another file. - if m := metadataFor(relpath); m != nil { - if m.Path != relpath { - // Redirect to canonical path. - http.Redirect(w, r, m.Path, http.StatusMovedPermanently) - return - } - // Serve from the actual filesystem path. - relpath = m.filePath - } - - abspath := relpath - relpath = relpath[1:] // strip leading slash - - switch pathpkg.Ext(relpath) { - case ".html": - if strings.HasSuffix(relpath, "/index.html") { - // We'll show index.html for the directory. - // Use the dir/ version as canonical instead of dir/index.html. - http.Redirect(w, r, r.URL.Path[0:len(r.URL.Path)-len("index.html")], http.StatusMovedPermanently) - return - } - serveHTMLDoc(w, r, abspath, relpath) - return - - case ".go": - serveTextFile(w, r, abspath, relpath, "Source file") - return - } - - dir, err := fs.Lstat(abspath) - if err != nil { - log.Print(err) - serveError(w, r, relpath, err) - return - } - - if dir != nil && dir.IsDir() { - if redirect(w, r) { - return - } - if index := pathpkg.Join(abspath, "index.html"); isTextFile(index) { - serveHTMLDoc(w, r, index, index) - return - } - serveDirectory(w, r, abspath, relpath) - return - } - - if isTextFile(abspath) { - if redirectFile(w, r) { - return - } - serveTextFile(w, r, abspath, relpath, "Text file") - return - } - - fileServer.ServeHTTP(w, r) -} - -func serveSearchDesc(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/opensearchdescription+xml") - data := map[string]interface{}{ - "BaseURL": fmt.Sprintf("http://%s", r.Host), - } - if err := searchDescXML.Execute(w, &data); err != nil { - log.Printf("searchDescXML.Execute: %s", err) - } -} - -// ---------------------------------------------------------------------------- -// Packages - -// Fake relative package path for built-ins. Documentation for all globals -// (not just exported ones) will be shown for packages in this directory. -const builtinPkgPath = "builtin" - -type PageInfoMode uint - -const ( - noFiltering PageInfoMode = 1 << iota // do not filter exports - allMethods // show all embedded methods - showSource // show source code, do not extract documentation - noHtml // show result in textual form, do not generate HTML - flatDir // show directory in a flat (non-indented) manner -) - -// modeNames defines names for each PageInfoMode flag. -var modeNames = map[string]PageInfoMode{ - "all": noFiltering, - "methods": allMethods, - "src": showSource, - "text": noHtml, - "flat": flatDir, -} - -// getPageInfoMode computes the PageInfoMode flags by analyzing the request -// URL form value "m". It is value is a comma-separated list of mode names -// as defined by modeNames (e.g.: m=src,text). -func getPageInfoMode(r *http.Request) PageInfoMode { - var mode PageInfoMode - for _, k := range strings.Split(r.FormValue("m"), ",") { - if m, found := modeNames[strings.TrimSpace(k)]; found { - mode |= m - } - } - return adjustPageInfoMode(r, mode) -} - -// Specialized versions of godoc may adjust the PageInfoMode by overriding -// this variable. -var adjustPageInfoMode = func(_ *http.Request, mode PageInfoMode) PageInfoMode { - return mode -} - -// remoteSearchURL returns the search URL for a given query as needed by -// remoteSearch. If html is set, an html result is requested; otherwise -// the result is in textual form. -// Adjust this function as necessary if modeNames or FormValue parameters -// change. -func remoteSearchURL(query string, html bool) string { - s := "/search?m=text&q=" - if html { - s = "/search?q=" - } - return s + url.QueryEscape(query) -} - -type PageInfo struct { - Dirname string // directory containing the package - Err error // error or nil - - // package info - FSet *token.FileSet // nil if no package documentation - PDoc *doc.Package // nil if no package documentation - Examples []*doc.Example // nil if no example code - Notes map[string][]*doc.Note // nil if no package Notes - PAst *ast.File // nil if no AST with package exports - IsMain bool // true for package main - - // directory info - Dirs *DirList // nil if no directory information - DirTime time.Time // directory time stamp - DirFlat bool // if set, show directory in a flat (non-indented) manner -} - -func (info *PageInfo) IsEmpty() bool { - return info.Err != nil || info.PAst == nil && info.PDoc == nil && info.Dirs == nil -} - -type docServer struct { - pattern string // url pattern; e.g. "/pkg/" - fsRoot string // file system root to which the pattern is mapped -} - -// fsReadDir implements ReadDir for the go/build package. -func fsReadDir(dir string) ([]os.FileInfo, error) { - return fs.ReadDir(filepath.ToSlash(dir)) -} - -// fsOpenFile implements OpenFile for the go/build package. -func fsOpenFile(name string) (r io.ReadCloser, err error) { - data, err := ReadFile(fs, filepath.ToSlash(name)) - if err != nil { - return nil, err - } - return ioutil.NopCloser(bytes.NewReader(data)), nil -} - -// packageExports is a local implementation of ast.PackageExports -// which correctly updates each package file's comment list. -// (The ast.PackageExports signature is frozen, hence the local -// implementation). -// -func packageExports(fset *token.FileSet, pkg *ast.Package) { - for _, src := range pkg.Files { - cmap := ast.NewCommentMap(fset, src, src.Comments) - ast.FileExports(src) - src.Comments = cmap.Filter(src).Comments() - } -} - -// addNames adds the names declared by decl to the names set. -// Method names are added in the form ReceiverTypeName_Method. -func addNames(names map[string]bool, decl ast.Decl) { - switch d := decl.(type) { - case *ast.FuncDecl: - name := d.Name.Name - if d.Recv != nil { - var typeName string - switch r := d.Recv.List[0].Type.(type) { - case *ast.StarExpr: - typeName = r.X.(*ast.Ident).Name - case *ast.Ident: - typeName = r.Name - } - name = typeName + "_" + name - } - names[name] = true - case *ast.GenDecl: - for _, spec := range d.Specs { - switch s := spec.(type) { - case *ast.TypeSpec: - names[s.Name.Name] = true - case *ast.ValueSpec: - for _, id := range s.Names { - names[id.Name] = true - } - } - } - } -} - -// globalNames returns a set of the names declared by all package-level -// declarations. Method names are returned in the form Receiver_Method. -func globalNames(pkg *ast.Package) map[string]bool { - names := make(map[string]bool) - for _, file := range pkg.Files { - for _, decl := range file.Decls { - addNames(names, decl) - } - } - return names -} - -// collectExamples collects examples for pkg from testfiles. -func collectExamples(pkg *ast.Package, testfiles map[string]*ast.File) []*doc.Example { - var files []*ast.File - for _, f := range testfiles { - files = append(files, f) - } - - var examples []*doc.Example - globals := globalNames(pkg) - for _, e := range doc.Examples(files...) { - name := stripExampleSuffix(e.Name) - if name == "" || globals[name] { - examples = append(examples, e) - } else { - log.Printf("skipping example 'Example%s' because '%s' is not a known function or type", e.Name, e.Name) - } - } - - return examples -} - -// poorMansImporter returns a (dummy) package object named -// by the last path component of the provided package path -// (as is the convention for packages). This is sufficient -// to resolve package identifiers without doing an actual -// import. It never returns an error. -// -func poorMansImporter(imports map[string]*ast.Object, path string) (*ast.Object, error) { - pkg := imports[path] - if pkg == nil { - // note that strings.LastIndex returns -1 if there is no "/" - pkg = ast.NewObj(ast.Pkg, path[strings.LastIndex(path, "/")+1:]) - pkg.Data = ast.NewScope(nil) // required by ast.NewPackage for dot-import - imports[path] = pkg - } - return pkg, nil -} - -// getPageInfo returns the PageInfo for a package directory abspath. If the -// parameter genAST is set, an AST containing only the package exports is -// computed (PageInfo.PAst), otherwise package documentation (PageInfo.Doc) -// is extracted from the AST. If there is no corresponding package in the -// directory, PageInfo.PAst and PageInfo.PDoc are nil. If there are no sub- -// directories, PageInfo.Dirs is nil. If an error occurred, PageInfo.Err is -// set to the respective error but the error is not logged. -// -func (h *docServer) getPageInfo(abspath, relpath string, mode PageInfoMode) *PageInfo { - info := &PageInfo{Dirname: abspath} - - // Restrict to the package files that would be used when building - // the package on this system. This makes sure that if there are - // separate implementations for, say, Windows vs Unix, we don't - // jumble them all together. - // Note: Uses current binary's GOOS/GOARCH. - // To use different pair, such as if we allowed the user to choose, - // set ctxt.GOOS and ctxt.GOARCH before calling ctxt.ImportDir. - ctxt := build.Default - ctxt.IsAbsPath = pathpkg.IsAbs - ctxt.ReadDir = fsReadDir - ctxt.OpenFile = fsOpenFile - pkginfo, err := ctxt.ImportDir(abspath, 0) - // continue if there are no Go source files; we still want the directory info - if _, nogo := err.(*build.NoGoError); err != nil && !nogo { - info.Err = err - return info - } - - // collect package files - pkgname := pkginfo.Name - pkgfiles := append(pkginfo.GoFiles, pkginfo.CgoFiles...) - if len(pkgfiles) == 0 { - // Commands written in C have no .go files in the build. - // Instead, documentation may be found in an ignored file. - // The file may be ignored via an explicit +build ignore - // constraint (recommended), or by defining the package - // documentation (historic). - pkgname = "main" // assume package main since pkginfo.Name == "" - pkgfiles = pkginfo.IgnoredGoFiles - } - - // get package information, if any - if len(pkgfiles) > 0 { - // build package AST - fset := token.NewFileSet() - files, err := parseFiles(fset, abspath, pkgfiles) - if err != nil { - info.Err = err - return info - } - - // ignore any errors - they are due to unresolved identifiers - pkg, _ := ast.NewPackage(fset, files, poorMansImporter, nil) - - // extract package documentation - info.FSet = fset - if mode&showSource == 0 { - // show extracted documentation - var m doc.Mode - if mode&noFiltering != 0 { - m = doc.AllDecls - } - if mode&allMethods != 0 { - m |= doc.AllMethods - } - info.PDoc = doc.New(pkg, pathpkg.Clean(relpath), m) // no trailing '/' in importpath - - // collect examples - testfiles := append(pkginfo.TestGoFiles, pkginfo.XTestGoFiles...) - files, err = parseFiles(fset, abspath, testfiles) - if err != nil { - log.Println("parsing examples:", err) - } - info.Examples = collectExamples(pkg, files) - - // collect any notes that we want to show - if info.PDoc.Notes != nil { - // could regexp.Compile only once per godoc, but probably not worth it - if rx, err := regexp.Compile(*notes); err == nil { - for m, n := range info.PDoc.Notes { - if rx.MatchString(m) { - if info.Notes == nil { - info.Notes = make(map[string][]*doc.Note) - } - info.Notes[m] = n - } - } - } - } - - } else { - // show source code - // TODO(gri) Consider eliminating export filtering in this mode, - // or perhaps eliminating the mode altogether. - if mode&noFiltering == 0 { - packageExports(fset, pkg) - } - info.PAst = ast.MergePackageFiles(pkg, 0) - } - info.IsMain = pkgname == "main" - } - - // get directory information, if any - var dir *Directory - var timestamp time.Time - if tree, ts := fsTree.get(); tree != nil && tree.(*Directory) != nil { - // directory tree is present; lookup respective directory - // (may still fail if the file system was updated and the - // new directory tree has not yet been computed) - dir = tree.(*Directory).lookup(abspath) - timestamp = ts - } - if dir == nil { - // no directory tree present (too early after startup or - // command-line mode); compute one level for this page - // note: cannot use path filter here because in general - // it doesn't contain the fsTree path - dir = newDirectory(abspath, 1) - timestamp = time.Now() - } - info.Dirs = dir.listing(true) - info.DirTime = timestamp - info.DirFlat = mode&flatDir != 0 - - return info -} - -func (h *docServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { - if redirect(w, r) { - return - } - - relpath := pathpkg.Clean(r.URL.Path[len(h.pattern):]) - abspath := pathpkg.Join(h.fsRoot, relpath) - mode := getPageInfoMode(r) - if relpath == builtinPkgPath { - mode = noFiltering - } - info := h.getPageInfo(abspath, relpath, mode) - if info.Err != nil { - log.Print(info.Err) - serveError(w, r, relpath, info.Err) - return - } - - if mode&noHtml != 0 { - serveText(w, applyTemplate(packageText, "packageText", info)) - return - } - - var tabtitle, title, subtitle string - switch { - case info.PAst != nil: - tabtitle = info.PAst.Name.Name - case info.PDoc != nil: - tabtitle = info.PDoc.Name - default: - tabtitle = info.Dirname - title = "Directory " - if *showTimestamps { - subtitle = "Last update: " + info.DirTime.String() - } - } - if title == "" { - if info.IsMain { - // assume that the directory name is the command name - _, tabtitle = pathpkg.Split(relpath) - title = "Command " - } else { - title = "Package " - } - } - title += tabtitle - - // special cases for top-level package/command directories - switch tabtitle { - case "/src/pkg": - tabtitle = "Packages" - case "/src/cmd": - tabtitle = "Commands" - } - - servePage(w, Page{ - Title: title, - Tabtitle: tabtitle, - Subtitle: subtitle, - Body: applyTemplate(packageHTML, "packageHTML", info), - }) -} - -// ---------------------------------------------------------------------------- -// Search - -var searchIndex RWValue - -type SearchResult struct { - Query string - Alert string // error or warning message - - // identifier matches - Pak HitList // packages matching Query - Hit *LookupResult // identifier matches of Query - Alt *AltWords // alternative identifiers to look for - - // textual matches - Found int // number of textual occurrences found - Textual []FileLines // textual matches of Query - Complete bool // true if all textual occurrences of Query are reported -} - -func lookup(query string) (result SearchResult) { - result.Query = query - - index, timestamp := searchIndex.get() - if index != nil { - index := index.(*Index) - - // identifier search - var err error - result.Pak, result.Hit, result.Alt, err = index.Lookup(query) - if err != nil && *maxResults <= 0 { - // ignore the error if full text search is enabled - // since the query may be a valid regular expression - result.Alert = "Error in query string: " + err.Error() - return - } - - // full text search - if *maxResults > 0 && query != "" { - rx, err := regexp.Compile(query) - if err != nil { - result.Alert = "Error in query regular expression: " + err.Error() - return - } - // If we get maxResults+1 results we know that there are more than - // maxResults results and thus the result may be incomplete (to be - // precise, we should remove one result from the result set, but - // nobody is going to count the results on the result page). - result.Found, result.Textual = index.LookupRegexp(rx, *maxResults+1) - result.Complete = result.Found <= *maxResults - if !result.Complete { - result.Found-- // since we looked for maxResults+1 - } - } - } - - // is the result accurate? - if *indexEnabled { - if _, ts := fsModified.get(); timestamp.Before(ts) { - // The index is older than the latest file system change under godoc's observation. - result.Alert = "Indexing in progress: result may be inaccurate" - } - } else { - result.Alert = "Search index disabled: no results available" - } - - return -} - -func search(w http.ResponseWriter, r *http.Request) { - query := strings.TrimSpace(r.FormValue("q")) - result := lookup(query) - - if getPageInfoMode(r)&noHtml != 0 { - serveText(w, applyTemplate(searchText, "searchText", result)) - return - } - - var title string - if result.Hit != nil || len(result.Textual) > 0 { - title = fmt.Sprintf(`Results for query %q`, query) - } else { - title = fmt.Sprintf(`No results found for query %q`, query) - } - - servePage(w, Page{ - Title: title, - Tabtitle: query, - Query: query, - Body: applyTemplate(searchHTML, "searchHTML", result), - }) -} - -// ---------------------------------------------------------------------------- -// Documentation Metadata - -type Metadata struct { - Title string - Subtitle string - Template bool // execute as template - Path string // canonical path for this page - filePath string // filesystem path relative to goroot -} - -// extractMetadata extracts the Metadata from a byte slice. -// It returns the Metadata value and the remaining data. -// If no metadata is present the original byte slice is returned. -// -func extractMetadata(b []byte) (meta Metadata, tail []byte, err error) { - tail = b - if !bytes.HasPrefix(b, jsonStart) { - return - } - end := bytes.Index(b, jsonEnd) - if end < 0 { - return - } - b = b[len(jsonStart)-1 : end+1] // drop leading <!-- and include trailing } - if err = json.Unmarshal(b, &meta); err != nil { - return - } - tail = tail[end+len(jsonEnd):] - return -} - -// updateMetadata scans $GOROOT/doc for HTML files, reads their metadata, -// and updates the docMetadata map. -// -func updateMetadata() { - metadata := make(map[string]*Metadata) - var scan func(string) // scan is recursive - scan = func(dir string) { - fis, err := fs.ReadDir(dir) - if err != nil { - log.Println("updateMetadata:", err) - return - } - for _, fi := range fis { - name := pathpkg.Join(dir, fi.Name()) - if fi.IsDir() { - scan(name) // recurse - continue - } - if !strings.HasSuffix(name, ".html") { - continue - } - // Extract metadata from the file. - b, err := ReadFile(fs, name) - if err != nil { - log.Printf("updateMetadata %s: %v", name, err) - continue - } - meta, _, err := extractMetadata(b) - if err != nil { - log.Printf("updateMetadata: %s: %v", name, err) - continue - } - // Store relative filesystem path in Metadata. - meta.filePath = name - if meta.Path == "" { - // If no Path, canonical path is actual path. - meta.Path = meta.filePath - } - // Store under both paths. - metadata[meta.Path] = &meta - metadata[meta.filePath] = &meta - } - } - scan("/doc") - docMetadata.set(metadata) -} - -// Send a value on this channel to trigger a metadata refresh. -// It is buffered so that if a signal is not lost if sent during a refresh. -// -var refreshMetadataSignal = make(chan bool, 1) - -// refreshMetadata sends a signal to update docMetadata. If a refresh is in -// progress the metadata will be refreshed again afterward. -// -func refreshMetadata() { - select { - case refreshMetadataSignal <- true: - default: - } -} - -// refreshMetadataLoop runs forever, updating docMetadata when the underlying -// file system changes. It should be launched in a goroutine by main. -// -func refreshMetadataLoop() { - for { - <-refreshMetadataSignal - updateMetadata() - time.Sleep(10 * time.Second) // at most once every 10 seconds - } -} - -// metadataFor returns the *Metadata for a given relative path or nil if none -// exists. -// -func metadataFor(relpath string) *Metadata { - if m, _ := docMetadata.get(); m != nil { - meta := m.(map[string]*Metadata) - // If metadata for this relpath exists, return it. - if p := meta[relpath]; p != nil { - return p - } - // Try with or without trailing slash. - if strings.HasSuffix(relpath, "/") { - relpath = relpath[:len(relpath)-1] - } else { - relpath = relpath + "/" - } - return meta[relpath] - } - return nil -} - -// ---------------------------------------------------------------------------- -// Indexer - -// invalidateIndex should be called whenever any of the file systems -// under godoc's observation change so that the indexer is kicked on. -// -func invalidateIndex() { - fsModified.set(nil) - refreshMetadata() -} - -// indexUpToDate() returns true if the search index is not older -// than any of the file systems under godoc's observation. -// -func indexUpToDate() bool { - _, fsTime := fsModified.get() - _, siTime := searchIndex.get() - return !fsTime.After(siTime) -} - -// feedDirnames feeds the directory names of all directories -// under the file system given by root to channel c. -// -func feedDirnames(root *RWValue, c chan<- string) { - if dir, _ := root.get(); dir != nil { - for d := range dir.(*Directory).iter(false) { - c <- d.Path - } - } -} - -// fsDirnames() returns a channel sending all directory names -// of all the file systems under godoc's observation. -// -func fsDirnames() <-chan string { - c := make(chan string, 256) // buffered for fewer context switches - go func() { - feedDirnames(&fsTree, c) - close(c) - }() - return c -} - -func readIndex(filenames string) error { - matches, err := filepath.Glob(filenames) - if err != nil { - return err - } else if matches == nil { - return fmt.Errorf("no index files match %q", filenames) - } - sort.Strings(matches) // make sure files are in the right order - files := make([]io.Reader, 0, len(matches)) - for _, filename := range matches { - f, err := os.Open(filename) - if err != nil { - return err - } - defer f.Close() - files = append(files, f) - } - x := new(Index) - if err := x.Read(io.MultiReader(files...)); err != nil { - return err - } - searchIndex.set(x) - return nil -} - -func updateIndex() { - if *verbose { - log.Printf("updating index...") - } - start := time.Now() - index := NewIndex(fsDirnames(), *maxResults > 0, *indexThrottle) - stop := time.Now() - searchIndex.set(index) - if *verbose { - secs := stop.Sub(start).Seconds() - stats := index.Stats() - log.Printf("index updated (%gs, %d bytes of source, %d files, %d lines, %d unique words, %d spots)", - secs, stats.Bytes, stats.Files, stats.Lines, stats.Words, stats.Spots) - } - memstats := new(runtime.MemStats) - runtime.ReadMemStats(memstats) - log.Printf("before GC: bytes = %d footprint = %d", memstats.HeapAlloc, memstats.Sys) - runtime.GC() - runtime.ReadMemStats(memstats) - log.Printf("after GC: bytes = %d footprint = %d", memstats.HeapAlloc, memstats.Sys) -} - -func indexer() { - // initialize the index from disk if possible - if *indexFiles != "" { - if err := readIndex(*indexFiles); err != nil { - log.Printf("error reading index: %s", err) - } - } - - // repeatedly update the index when it goes out of date - for { - if !indexUpToDate() { - // index possibly out of date - make a new one - updateIndex() - } - delay := 60 * time.Second // by default, try every 60s - if *testDir != "" { - // in test mode, try once a second for fast startup - delay = 1 * time.Second - } - time.Sleep(delay) - } -} diff --git a/src/cmd/godoc/index.go b/src/cmd/godoc/index.go deleted file mode 100644 index 8198fca0d..000000000 --- a/src/cmd/godoc/index.go +++ /dev/null @@ -1,1073 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains the infrastructure to create an -// identifier and full-text index for a set of Go files. -// -// Algorithm for identifier index: -// - traverse all .go files of the file tree specified by root -// - for each identifier (word) encountered, collect all occurrences (spots) -// into a list; this produces a list of spots for each word -// - reduce the lists: from a list of spots to a list of FileRuns, -// and from a list of FileRuns into a list of PakRuns -// - make a HitList from the PakRuns -// -// Details: -// - keep two lists per word: one containing package-level declarations -// that have snippets, and one containing all other spots -// - keep the snippets in a separate table indexed by snippet index -// and store the snippet index in place of the line number in a SpotInfo -// (the line number for spots with snippets is stored in the snippet) -// - at the end, create lists of alternative spellings for a given -// word -// -// Algorithm for full text index: -// - concatenate all source code in a byte buffer (in memory) -// - add the files to a file set in lockstep as they are added to the byte -// buffer such that a byte buffer offset corresponds to the Pos value for -// that file location -// - create a suffix array from the concatenated sources -// -// String lookup in full text index: -// - use the suffix array to lookup a string's offsets - the offsets -// correspond to the Pos values relative to the file set -// - translate the Pos values back into file and line information and -// sort the result - -package main - -import ( - "bufio" - "bytes" - "encoding/gob" - "errors" - "go/ast" - "go/parser" - "go/token" - "index/suffixarray" - "io" - "os" - pathpkg "path" - "regexp" - "sort" - "strings" - "time" - "unicode" -) - -// ---------------------------------------------------------------------------- -// InterfaceSlice is a helper type for sorting interface -// slices according to some slice-specific sort criteria. - -type Comparer func(x, y interface{}) bool - -type InterfaceSlice struct { - slice []interface{} - less Comparer -} - -func (p *InterfaceSlice) Len() int { return len(p.slice) } -func (p *InterfaceSlice) Less(i, j int) bool { return p.less(p.slice[i], p.slice[j]) } -func (p *InterfaceSlice) Swap(i, j int) { p.slice[i], p.slice[j] = p.slice[j], p.slice[i] } - -// ---------------------------------------------------------------------------- -// RunList - -// A RunList is a list of entries that can be sorted according to some -// criteria. A RunList may be compressed by grouping "runs" of entries -// which are equal (according to the sort critera) into a new RunList of -// runs. For instance, a RunList containing pairs (x, y) may be compressed -// into a RunList containing pair runs (x, {y}) where each run consists of -// a list of y's with the same x. -type RunList []interface{} - -func (h RunList) sort(less Comparer) { - sort.Sort(&InterfaceSlice{h, less}) -} - -// Compress entries which are the same according to a sort criteria -// (specified by less) into "runs". -func (h RunList) reduce(less Comparer, newRun func(h RunList) interface{}) RunList { - if len(h) == 0 { - return nil - } - // len(h) > 0 - - // create runs of entries with equal values - h.sort(less) - - // for each run, make a new run object and collect them in a new RunList - var hh RunList - i, x := 0, h[0] - for j, y := range h { - if less(x, y) { - hh = append(hh, newRun(h[i:j])) - i, x = j, h[j] // start a new run - } - } - // add final run, if any - if i < len(h) { - hh = append(hh, newRun(h[i:])) - } - - return hh -} - -// ---------------------------------------------------------------------------- -// SpotInfo - -// A SpotInfo value describes a particular identifier spot in a given file; -// It encodes three values: the SpotKind (declaration or use), a line or -// snippet index "lori", and whether it's a line or index. -// -// The following encoding is used: -// -// bits 32 4 1 0 -// value [lori|kind|isIndex] -// -type SpotInfo uint32 - -// SpotKind describes whether an identifier is declared (and what kind of -// declaration) or used. -type SpotKind uint32 - -const ( - PackageClause SpotKind = iota - ImportDecl - ConstDecl - TypeDecl - VarDecl - FuncDecl - MethodDecl - Use - nKinds -) - -func init() { - // sanity check: if nKinds is too large, the SpotInfo - // accessor functions may need to be updated - if nKinds > 8 { - panic("internal error: nKinds > 8") - } -} - -// makeSpotInfo makes a SpotInfo. -func makeSpotInfo(kind SpotKind, lori int, isIndex bool) SpotInfo { - // encode lori: bits [4..32) - x := SpotInfo(lori) << 4 - if int(x>>4) != lori { - // lori value doesn't fit - since snippet indices are - // most certainly always smaller then 1<<28, this can - // only happen for line numbers; give it no line number (= 0) - x = 0 - } - // encode kind: bits [1..4) - x |= SpotInfo(kind) << 1 - // encode isIndex: bit 0 - if isIndex { - x |= 1 - } - return x -} - -func (x SpotInfo) Kind() SpotKind { return SpotKind(x >> 1 & 7) } -func (x SpotInfo) Lori() int { return int(x >> 4) } -func (x SpotInfo) IsIndex() bool { return x&1 != 0 } - -// ---------------------------------------------------------------------------- -// KindRun - -// Debugging support. Disable to see multiple entries per line. -const removeDuplicates = true - -// A KindRun is a run of SpotInfos of the same kind in a given file. -// The kind (3 bits) is stored in each SpotInfo element; to find the -// kind of a KindRun, look at any of it's elements. -type KindRun []SpotInfo - -// KindRuns are sorted by line number or index. Since the isIndex bit -// is always the same for all infos in one list we can compare lori's. -func (k KindRun) Len() int { return len(k) } -func (k KindRun) Less(i, j int) bool { return k[i].Lori() < k[j].Lori() } -func (k KindRun) Swap(i, j int) { k[i], k[j] = k[j], k[i] } - -// FileRun contents are sorted by Kind for the reduction into KindRuns. -func lessKind(x, y interface{}) bool { return x.(SpotInfo).Kind() < y.(SpotInfo).Kind() } - -// newKindRun allocates a new KindRun from the SpotInfo run h. -func newKindRun(h RunList) interface{} { - run := make(KindRun, len(h)) - for i, x := range h { - run[i] = x.(SpotInfo) - } - - // Spots were sorted by file and kind to create this run. - // Within this run, sort them by line number or index. - sort.Sort(run) - - if removeDuplicates { - // Since both the lori and kind field must be - // same for duplicates, and since the isIndex - // bit is always the same for all infos in one - // list we can simply compare the entire info. - k := 0 - prev := SpotInfo(1<<32 - 1) // an unlikely value - for _, x := range run { - if x != prev { - run[k] = x - k++ - prev = x - } - } - run = run[0:k] - } - - return run -} - -// ---------------------------------------------------------------------------- -// FileRun - -// A Pak describes a Go package. -type Pak struct { - Path string // path of directory containing the package - Name string // package name as declared by package clause -} - -// Paks are sorted by name (primary key) and by import path (secondary key). -func (p *Pak) less(q *Pak) bool { - return p.Name < q.Name || p.Name == q.Name && p.Path < q.Path -} - -// A File describes a Go file. -type File struct { - Name string // directory-local file name - Pak *Pak // the package to which the file belongs -} - -// Path returns the file path of f. -func (f *File) Path() string { - return pathpkg.Join(f.Pak.Path, f.Name) -} - -// A Spot describes a single occurrence of a word. -type Spot struct { - File *File - Info SpotInfo -} - -// A FileRun is a list of KindRuns belonging to the same file. -type FileRun struct { - File *File - Groups []KindRun -} - -// Spots are sorted by file path for the reduction into FileRuns. -func lessSpot(x, y interface{}) bool { - fx := x.(Spot).File - fy := y.(Spot).File - // same as "return fx.Path() < fy.Path()" but w/o computing the file path first - px := fx.Pak.Path - py := fy.Pak.Path - return px < py || px == py && fx.Name < fy.Name -} - -// newFileRun allocates a new FileRun from the Spot run h. -func newFileRun(h RunList) interface{} { - file := h[0].(Spot).File - - // reduce the list of Spots into a list of KindRuns - h1 := make(RunList, len(h)) - for i, x := range h { - h1[i] = x.(Spot).Info - } - h2 := h1.reduce(lessKind, newKindRun) - - // create the FileRun - groups := make([]KindRun, len(h2)) - for i, x := range h2 { - groups[i] = x.(KindRun) - } - return &FileRun{file, groups} -} - -// ---------------------------------------------------------------------------- -// PakRun - -// A PakRun describes a run of *FileRuns of a package. -type PakRun struct { - Pak *Pak - Files []*FileRun -} - -// Sorting support for files within a PakRun. -func (p *PakRun) Len() int { return len(p.Files) } -func (p *PakRun) Less(i, j int) bool { return p.Files[i].File.Name < p.Files[j].File.Name } -func (p *PakRun) Swap(i, j int) { p.Files[i], p.Files[j] = p.Files[j], p.Files[i] } - -// FileRuns are sorted by package for the reduction into PakRuns. -func lessFileRun(x, y interface{}) bool { - return x.(*FileRun).File.Pak.less(y.(*FileRun).File.Pak) -} - -// newPakRun allocates a new PakRun from the *FileRun run h. -func newPakRun(h RunList) interface{} { - pak := h[0].(*FileRun).File.Pak - files := make([]*FileRun, len(h)) - for i, x := range h { - files[i] = x.(*FileRun) - } - run := &PakRun{pak, files} - sort.Sort(run) // files were sorted by package; sort them by file now - return run -} - -// ---------------------------------------------------------------------------- -// HitList - -// A HitList describes a list of PakRuns. -type HitList []*PakRun - -// PakRuns are sorted by package. -func lessPakRun(x, y interface{}) bool { return x.(*PakRun).Pak.less(y.(*PakRun).Pak) } - -func reduce(h0 RunList) HitList { - // reduce a list of Spots into a list of FileRuns - h1 := h0.reduce(lessSpot, newFileRun) - // reduce a list of FileRuns into a list of PakRuns - h2 := h1.reduce(lessFileRun, newPakRun) - // sort the list of PakRuns by package - h2.sort(lessPakRun) - // create a HitList - h := make(HitList, len(h2)) - for i, p := range h2 { - h[i] = p.(*PakRun) - } - return h -} - -// filter returns a new HitList created by filtering -// all PakRuns from h that have a matching pakname. -func (h HitList) filter(pakname string) HitList { - var hh HitList - for _, p := range h { - if p.Pak.Name == pakname { - hh = append(hh, p) - } - } - return hh -} - -// ---------------------------------------------------------------------------- -// AltWords - -type wordPair struct { - canon string // canonical word spelling (all lowercase) - alt string // alternative spelling -} - -// An AltWords describes a list of alternative spellings for a -// canonical (all lowercase) spelling of a word. -type AltWords struct { - Canon string // canonical word spelling (all lowercase) - Alts []string // alternative spelling for the same word -} - -// wordPairs are sorted by their canonical spelling. -func lessWordPair(x, y interface{}) bool { return x.(*wordPair).canon < y.(*wordPair).canon } - -// newAltWords allocates a new AltWords from the *wordPair run h. -func newAltWords(h RunList) interface{} { - canon := h[0].(*wordPair).canon - alts := make([]string, len(h)) - for i, x := range h { - alts[i] = x.(*wordPair).alt - } - return &AltWords{canon, alts} -} - -func (a *AltWords) filter(s string) *AltWords { - var alts []string - for _, w := range a.Alts { - if w != s { - alts = append(alts, w) - } - } - if len(alts) > 0 { - return &AltWords{a.Canon, alts} - } - return nil -} - -// ---------------------------------------------------------------------------- -// Indexer - -// Adjust these flags as seems best. -const includeMainPackages = true -const includeTestFiles = true - -type IndexResult struct { - Decls RunList // package-level declarations (with snippets) - Others RunList // all other occurrences -} - -// Statistics provides statistics information for an index. -type Statistics struct { - Bytes int // total size of indexed source files - Files int // number of indexed source files - Lines int // number of lines (all files) - Words int // number of different identifiers - Spots int // number of identifier occurrences -} - -// An Indexer maintains the data structures and provides the machinery -// for indexing .go files under a file tree. It implements the path.Visitor -// interface for walking file trees, and the ast.Visitor interface for -// walking Go ASTs. -type Indexer struct { - fset *token.FileSet // file set for all indexed files - sources bytes.Buffer // concatenated sources - packages map[string]*Pak // map of canonicalized *Paks - words map[string]*IndexResult // RunLists of Spots - snippets []*Snippet // indices are stored in SpotInfos - current *token.File // last file added to file set - file *File // AST for current file - decl ast.Decl // AST for current decl - stats Statistics -} - -func (x *Indexer) lookupPackage(path, name string) *Pak { - // In the source directory tree, more than one package may - // live in the same directory. For the packages map, construct - // a key that includes both the directory path and the package - // name. - key := path + ":" + name - pak := x.packages[key] - if pak == nil { - pak = &Pak{path, name} - x.packages[key] = pak - } - return pak -} - -func (x *Indexer) addSnippet(s *Snippet) int { - index := len(x.snippets) - x.snippets = append(x.snippets, s) - return index -} - -func (x *Indexer) visitIdent(kind SpotKind, id *ast.Ident) { - if id != nil { - lists, found := x.words[id.Name] - if !found { - lists = new(IndexResult) - x.words[id.Name] = lists - } - - if kind == Use || x.decl == nil { - // not a declaration or no snippet required - info := makeSpotInfo(kind, x.current.Line(id.Pos()), false) - lists.Others = append(lists.Others, Spot{x.file, info}) - } else { - // a declaration with snippet - index := x.addSnippet(NewSnippet(x.fset, x.decl, id)) - info := makeSpotInfo(kind, index, true) - lists.Decls = append(lists.Decls, Spot{x.file, info}) - } - - x.stats.Spots++ - } -} - -func (x *Indexer) visitFieldList(kind SpotKind, list *ast.FieldList) { - for _, f := range list.List { - x.decl = nil // no snippets for fields - for _, name := range f.Names { - x.visitIdent(kind, name) - } - ast.Walk(x, f.Type) - // ignore tag - not indexed at the moment - } -} - -func (x *Indexer) visitSpec(kind SpotKind, spec ast.Spec) { - switch n := spec.(type) { - case *ast.ImportSpec: - x.visitIdent(ImportDecl, n.Name) - // ignore path - not indexed at the moment - - case *ast.ValueSpec: - for _, n := range n.Names { - x.visitIdent(kind, n) - } - ast.Walk(x, n.Type) - for _, v := range n.Values { - ast.Walk(x, v) - } - - case *ast.TypeSpec: - x.visitIdent(TypeDecl, n.Name) - ast.Walk(x, n.Type) - } -} - -func (x *Indexer) visitGenDecl(decl *ast.GenDecl) { - kind := VarDecl - if decl.Tok == token.CONST { - kind = ConstDecl - } - x.decl = decl - for _, s := range decl.Specs { - x.visitSpec(kind, s) - } -} - -func (x *Indexer) Visit(node ast.Node) ast.Visitor { - switch n := node.(type) { - case nil: - // nothing to do - - case *ast.Ident: - x.visitIdent(Use, n) - - case *ast.FieldList: - x.visitFieldList(VarDecl, n) - - case *ast.InterfaceType: - x.visitFieldList(MethodDecl, n.Methods) - - case *ast.DeclStmt: - // local declarations should only be *ast.GenDecls; - // ignore incorrect ASTs - if decl, ok := n.Decl.(*ast.GenDecl); ok { - x.decl = nil // no snippets for local declarations - x.visitGenDecl(decl) - } - - case *ast.GenDecl: - x.decl = n - x.visitGenDecl(n) - - case *ast.FuncDecl: - kind := FuncDecl - if n.Recv != nil { - kind = MethodDecl - ast.Walk(x, n.Recv) - } - x.decl = n - x.visitIdent(kind, n.Name) - ast.Walk(x, n.Type) - if n.Body != nil { - ast.Walk(x, n.Body) - } - - case *ast.File: - x.decl = nil - x.visitIdent(PackageClause, n.Name) - for _, d := range n.Decls { - ast.Walk(x, d) - } - - default: - return x - } - - return nil -} - -func pkgName(filename string) string { - // use a new file set each time in order to not pollute the indexer's - // file set (which must stay in sync with the concatenated source code) - file, err := parser.ParseFile(token.NewFileSet(), filename, nil, parser.PackageClauseOnly) - if err != nil || file == nil { - return "" - } - return file.Name.Name -} - -// addFile adds a file to the index if possible and returns the file set file -// and the file's AST if it was successfully parsed as a Go file. If addFile -// failed (that is, if the file was not added), it returns file == nil. -func (x *Indexer) addFile(filename string, goFile bool) (file *token.File, ast *ast.File) { - // open file - f, err := fs.Open(filename) - if err != nil { - return - } - defer f.Close() - - // The file set's base offset and x.sources size must be in lock-step; - // this permits the direct mapping of suffix array lookup results to - // to corresponding Pos values. - // - // When a file is added to the file set, its offset base increases by - // the size of the file + 1; and the initial base offset is 1. Add an - // extra byte to the sources here. - x.sources.WriteByte(0) - - // If the sources length doesn't match the file set base at this point - // the file set implementation changed or we have another error. - base := x.fset.Base() - if x.sources.Len() != base { - panic("internal error: file base incorrect") - } - - // append file contents (src) to x.sources - if _, err := x.sources.ReadFrom(f); err == nil { - src := x.sources.Bytes()[base:] - - if goFile { - // parse the file and in the process add it to the file set - if ast, err = parser.ParseFile(x.fset, filename, src, parser.ParseComments); err == nil { - file = x.fset.File(ast.Pos()) // ast.Pos() is inside the file - return - } - // file has parse errors, and the AST may be incorrect - - // set lines information explicitly and index as ordinary - // text file (cannot fall through to the text case below - // because the file has already been added to the file set - // by the parser) - file = x.fset.File(token.Pos(base)) // token.Pos(base) is inside the file - file.SetLinesForContent(src) - ast = nil - return - } - - if isText(src) { - // only add the file to the file set (for the full text index) - file = x.fset.AddFile(filename, x.fset.Base(), len(src)) - file.SetLinesForContent(src) - return - } - } - - // discard possibly added data - x.sources.Truncate(base - 1) // -1 to remove added byte 0 since no file was added - return -} - -// Design note: Using an explicit white list of permitted files for indexing -// makes sure that the important files are included and massively reduces the -// number of files to index. The advantage over a blacklist is that unexpected -// (non-blacklisted) files won't suddenly explode the index. - -// Files are whitelisted if they have a file name or extension -// present as key in whitelisted. -var whitelisted = map[string]bool{ - ".bash": true, - ".c": true, - ".css": true, - ".go": true, - ".goc": true, - ".h": true, - ".html": true, - ".js": true, - ".out": true, - ".py": true, - ".s": true, - ".sh": true, - ".txt": true, - ".xml": true, - "AUTHORS": true, - "CONTRIBUTORS": true, - "LICENSE": true, - "Makefile": true, - "PATENTS": true, - "README": true, -} - -// isWhitelisted returns true if a file is on the list -// of "permitted" files for indexing. The filename must -// be the directory-local name of the file. -func isWhitelisted(filename string) bool { - key := pathpkg.Ext(filename) - if key == "" { - // file has no extension - use entire filename - key = filename - } - return whitelisted[key] -} - -func (x *Indexer) visitFile(dirname string, f os.FileInfo, fulltextIndex bool) { - if f.IsDir() { - return - } - - filename := pathpkg.Join(dirname, f.Name()) - goFile := false - - switch { - case isGoFile(f): - if !includeTestFiles && (!isPkgFile(f) || strings.HasPrefix(filename, "test/")) { - return - } - if !includeMainPackages && pkgName(filename) == "main" { - return - } - goFile = true - - case !fulltextIndex || !isWhitelisted(f.Name()): - return - } - - file, fast := x.addFile(filename, goFile) - if file == nil { - return // addFile failed - } - - if fast != nil { - // we've got a Go file to index - x.current = file - pak := x.lookupPackage(dirname, fast.Name.Name) - x.file = &File{f.Name(), pak} - ast.Walk(x, fast) - } - - // update statistics - x.stats.Bytes += file.Size() - x.stats.Files++ - x.stats.Lines += file.LineCount() -} - -// ---------------------------------------------------------------------------- -// Index - -type LookupResult struct { - Decls HitList // package-level declarations (with snippets) - Others HitList // all other occurrences -} - -type Index struct { - fset *token.FileSet // file set used during indexing; nil if no textindex - suffixes *suffixarray.Index // suffixes for concatenated sources; nil if no textindex - words map[string]*LookupResult // maps words to hit lists - alts map[string]*AltWords // maps canonical(words) to lists of alternative spellings - snippets []*Snippet // all snippets, indexed by snippet index - stats Statistics -} - -func canonical(w string) string { return strings.ToLower(w) } - -// NewIndex creates a new index for the .go files -// in the directories given by dirnames. -// -func NewIndex(dirnames <-chan string, fulltextIndex bool, throttle float64) *Index { - var x Indexer - th := NewThrottle(throttle, 100*time.Millisecond) // run at least 0.1s at a time - - // initialize Indexer - // (use some reasonably sized maps to start) - x.fset = token.NewFileSet() - x.packages = make(map[string]*Pak, 256) - x.words = make(map[string]*IndexResult, 8192) - - // index all files in the directories given by dirnames - for dirname := range dirnames { - list, err := fs.ReadDir(dirname) - if err != nil { - continue // ignore this directory - } - for _, f := range list { - if !f.IsDir() { - x.visitFile(dirname, f, fulltextIndex) - } - th.Throttle() - } - } - - if !fulltextIndex { - // the file set, the current file, and the sources are - // not needed after indexing if no text index is built - - // help GC and clear them - x.fset = nil - x.sources.Reset() - x.current = nil // contains reference to fset! - } - - // for each word, reduce the RunLists into a LookupResult; - // also collect the word with its canonical spelling in a - // word list for later computation of alternative spellings - words := make(map[string]*LookupResult) - var wlist RunList - for w, h := range x.words { - decls := reduce(h.Decls) - others := reduce(h.Others) - words[w] = &LookupResult{ - Decls: decls, - Others: others, - } - wlist = append(wlist, &wordPair{canonical(w), w}) - th.Throttle() - } - x.stats.Words = len(words) - - // reduce the word list {canonical(w), w} into - // a list of AltWords runs {canonical(w), {w}} - alist := wlist.reduce(lessWordPair, newAltWords) - - // convert alist into a map of alternative spellings - alts := make(map[string]*AltWords) - for i := 0; i < len(alist); i++ { - a := alist[i].(*AltWords) - alts[a.Canon] = a - } - - // create text index - var suffixes *suffixarray.Index - if fulltextIndex { - suffixes = suffixarray.New(x.sources.Bytes()) - } - - return &Index{x.fset, suffixes, words, alts, x.snippets, x.stats} -} - -type fileIndex struct { - Words map[string]*LookupResult - Alts map[string]*AltWords - Snippets []*Snippet - Fulltext bool -} - -func (x *fileIndex) Write(w io.Writer) error { - return gob.NewEncoder(w).Encode(x) -} - -func (x *fileIndex) Read(r io.Reader) error { - return gob.NewDecoder(r).Decode(x) -} - -// Write writes the index x to w. -func (x *Index) Write(w io.Writer) error { - fulltext := false - if x.suffixes != nil { - fulltext = true - } - fx := fileIndex{ - x.words, - x.alts, - x.snippets, - fulltext, - } - if err := fx.Write(w); err != nil { - return err - } - if fulltext { - encode := func(x interface{}) error { - return gob.NewEncoder(w).Encode(x) - } - if err := x.fset.Write(encode); err != nil { - return err - } - if err := x.suffixes.Write(w); err != nil { - return err - } - } - return nil -} - -// Read reads the index from r into x; x must not be nil. -// If r does not also implement io.ByteReader, it will be wrapped in a bufio.Reader. -func (x *Index) Read(r io.Reader) error { - // We use the ability to read bytes as a plausible surrogate for buffering. - if _, ok := r.(io.ByteReader); !ok { - r = bufio.NewReader(r) - } - var fx fileIndex - if err := fx.Read(r); err != nil { - return err - } - x.words = fx.Words - x.alts = fx.Alts - x.snippets = fx.Snippets - if fx.Fulltext { - x.fset = token.NewFileSet() - decode := func(x interface{}) error { - return gob.NewDecoder(r).Decode(x) - } - if err := x.fset.Read(decode); err != nil { - return err - } - x.suffixes = new(suffixarray.Index) - if err := x.suffixes.Read(r); err != nil { - return err - } - } - return nil -} - -// Stats() returns index statistics. -func (x *Index) Stats() Statistics { - return x.stats -} - -func (x *Index) lookupWord(w string) (match *LookupResult, alt *AltWords) { - match = x.words[w] - alt = x.alts[canonical(w)] - // remove current spelling from alternatives - // (if there is no match, the alternatives do - // not contain the current spelling) - if match != nil && alt != nil { - alt = alt.filter(w) - } - return -} - -// isIdentifier reports whether s is a Go identifier. -func isIdentifier(s string) bool { - for i, ch := range s { - if unicode.IsLetter(ch) || ch == ' ' || i > 0 && unicode.IsDigit(ch) { - continue - } - return false - } - return len(s) > 0 -} - -// For a given query, which is either a single identifier or a qualified -// identifier, Lookup returns a list of packages, a LookupResult, and a -// list of alternative spellings, if any. Any and all results may be nil. -// If the query syntax is wrong, an error is reported. -func (x *Index) Lookup(query string) (paks HitList, match *LookupResult, alt *AltWords, err error) { - ss := strings.Split(query, ".") - - // check query syntax - for _, s := range ss { - if !isIdentifier(s) { - err = errors.New("all query parts must be identifiers") - return - } - } - - // handle simple and qualified identifiers - switch len(ss) { - case 1: - ident := ss[0] - match, alt = x.lookupWord(ident) - if match != nil { - // found a match - filter packages with same name - // for the list of packages called ident, if any - paks = match.Others.filter(ident) - } - - case 2: - pakname, ident := ss[0], ss[1] - match, alt = x.lookupWord(ident) - if match != nil { - // found a match - filter by package name - // (no paks - package names are not qualified) - decls := match.Decls.filter(pakname) - others := match.Others.filter(pakname) - match = &LookupResult{decls, others} - } - - default: - err = errors.New("query is not a (qualified) identifier") - } - - return -} - -func (x *Index) Snippet(i int) *Snippet { - // handle illegal snippet indices gracefully - if 0 <= i && i < len(x.snippets) { - return x.snippets[i] - } - return nil -} - -type positionList []struct { - filename string - line int -} - -func (list positionList) Len() int { return len(list) } -func (list positionList) Less(i, j int) bool { return list[i].filename < list[j].filename } -func (list positionList) Swap(i, j int) { list[i], list[j] = list[j], list[i] } - -// unique returns the list sorted and with duplicate entries removed -func unique(list []int) []int { - sort.Ints(list) - var last int - i := 0 - for _, x := range list { - if i == 0 || x != last { - last = x - list[i] = x - i++ - } - } - return list[0:i] -} - -// A FileLines value specifies a file and line numbers within that file. -type FileLines struct { - Filename string - Lines []int -} - -// LookupRegexp returns the number of matches and the matches where a regular -// expression r is found in the full text index. At most n matches are -// returned (thus found <= n). -// -func (x *Index) LookupRegexp(r *regexp.Regexp, n int) (found int, result []FileLines) { - if x.suffixes == nil || n <= 0 { - return - } - // n > 0 - - var list positionList - // FindAllIndex may returns matches that span across file boundaries. - // Such matches are unlikely, buf after eliminating them we may end up - // with fewer than n matches. If we don't have enough at the end, redo - // the search with an increased value n1, but only if FindAllIndex - // returned all the requested matches in the first place (if it - // returned fewer than that there cannot be more). - for n1 := n; found < n; n1 += n - found { - found = 0 - matches := x.suffixes.FindAllIndex(r, n1) - // compute files, exclude matches that span file boundaries, - // and map offsets to file-local offsets - list = make(positionList, len(matches)) - for _, m := range matches { - // by construction, an offset corresponds to the Pos value - // for the file set - use it to get the file and line - p := token.Pos(m[0]) - if file := x.fset.File(p); file != nil { - if base := file.Base(); base <= m[1] && m[1] <= base+file.Size() { - // match [m[0], m[1]) is within the file boundaries - list[found].filename = file.Name() - list[found].line = file.Line(p) - found++ - } - } - } - if found == n || len(matches) < n1 { - // found all matches or there's no chance to find more - break - } - } - list = list[0:found] - sort.Sort(list) // sort by filename - - // collect matches belonging to the same file - var last string - var lines []int - addLines := func() { - if len(lines) > 0 { - // remove duplicate lines - result = append(result, FileLines{last, unique(lines)}) - lines = nil - } - } - for _, m := range list { - if m.filename != last { - addLines() - last = m.filename - } - lines = append(lines, m.line) - } - addLines() - - return -} diff --git a/src/cmd/godoc/linkify.go b/src/cmd/godoc/linkify.go deleted file mode 100644 index 5b4862419..000000000 --- a/src/cmd/godoc/linkify.go +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file implements LinkifyText which introduces -// links for identifiers pointing to their declarations. -// The approach does not cover all cases because godoc -// doesn't have complete type information, but it's -// reasonably good for browsing. - -package main - -import ( - "fmt" - "go/ast" - "go/token" - "io" - "strconv" -) - -// LinkifyText HTML-escapes source text and writes it to w. -// Identifiers that are in a "use" position (i.e., that are -// not being declared), are wrapped with HTML links pointing -// to the respective declaration, if possible. Comments are -// formatted the same way as with FormatText. -// -func LinkifyText(w io.Writer, text []byte, n ast.Node) { - links := linksFor(n) - - i := 0 // links index - prev := "" // prev HTML tag - linkWriter := func(w io.Writer, _ int, start bool) { - // end tag - if !start { - if prev != "" { - fmt.Fprintf(w, `</%s>`, prev) - prev = "" - } - return - } - - // start tag - prev = "" - if i < len(links) { - switch info := links[i]; { - case info.path != "" && info.name == "": - // package path - fmt.Fprintf(w, `<a href="/pkg/%s/">`, info.path) - prev = "a" - case info.path != "" && info.name != "": - // qualified identifier - fmt.Fprintf(w, `<a href="/pkg/%s/#%s">`, info.path, info.name) - prev = "a" - case info.path == "" && info.name != "": - // local identifier - if info.mode == identVal { - fmt.Fprintf(w, `<span id="%s">`, info.name) - prev = "span" - } else { - fmt.Fprintf(w, `<a href="#%s">`, info.name) - prev = "a" - } - } - i++ - } - } - - idents := tokenSelection(text, token.IDENT) - comments := tokenSelection(text, token.COMMENT) - FormatSelections(w, text, linkWriter, idents, selectionTag, comments) -} - -// A link describes the (HTML) link information for an identifier. -// The zero value of a link represents "no link". -// -type link struct { - mode identMode - path, name string // package path, identifier name -} - -// linksFor returns the list of links for the identifiers used -// by node in the same order as they appear in the source. -// -func linksFor(node ast.Node) (list []link) { - modes := identModesFor(node) - - // NOTE: We are expecting ast.Inspect to call the - // callback function in source text order. - ast.Inspect(node, func(node ast.Node) bool { - switch n := node.(type) { - case *ast.Ident: - m := modes[n] - info := link{mode: m} - switch m { - case identUse: - if n.Obj == nil && predeclared[n.Name] { - info.path = builtinPkgPath - } - info.name = n.Name - case identDef: - // any declaration expect const or var - empty link - case identVal: - // const or var declaration - info.name = n.Name - } - list = append(list, info) - return false - case *ast.SelectorExpr: - // Detect qualified identifiers of the form pkg.ident. - // If anything fails we return true and collect individual - // identifiers instead. - if x, _ := n.X.(*ast.Ident); x != nil { - // x must be a package for a qualified identifier - if obj := x.Obj; obj != nil && obj.Kind == ast.Pkg { - if spec, _ := obj.Decl.(*ast.ImportSpec); spec != nil { - // spec.Path.Value is the import path - if path, err := strconv.Unquote(spec.Path.Value); err == nil { - // Register two links, one for the package - // and one for the qualified identifier. - info := link{path: path} - list = append(list, info) - info.name = n.Sel.Name - list = append(list, info) - return false - } - } - } - } - } - return true - }) - - return -} - -// The identMode describes how an identifier is "used" at its source location. -type identMode int - -const ( - identUse identMode = iota // identifier is used (must be zero value for identMode) - identDef // identifier is defined - identVal // identifier is defined in a const or var declaration -) - -// identModesFor returns a map providing the identMode for each identifier used by node. -func identModesFor(node ast.Node) map[*ast.Ident]identMode { - m := make(map[*ast.Ident]identMode) - - ast.Inspect(node, func(node ast.Node) bool { - switch n := node.(type) { - case *ast.Field: - for _, n := range n.Names { - m[n] = identDef - } - case *ast.ImportSpec: - if name := n.Name; name != nil { - m[name] = identDef - } - case *ast.ValueSpec: - for _, n := range n.Names { - m[n] = identVal - } - case *ast.TypeSpec: - m[n.Name] = identDef - case *ast.FuncDecl: - m[n.Name] = identDef - case *ast.AssignStmt: - // Short variable declarations only show up if we apply - // this code to all source code (as opposed to exported - // declarations only). - if n.Tok == token.DEFINE { - // Some of the lhs variables may be re-declared, - // so technically they are not defs. We don't - // care for now. - for _, x := range n.Lhs { - // Each lhs expression should be an - // ident, but we are conservative and check. - if n, _ := x.(*ast.Ident); n != nil { - m[n] = identVal - } - } - } - } - return true - }) - - return m -} - -// The predeclared map represents the set of all predeclared identifiers. -// TODO(gri) This information is also encoded in similar maps in go/doc, -// but not exported. Consider exporting an accessor and using -// it instead. -var predeclared = map[string]bool{ - "bool": true, - "byte": true, - "complex64": true, - "complex128": true, - "error": true, - "float32": true, - "float64": true, - "int": true, - "int8": true, - "int16": true, - "int32": true, - "int64": true, - "rune": true, - "string": true, - "uint": true, - "uint8": true, - "uint16": true, - "uint32": true, - "uint64": true, - "uintptr": true, - "true": true, - "false": true, - "iota": true, - "nil": true, - "append": true, - "cap": true, - "close": true, - "complex": true, - "copy": true, - "delete": true, - "imag": true, - "len": true, - "make": true, - "new": true, - "panic": true, - "print": true, - "println": true, - "real": true, - "recover": true, -} diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go deleted file mode 100644 index 81e739d20..000000000 --- a/src/cmd/godoc/main.go +++ /dev/null @@ -1,470 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// godoc: Go Documentation Server - -// Web server tree: -// -// http://godoc/ main landing page -// http://godoc/doc/ serve from $GOROOT/doc - spec, mem, etc. -// http://godoc/src/ serve files from $GOROOT/src; .go gets pretty-printed -// http://godoc/cmd/ serve documentation about commands -// http://godoc/pkg/ serve documentation about packages -// (idea is if you say import "compress/zlib", you go to -// http://godoc/pkg/compress/zlib) -// -// Command-line interface: -// -// godoc packagepath [name ...] -// -// godoc compress/zlib -// - prints doc for package compress/zlib -// godoc crypto/block Cipher NewCMAC -// - prints doc for Cipher and NewCMAC in package crypto/block - -// +build !appengine - -package main - -import ( - "archive/zip" - "bytes" - "errors" - _ "expvar" // to serve /debug/vars - "flag" - "fmt" - "go/ast" - "go/build" - "go/printer" - "io" - "log" - "net/http" - _ "net/http/pprof" // to serve /debug/pprof/* - "net/url" - "os" - pathpkg "path" - "path/filepath" - "regexp" - "runtime" - "strings" -) - -const defaultAddr = ":6060" // default webserver address - -var ( - // file system to serve - // (with e.g.: zip -r go.zip $GOROOT -i \*.go -i \*.html -i \*.css -i \*.js -i \*.txt -i \*.c -i \*.h -i \*.s -i \*.png -i \*.jpg -i \*.sh -i favicon.ico) - zipfile = flag.String("zip", "", "zip file providing the file system to serve; disabled if empty") - - // file-based index - writeIndex = flag.Bool("write_index", false, "write index to a file; the file name must be specified with -index_files") - - // network - httpAddr = flag.String("http", "", "HTTP service address (e.g., '"+defaultAddr+"')") - serverAddr = flag.String("server", "", "webserver address for command line searches") - - // layout control - html = flag.Bool("html", false, "print HTML in command-line mode") - srcMode = flag.Bool("src", false, "print (exported) source in command-line mode") - urlFlag = flag.String("url", "", "print HTML for named URL") - - // command-line searches - query = flag.Bool("q", false, "arguments are considered search queries") -) - -func serveError(w http.ResponseWriter, r *http.Request, relpath string, err error) { - w.WriteHeader(http.StatusNotFound) - servePage(w, Page{ - Title: "File " + relpath, - Subtitle: relpath, - Body: applyTemplate(errorHTML, "errorHTML", err), // err may contain an absolute path! - }) -} - -func usage() { - fmt.Fprintf(os.Stderr, - "usage: godoc package [name ...]\n"+ - " godoc -http="+defaultAddr+"\n") - flag.PrintDefaults() - os.Exit(2) -} - -func loggingHandler(h http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - log.Printf("%s\t%s", req.RemoteAddr, req.URL) - h.ServeHTTP(w, req) - }) -} - -func remoteSearch(query string) (res *http.Response, err error) { - // list of addresses to try - var addrs []string - if *serverAddr != "" { - // explicit server address - only try this one - addrs = []string{*serverAddr} - } else { - addrs = []string{ - defaultAddr, - "golang.org", - } - } - - // remote search - search := remoteSearchURL(query, *html) - for _, addr := range addrs { - url := "http://" + addr + search - res, err = http.Get(url) - if err == nil && res.StatusCode == http.StatusOK { - break - } - } - - if err == nil && res.StatusCode != http.StatusOK { - err = errors.New(res.Status) - } - - return -} - -// Does s look like a regular expression? -func isRegexp(s string) bool { - return strings.IndexAny(s, ".(|)*+?^$[]") >= 0 -} - -// Make a regular expression of the form -// names[0]|names[1]|...names[len(names)-1]. -// Returns nil if the regular expression is illegal. -func makeRx(names []string) (rx *regexp.Regexp) { - if len(names) > 0 { - s := "" - for i, name := range names { - if i > 0 { - s += "|" - } - if isRegexp(name) { - s += name - } else { - s += "^" + name + "$" // must match exactly - } - } - rx, _ = regexp.Compile(s) // rx is nil if there's a compilation error - } - return -} - -func main() { - flag.Usage = usage - flag.Parse() - - // Check usage: either server and no args, command line and args, or index creation mode - if (*httpAddr != "" || *urlFlag != "") != (flag.NArg() == 0) && !*writeIndex { - usage() - } - - if *tabwidth < 0 { - log.Fatalf("negative tabwidth %d", *tabwidth) - } - - // Determine file system to use. - // TODO(gri) - fs and fsHttp should really be the same. Try to unify. - // - fsHttp doesn't need to be set up in command-line mode, - // same is true for the http handlers in initHandlers. - if *zipfile == "" { - // use file system of underlying OS - fs.Bind("/", OS(*goroot), "/", bindReplace) - if *templateDir != "" { - fs.Bind("/lib/godoc", OS(*templateDir), "/", bindBefore) - } - } else { - // use file system specified via .zip file (path separator must be '/') - rc, err := zip.OpenReader(*zipfile) - if err != nil { - log.Fatalf("%s: %s\n", *zipfile, err) - } - defer rc.Close() // be nice (e.g., -writeIndex mode) - fs.Bind("/", NewZipFS(rc, *zipfile), *goroot, bindReplace) - } - - // Bind $GOPATH trees into Go root. - for _, p := range filepath.SplitList(build.Default.GOPATH) { - fs.Bind("/src/pkg", OS(p), "/src", bindAfter) - } - - readTemplates() - initHandlers() - - if *writeIndex { - // Write search index and exit. - if *indexFiles == "" { - log.Fatal("no index file specified") - } - - log.Println("initialize file systems") - *verbose = true // want to see what happens - initFSTree() - - *indexThrottle = 1 - updateIndex() - - log.Println("writing index file", *indexFiles) - f, err := os.Create(*indexFiles) - if err != nil { - log.Fatal(err) - } - index, _ := searchIndex.get() - err = index.(*Index).Write(f) - if err != nil { - log.Fatal(err) - } - - log.Println("done") - return - } - - // Print content that would be served at the URL *urlFlag. - if *urlFlag != "" { - registerPublicHandlers(http.DefaultServeMux) - initFSTree() - updateMetadata() - // Try up to 10 fetches, following redirects. - urlstr := *urlFlag - for i := 0; i < 10; i++ { - // Prepare request. - u, err := url.Parse(urlstr) - if err != nil { - log.Fatal(err) - } - req := &http.Request{ - URL: u, - } - - // Invoke default HTTP handler to serve request - // to our buffering httpWriter. - w := &httpWriter{h: http.Header{}, code: 200} - http.DefaultServeMux.ServeHTTP(w, req) - - // Return data, error, or follow redirect. - switch w.code { - case 200: // ok - os.Stdout.Write(w.Bytes()) - return - case 301, 302, 303, 307: // redirect - redirect := w.h.Get("Location") - if redirect == "" { - log.Fatalf("HTTP %d without Location header", w.code) - } - urlstr = redirect - default: - log.Fatalf("HTTP error %d", w.code) - } - } - log.Fatalf("too many redirects") - } - - if *httpAddr != "" { - // HTTP server mode. - var handler http.Handler = http.DefaultServeMux - if *verbose { - log.Printf("Go Documentation Server") - log.Printf("version = %s", runtime.Version()) - log.Printf("address = %s", *httpAddr) - log.Printf("goroot = %s", *goroot) - log.Printf("tabwidth = %d", *tabwidth) - switch { - case !*indexEnabled: - log.Print("search index disabled") - case *maxResults > 0: - log.Printf("full text index enabled (maxresults = %d)", *maxResults) - default: - log.Print("identifier search index enabled") - } - fs.Fprint(os.Stderr) - handler = loggingHandler(handler) - } - - registerPublicHandlers(http.DefaultServeMux) - registerPlaygroundHandlers(http.DefaultServeMux) - - // Initialize default directory tree with corresponding timestamp. - // (Do it in a goroutine so that launch is quick.) - go initFSTree() - - // Immediately update metadata. - updateMetadata() - // Periodically refresh metadata. - go refreshMetadataLoop() - - // Initialize search index. - if *indexEnabled { - go indexer() - } - - // Start http server. - if err := http.ListenAndServe(*httpAddr, handler); err != nil { - log.Fatalf("ListenAndServe %s: %v", *httpAddr, err) - } - - return - } - - // Command line mode. - if *html { - packageText = packageHTML - searchText = packageHTML - } - - if *query { - // Command-line queries. - for i := 0; i < flag.NArg(); i++ { - res, err := remoteSearch(flag.Arg(i)) - if err != nil { - log.Fatalf("remoteSearch: %s", err) - } - io.Copy(os.Stdout, res.Body) - } - return - } - - // Determine paths. - // - // If we are passed an operating system path like . or ./foo or /foo/bar or c:\mysrc, - // we need to map that path somewhere in the fs name space so that routines - // like getPageInfo will see it. We use the arbitrarily-chosen virtual path "/target" - // for this. That is, if we get passed a directory like the above, we map that - // directory so that getPageInfo sees it as /target. - const target = "/target" - const cmdPrefix = "cmd/" - path := flag.Arg(0) - var forceCmd bool - var abspath, relpath string - if filepath.IsAbs(path) { - fs.Bind(target, OS(path), "/", bindReplace) - abspath = target - } else if build.IsLocalImport(path) { - cwd, _ := os.Getwd() // ignore errors - path = filepath.Join(cwd, path) - fs.Bind(target, OS(path), "/", bindReplace) - abspath = target - } else if strings.HasPrefix(path, cmdPrefix) { - path = strings.TrimPrefix(path, cmdPrefix) - forceCmd = true - } else if bp, _ := build.Import(path, "", build.FindOnly); bp.Dir != "" && bp.ImportPath != "" { - fs.Bind(target, OS(bp.Dir), "/", bindReplace) - abspath = target - relpath = bp.ImportPath - } else { - abspath = pathpkg.Join(pkgHandler.fsRoot, path) - } - if relpath == "" { - relpath = abspath - } - - var mode PageInfoMode - if relpath == builtinPkgPath { - // the fake built-in package contains unexported identifiers - mode = noFiltering - } - if *srcMode { - // only filter exports if we don't have explicit command-line filter arguments - if flag.NArg() > 1 { - mode |= noFiltering - } - mode |= showSource - } - - // first, try as package unless forced as command - var info *PageInfo - if !forceCmd { - info = pkgHandler.getPageInfo(abspath, relpath, mode) - } - - // second, try as command unless the path is absolute - // (the go command invokes godoc w/ absolute paths; don't override) - var cinfo *PageInfo - if !filepath.IsAbs(path) { - abspath = pathpkg.Join(cmdHandler.fsRoot, path) - cinfo = cmdHandler.getPageInfo(abspath, relpath, mode) - } - - // determine what to use - if info == nil || info.IsEmpty() { - if cinfo != nil && !cinfo.IsEmpty() { - // only cinfo exists - switch to cinfo - info = cinfo - } - } else if cinfo != nil && !cinfo.IsEmpty() { - // both info and cinfo exist - use cinfo if info - // contains only subdirectory information - if info.PAst == nil && info.PDoc == nil { - info = cinfo - } else { - fmt.Printf("use 'godoc %s%s' for documentation on the %s command \n\n", cmdPrefix, relpath, relpath) - } - } - - if info == nil { - log.Fatalf("%s: no such directory or package", flag.Arg(0)) - } - if info.Err != nil { - log.Fatalf("%v", info.Err) - } - - if info.PDoc != nil && info.PDoc.ImportPath == target { - // Replace virtual /target with actual argument from command line. - info.PDoc.ImportPath = flag.Arg(0) - } - - // If we have more than one argument, use the remaining arguments for filtering. - if flag.NArg() > 1 { - args := flag.Args()[1:] - rx := makeRx(args) - if rx == nil { - log.Fatalf("illegal regular expression from %v", args) - } - - filter := func(s string) bool { return rx.MatchString(s) } - switch { - case info.PAst != nil: - cmap := ast.NewCommentMap(info.FSet, info.PAst, info.PAst.Comments) - ast.FilterFile(info.PAst, filter) - // Special case: Don't use templates for printing - // so we only get the filtered declarations without - // package clause or extra whitespace. - for i, d := range info.PAst.Decls { - // determine the comments associated with d only - comments := cmap.Filter(d).Comments() - cn := &printer.CommentedNode{Node: d, Comments: comments} - if i > 0 { - fmt.Println() - } - if *html { - var buf bytes.Buffer - writeNode(&buf, info.FSet, cn) - FormatText(os.Stdout, buf.Bytes(), -1, true, "", nil) - } else { - writeNode(os.Stdout, info.FSet, cn) - } - fmt.Println() - } - return - - case info.PDoc != nil: - info.PDoc.Filter(filter) - } - } - - if err := packageText.Execute(os.Stdout, info); err != nil { - log.Printf("packageText.Execute: %s", err) - } -} - -// An httpWriter is an http.ResponseWriter writing to a bytes.Buffer. -type httpWriter struct { - bytes.Buffer - h http.Header - code int -} - -func (w *httpWriter) Header() http.Header { return w.h } -func (w *httpWriter) WriteHeader(code int) { w.code = code } diff --git a/src/cmd/godoc/parser.go b/src/cmd/godoc/parser.go deleted file mode 100644 index 42a5d2d98..000000000 --- a/src/cmd/godoc/parser.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains support functions for parsing .go files -// accessed via godoc's file system fs. - -package main - -import ( - "go/ast" - "go/parser" - "go/token" - pathpkg "path" -) - -func parseFile(fset *token.FileSet, filename string, mode parser.Mode) (*ast.File, error) { - src, err := ReadFile(fs, filename) - if err != nil { - return nil, err - } - return parser.ParseFile(fset, filename, src, mode) -} - -func parseFiles(fset *token.FileSet, abspath string, localnames []string) (map[string]*ast.File, error) { - files := make(map[string]*ast.File) - for _, f := range localnames { - absname := pathpkg.Join(abspath, f) - file, err := parseFile(fset, absname, parser.ParseComments) - if err != nil { - return nil, err - } - files[absname] = file - } - - return files, nil -} diff --git a/src/cmd/godoc/play-appengine.go b/src/cmd/godoc/play-appengine.go deleted file mode 100644 index 9e351d1a2..000000000 --- a/src/cmd/godoc/play-appengine.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// App Engine godoc Playground functionality. - -// +build appengine - -package main - -import ( - "io" - "net/http" - - "appengine" - "appengine/urlfetch" -) - -func bounceToPlayground(w http.ResponseWriter, req *http.Request) { - c := appengine.NewContext(req) - client := urlfetch.Client(c) - url := playgroundBaseURL + req.URL.Path - defer req.Body.Close() - resp, err := client.Post(url, req.Header.Get("Content-type"), req.Body) - if err != nil { - http.Error(w, "Internal Server Error", 500) - c.Errorf("making POST request: %v", err) - return - } - defer resp.Body.Close() - if _, err := io.Copy(w, resp.Body); err != nil { - http.Error(w, "Internal Server Error", 500) - c.Errorf("making POST request: %v", err) - } -} diff --git a/src/cmd/godoc/play-local.go b/src/cmd/godoc/play-local.go deleted file mode 100644 index 637ce5e1a..000000000 --- a/src/cmd/godoc/play-local.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Stand-alone godoc Playground functionality. - -// +build !appengine - -package main - -import ( - "io" - "net/http" - "net/url" -) - -var playgroundScheme, playgroundHost string - -func init() { - u, err := url.Parse(playgroundBaseURL) - if err != nil { - panic(err) - } - playgroundScheme = u.Scheme - playgroundHost = u.Host -} - -// bounceToPlayground forwards the request to play.golang.org. -func bounceToPlayground(w http.ResponseWriter, req *http.Request) { - defer req.Body.Close() - req.URL.Scheme = playgroundScheme - req.URL.Host = playgroundHost - resp, err := http.Post(req.URL.String(), req.Header.Get("Content-type"), req.Body) - if err != nil { - http.Error(w, err.Error(), 500) - return - } - w.WriteHeader(resp.StatusCode) - io.Copy(w, resp.Body) - resp.Body.Close() -} diff --git a/src/cmd/godoc/play.go b/src/cmd/godoc/play.go deleted file mode 100644 index 47a11f6c0..000000000 --- a/src/cmd/godoc/play.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Common Playground functionality. - -package main - -import ( - "encoding/json" - "fmt" - "go/format" - "net/http" -) - -// The server that will service compile and share requests. -const playgroundBaseURL = "http://play.golang.org" - -func registerPlaygroundHandlers(mux *http.ServeMux) { - if *showPlayground { - mux.HandleFunc("/compile", bounceToPlayground) - mux.HandleFunc("/share", bounceToPlayground) - } else { - mux.HandleFunc("/compile", disabledHandler) - mux.HandleFunc("/share", disabledHandler) - } - http.HandleFunc("/fmt", fmtHandler) -} - -type fmtResponse struct { - Body string - Error string -} - -// fmtHandler takes a Go program in its "body" form value, formats it with -// standard gofmt formatting, and writes a fmtResponse as a JSON object. -func fmtHandler(w http.ResponseWriter, r *http.Request) { - resp := new(fmtResponse) - body, err := format.Source([]byte(r.FormValue("body"))) - if err != nil { - resp.Error = err.Error() - } else { - resp.Body = string(body) - } - json.NewEncoder(w).Encode(resp) -} - -// disabledHandler serves a 501 "Not Implemented" response. -func disabledHandler(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) - fmt.Fprint(w, "This functionality is not available via local godoc.") -} diff --git a/src/cmd/godoc/setup-godoc-app.bash b/src/cmd/godoc/setup-godoc-app.bash deleted file mode 100755 index 792e0d450..000000000 --- a/src/cmd/godoc/setup-godoc-app.bash +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2011 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -# This script creates a complete godoc app in $APPDIR. -# It copies the cmd/godoc and src/pkg/go/... sources from GOROOT, -# synthesizes an app.yaml file, and creates the .zip, index, and -# configuration files. -# -# If an argument is provided it is assumed to be the app-engine godoc directory. -# Without an argument, $APPDIR is used instead. If GOROOT is not set, "go env" -# is consulted to find the $GOROOT. -# -# The script creates a .zip file representing the $GOROOT file system -# and computes the correspondig search index files. These files are then -# copied to $APPDIR. A corresponding godoc configuration file is created -# in $APPDIR/appconfig.go. - -ZIPFILE=godoc.zip -INDEXFILE=godoc.index -SPLITFILES=index.split. -CONFIGFILE=godoc/appconfig.go - -error() { - echo "error: $1" - exit 2 -} - -getArgs() { - if [ -z $GOROOT ]; then - GOROOT=$(go env GOROOT) - echo "GOROOT not set explicitly, using $GOROOT instead" - fi - if [ -z $APPDIR ]; then - if [ $# == 0 ]; then - error "APPDIR not set, and no argument provided" - fi - APPDIR=$1 - echo "APPDIR not set, using argument instead" - fi - - # safety checks - if [ ! -d $GOROOT ]; then - error "$GOROOT is not a directory" - fi - if [ ! -x $GOROOT/bin/godoc ]; then - error "$GOROOT/bin/godoc does not exist or is not executable" - fi - if [ -e $APPDIR ]; then - error "$APPDIR exists; check and remove it before trying again" - fi - - # reporting - echo "GOROOT = $GOROOT" - echo "APPDIR = $APPDIR" -} - -copyGodoc() { - echo "*** copy $GOROOT/src/cmd/godoc to $APPDIR/godoc" - cp -r $GOROOT/src/cmd/godoc $APPDIR/godoc -} - -copyGoPackages() { - echo "*** copy $GOROOT/src/pkg/go to $APPDIR/newgo and rewrite imports" - cp -r $GOROOT/src/pkg/go $APPDIR/newgo - find $APPDIR/newgo -type d -name testdata | xargs rm -r - gofiles=$(find $APPDIR -name '*.go') - sed -i '' 's_^\(."\)\(go/[a-z]*\)"$_\1new\2"_' $gofiles - sed -i '' 's_^\(import "\)\(go/[a-z]*\)"$_\1new\2"_' $gofiles -} - -makeAppYaml() { - echo "*** make $APPDIR/app.yaml" - cat > $APPDIR/app.yaml <<EOF -application: godoc -version: 1 -runtime: go -api_version: go1 - -handlers: -- url: /.* - script: _go_app -EOF -} - -makeZipfile() { - echo "*** make $APPDIR/$ZIPFILE" - zip -q -r $APPDIR/$ZIPFILE $GOROOT -i \*.go -i \*.html -i \*.xml -i \*.css -i \*.js -i \*.txt -i \*.c -i \*.h -i \*.s -i \*.png -i \*.jpg -i \*.sh -i \*.ico -} - -makeIndexfile() { - echo "*** make $APPDIR/$INDEXFILE" - OUT=/tmp/godoc.out - $GOROOT/bin/godoc -write_index -index_files=$APPDIR/$INDEXFILE -zip=$APPDIR/$ZIPFILE 2> $OUT - if [ $? != 0 ]; then - error "$GOROOT/bin/godoc failed - see $OUT for details" - fi -} - -splitIndexfile() { - echo "*** split $APPDIR/$INDEXFILE" - split -b8m $APPDIR/$INDEXFILE $APPDIR/$SPLITFILES -} - -makeConfigfile() { - echo "*** make $APPDIR/$CONFIGFILE" - cat > $APPDIR/$CONFIGFILE <<EOF -package main - -// GENERATED FILE - DO NOT MODIFY BY HAND. -// (generated by $GOROOT/src/cmd/godoc/setup-godoc-app.bash) - -const ( - // .zip filename - zipFilename = "$ZIPFILE" - - // goroot directory in .zip file - zipGoroot = "$GOROOT" - - // glob pattern describing search index files - // (if empty, the index is built at run-time) - indexFilenames = "$SPLITFILES*" -) -EOF -} - -getArgs "$@" -set -e -mkdir $APPDIR -copyGodoc -copyGoPackages -makeAppYaml -makeZipfile -makeIndexfile -splitIndexfile -makeConfigfile - -echo "*** setup complete" diff --git a/src/cmd/godoc/snippet.go b/src/cmd/godoc/snippet.go deleted file mode 100644 index b482b7487..000000000 --- a/src/cmd/godoc/snippet.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains the infrastructure to create a code -// snippet for search results. -// -// Note: At the moment, this only creates HTML snippets. - -package main - -import ( - "bytes" - "fmt" - "go/ast" - "go/token" -) - -type Snippet struct { - Line int - Text string // HTML-escaped -} - -func newSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet { - // TODO instead of pretty-printing the node, should use the original source instead - var buf1 bytes.Buffer - writeNode(&buf1, fset, decl) - // wrap text with <pre> tag - var buf2 bytes.Buffer - buf2.WriteString("<pre>") - FormatText(&buf2, buf1.Bytes(), -1, true, id.Name, nil) - buf2.WriteString("</pre>") - return &Snippet{fset.Position(id.Pos()).Line, buf2.String()} -} - -func findSpec(list []ast.Spec, id *ast.Ident) ast.Spec { - for _, spec := range list { - switch s := spec.(type) { - case *ast.ImportSpec: - if s.Name == id { - return s - } - case *ast.ValueSpec: - for _, n := range s.Names { - if n == id { - return s - } - } - case *ast.TypeSpec: - if s.Name == id { - return s - } - } - } - return nil -} - -func genSnippet(fset *token.FileSet, d *ast.GenDecl, id *ast.Ident) *Snippet { - s := findSpec(d.Specs, id) - if s == nil { - return nil // declaration doesn't contain id - exit gracefully - } - - // only use the spec containing the id for the snippet - dd := &ast.GenDecl{ - Doc: d.Doc, - TokPos: d.Pos(), - Tok: d.Tok, - Lparen: d.Lparen, - Specs: []ast.Spec{s}, - Rparen: d.Rparen, - } - - return newSnippet(fset, dd, id) -} - -func funcSnippet(fset *token.FileSet, d *ast.FuncDecl, id *ast.Ident) *Snippet { - if d.Name != id { - return nil // declaration doesn't contain id - exit gracefully - } - - // only use the function signature for the snippet - dd := &ast.FuncDecl{ - Doc: d.Doc, - Recv: d.Recv, - Name: d.Name, - Type: d.Type, - } - - return newSnippet(fset, dd, id) -} - -// NewSnippet creates a text snippet from a declaration decl containing an -// identifier id. Parts of the declaration not containing the identifier -// may be removed for a more compact snippet. -// -func NewSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) (s *Snippet) { - switch d := decl.(type) { - case *ast.GenDecl: - s = genSnippet(fset, d, id) - case *ast.FuncDecl: - s = funcSnippet(fset, d, id) - } - - // handle failure gracefully - if s == nil { - var buf bytes.Buffer - fmt.Fprintf(&buf, `<span class="alert">could not generate a snippet for <span class="highlight">%s</span></span>`, id.Name) - s = &Snippet{fset.Position(id.Pos()).Line, buf.String()} - } - return -} diff --git a/src/cmd/godoc/spec.go b/src/cmd/godoc/spec.go deleted file mode 100644 index c11f25d20..000000000 --- a/src/cmd/godoc/spec.go +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -// This file contains the mechanism to "linkify" html source -// text containing EBNF sections (as found in go_spec.html). -// The result is the input source text with the EBNF sections -// modified such that identifiers are linked to the respective -// definitions. - -import ( - "bytes" - "fmt" - "io" - "text/scanner" -) - -type ebnfParser struct { - out io.Writer // parser output - src []byte // parser input - scanner scanner.Scanner - prev int // offset of previous token - pos int // offset of current token - tok rune // one token look-ahead - lit string // token literal -} - -func (p *ebnfParser) flush() { - p.out.Write(p.src[p.prev:p.pos]) - p.prev = p.pos -} - -func (p *ebnfParser) next() { - p.tok = p.scanner.Scan() - p.pos = p.scanner.Position.Offset - p.lit = p.scanner.TokenText() -} - -func (p *ebnfParser) printf(format string, args ...interface{}) { - p.flush() - fmt.Fprintf(p.out, format, args...) -} - -func (p *ebnfParser) errorExpected(msg string) { - p.printf(`<span class="highlight">error: expected %s, found %s</span>`, msg, scanner.TokenString(p.tok)) -} - -func (p *ebnfParser) expect(tok rune) { - if p.tok != tok { - p.errorExpected(scanner.TokenString(tok)) - } - p.next() // make progress in any case -} - -func (p *ebnfParser) parseIdentifier(def bool) { - if p.tok == scanner.Ident { - name := p.lit - if def { - p.printf(`<a id="%s">%s</a>`, name, name) - } else { - p.printf(`<a href="#%s" class="noline">%s</a>`, name, name) - } - p.prev += len(name) // skip identifier when printing next time - p.next() - } else { - p.expect(scanner.Ident) - } -} - -func (p *ebnfParser) parseTerm() bool { - switch p.tok { - case scanner.Ident: - p.parseIdentifier(false) - - case scanner.String: - p.next() - const ellipsis = '…' // U+2026, the horizontal ellipsis character - if p.tok == ellipsis { - p.next() - p.expect(scanner.String) - } - - case '(': - p.next() - p.parseExpression() - p.expect(')') - - case '[': - p.next() - p.parseExpression() - p.expect(']') - - case '{': - p.next() - p.parseExpression() - p.expect('}') - - default: - return false // no term found - } - - return true -} - -func (p *ebnfParser) parseSequence() { - if !p.parseTerm() { - p.errorExpected("term") - } - for p.parseTerm() { - } -} - -func (p *ebnfParser) parseExpression() { - for { - p.parseSequence() - if p.tok != '|' { - break - } - p.next() - } -} - -func (p *ebnfParser) parseProduction() { - p.parseIdentifier(true) - p.expect('=') - if p.tok != '.' { - p.parseExpression() - } - p.expect('.') -} - -func (p *ebnfParser) parse(out io.Writer, src []byte) { - // initialize ebnfParser - p.out = out - p.src = src - p.scanner.Init(bytes.NewBuffer(src)) - p.next() // initializes pos, tok, lit - - // process source - for p.tok != scanner.EOF { - p.parseProduction() - } - p.flush() -} - -// Markers around EBNF sections -var ( - openTag = []byte(`<pre class="ebnf">`) - closeTag = []byte(`</pre>`) -) - -func Linkify(out io.Writer, src []byte) { - for len(src) > 0 { - // i: beginning of EBNF text (or end of source) - i := bytes.Index(src, openTag) - if i < 0 { - i = len(src) - len(openTag) - } - i += len(openTag) - - // j: end of EBNF text (or end of source) - j := bytes.Index(src[i:], closeTag) // close marker - if j < 0 { - j = len(src) - i - } - j += i - - // write text before EBNF - out.Write(src[0:i]) - // process EBNF - var p ebnfParser - p.parse(out, src[i:j]) - - // advance - src = src[j:] - } -} diff --git a/src/cmd/godoc/template.go b/src/cmd/godoc/template.go deleted file mode 100644 index 7b9b9cfeb..000000000 --- a/src/cmd/godoc/template.go +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Template support for writing HTML documents. -// Documents that include Template: true in their -// metadata are executed as input to text/template. -// -// This file defines functions for those templates to invoke. - -// The template uses the function "code" to inject program -// source into the output by extracting code from files and -// injecting them as HTML-escaped <pre> blocks. -// -// The syntax is simple: 1, 2, or 3 space-separated arguments: -// -// Whole file: -// {{code "foo.go"}} -// One line (here the signature of main): -// {{code "foo.go" `/^func.main/`}} -// Block of text, determined by start and end (here the body of main): -// {{code "foo.go" `/^func.main/` `/^}/` -// -// Patterns can be `/regular expression/`, a decimal number, or "$" -// to signify the end of the file. In multi-line matches, -// lines that end with the four characters -// OMIT -// are omitted from the output, making it easy to provide marker -// lines in the input that will not appear in the output but are easy -// to identify by pattern. - -package main - -import ( - "bytes" - "fmt" - "log" - "regexp" - "strings" - "text/template" -) - -// Functions in this file panic on error, but the panic is recovered -// to an error by 'code'. - -var templateFuncs = template.FuncMap{ - "code": code, -} - -// contents reads and returns the content of the named file -// (from the virtual file system, so for example /doc refers to $GOROOT/doc). -func contents(name string) string { - file, err := ReadFile(fs, name) - if err != nil { - log.Panic(err) - } - return string(file) -} - -// stringFor returns a textual representation of the arg, formatted according to its nature. -func stringFor(arg interface{}) string { - switch arg := arg.(type) { - case int: - return fmt.Sprintf("%d", arg) - case string: - if len(arg) > 2 && arg[0] == '/' && arg[len(arg)-1] == '/' { - return fmt.Sprintf("%#q", arg) - } - return fmt.Sprintf("%q", arg) - default: - log.Panicf("unrecognized argument: %v type %T", arg, arg) - } - return "" -} - -func code(file string, arg ...interface{}) (s string, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("%v", r) - } - }() - - text := contents(file) - var command string - switch len(arg) { - case 0: - // text is already whole file. - command = fmt.Sprintf("code %q", file) - case 1: - command = fmt.Sprintf("code %q %s", file, stringFor(arg[0])) - text = oneLine(file, text, arg[0]) - case 2: - command = fmt.Sprintf("code %q %s %s", file, stringFor(arg[0]), stringFor(arg[1])) - text = multipleLines(file, text, arg[0], arg[1]) - default: - return "", fmt.Errorf("incorrect code invocation: code %q %q", file, arg) - } - // Trim spaces from output. - text = strings.Trim(text, "\n") - // Replace tabs by spaces, which work better in HTML. - text = strings.Replace(text, "\t", " ", -1) - var buf bytes.Buffer - // HTML-escape text and syntax-color comments like elsewhere. - FormatText(&buf, []byte(text), -1, true, "", nil) - // Include the command as a comment. - text = fmt.Sprintf("<pre><!--{{%s}}\n-->%s</pre>", command, buf.Bytes()) - return text, nil -} - -// parseArg returns the integer or string value of the argument and tells which it is. -func parseArg(arg interface{}, file string, max int) (ival int, sval string, isInt bool) { - switch n := arg.(type) { - case int: - if n <= 0 || n > max { - log.Panicf("%q:%d is out of range", file, n) - } - return n, "", true - case string: - return 0, n, false - } - log.Panicf("unrecognized argument %v type %T", arg, arg) - return -} - -// oneLine returns the single line generated by a two-argument code invocation. -func oneLine(file, text string, arg interface{}) string { - lines := strings.SplitAfter(contents(file), "\n") - line, pattern, isInt := parseArg(arg, file, len(lines)) - if isInt { - return lines[line-1] - } - return lines[match(file, 0, lines, pattern)-1] -} - -// multipleLines returns the text generated by a three-argument code invocation. -func multipleLines(file, text string, arg1, arg2 interface{}) string { - lines := strings.SplitAfter(contents(file), "\n") - line1, pattern1, isInt1 := parseArg(arg1, file, len(lines)) - line2, pattern2, isInt2 := parseArg(arg2, file, len(lines)) - if !isInt1 { - line1 = match(file, 0, lines, pattern1) - } - if !isInt2 { - line2 = match(file, line1, lines, pattern2) - } else if line2 < line1 { - log.Panicf("lines out of order for %q: %d %d", text, line1, line2) - } - for k := line1 - 1; k < line2; k++ { - if strings.HasSuffix(lines[k], "OMIT\n") { - lines[k] = "" - } - } - return strings.Join(lines[line1-1:line2], "") -} - -// match identifies the input line that matches the pattern in a code invocation. -// If start>0, match lines starting there rather than at the beginning. -// The return value is 1-indexed. -func match(file string, start int, lines []string, pattern string) int { - // $ matches the end of the file. - if pattern == "$" { - if len(lines) == 0 { - log.Panicf("%q: empty file", file) - } - return len(lines) - } - // /regexp/ matches the line that matches the regexp. - if len(pattern) > 2 && pattern[0] == '/' && pattern[len(pattern)-1] == '/' { - re, err := regexp.Compile(pattern[1 : len(pattern)-1]) - if err != nil { - log.Panic(err) - } - for i := start; i < len(lines); i++ { - if re.MatchString(lines[i]) { - return i + 1 - } - } - log.Panicf("%s: no match for %#q", file, pattern) - } - log.Panicf("unrecognized pattern: %q", pattern) - return 0 -} diff --git a/src/cmd/godoc/throttle.go b/src/cmd/godoc/throttle.go deleted file mode 100644 index ac18b44e0..000000000 --- a/src/cmd/godoc/throttle.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import "time" - -// A Throttle permits throttling of a goroutine by -// calling the Throttle method repeatedly. -// -type Throttle struct { - f float64 // f = (1-r)/r for 0 < r < 1 - dt time.Duration // minimum run time slice; >= 0 - tr time.Duration // accumulated time running - ts time.Duration // accumulated time stopped - tt time.Time // earliest throttle time (= time Throttle returned + tm) -} - -// NewThrottle creates a new Throttle with a throttle value r and -// a minimum allocated run time slice of dt: -// -// r == 0: "empty" throttle; the goroutine is always sleeping -// r == 1: full throttle; the goroutine is never sleeping -// -// A value of r == 0.6 throttles a goroutine such that it runs -// approx. 60% of the time, and sleeps approx. 40% of the time. -// Values of r < 0 or r > 1 are clamped down to values between 0 and 1. -// Values of dt < 0 are set to 0. -// -func NewThrottle(r float64, dt time.Duration) *Throttle { - var f float64 - switch { - case r <= 0: - f = -1 // indicates always sleep - case r >= 1: - f = 0 // assume r == 1 (never sleep) - default: - // 0 < r < 1 - f = (1 - r) / r - } - if dt < 0 { - dt = 0 - } - return &Throttle{f: f, dt: dt, tt: time.Now().Add(dt)} -} - -// Throttle calls time.Sleep such that over time the ratio tr/ts between -// accumulated run (tr) and sleep times (ts) approximates the value 1/(1-r) -// where r is the throttle value. Throttle returns immediately (w/o sleeping) -// if less than tm ns have passed since the last call to Throttle. -// -func (p *Throttle) Throttle() { - if p.f < 0 { - select {} // always sleep - } - - t0 := time.Now() - if t0.Before(p.tt) { - return // keep running (minimum time slice not exhausted yet) - } - - // accumulate running time - p.tr += t0.Sub(p.tt) + p.dt - - // compute sleep time - // Over time we want: - // - // tr/ts = r/(1-r) - // - // Thus: - // - // ts = tr*f with f = (1-r)/r - // - // After some incremental run time δr added to the total run time - // tr, the incremental sleep-time δs to get to the same ratio again - // after waking up from time.Sleep is: - if δs := time.Duration(float64(p.tr)*p.f) - p.ts; δs > 0 { - time.Sleep(δs) - } - - // accumulate (actual) sleep time - t1 := time.Now() - p.ts += t1.Sub(t0) - - // set earliest next throttle time - p.tt = t1.Add(p.dt) -} diff --git a/src/cmd/godoc/utils.go b/src/cmd/godoc/utils.go deleted file mode 100644 index 0cdb7ff7a..000000000 --- a/src/cmd/godoc/utils.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains support functionality for godoc. - -package main - -import ( - pathpkg "path" - "sync" - "time" - "unicode/utf8" -) - -// An RWValue wraps a value and permits mutually exclusive -// access to it and records the time the value was last set. -// -type RWValue struct { - mutex sync.RWMutex - value interface{} - timestamp time.Time // time of last set() -} - -func (v *RWValue) set(value interface{}) { - v.mutex.Lock() - v.value = value - v.timestamp = time.Now() - v.mutex.Unlock() -} - -func (v *RWValue) get() (interface{}, time.Time) { - v.mutex.RLock() - defer v.mutex.RUnlock() - return v.value, v.timestamp -} - -// isText returns true if a significant prefix of s looks like correct UTF-8; -// that is, if it is likely that s is human-readable text. -// -func isText(s []byte) bool { - const max = 1024 // at least utf8.UTFMax - if len(s) > max { - s = s[0:max] - } - for i, c := range string(s) { - if i+utf8.UTFMax > len(s) { - // last char may be incomplete - ignore - break - } - if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' && c != '\f' { - // decoding error or control character - not a text file - return false - } - } - return true -} - -// textExt[x] is true if the extension x indicates a text file, and false otherwise. -var textExt = map[string]bool{ - ".css": false, // must be served raw - ".js": false, // must be served raw -} - -// isTextFile returns true if the file has a known extension indicating -// a text file, or if a significant chunk of the specified file looks like -// correct UTF-8; that is, if it is likely that the file contains human- -// readable text. -// -func isTextFile(filename string) bool { - // if the extension is known, use it for decision making - if isText, found := textExt[pathpkg.Ext(filename)]; found { - return isText - } - - // the extension is not known; read an initial chunk - // of the file and check if it looks like text - f, err := fs.Open(filename) - if err != nil { - return false - } - defer f.Close() - - var buf [1024]byte - n, err := f.Read(buf[0:]) - if err != nil { - return false - } - - return isText(buf[0:n]) -} diff --git a/src/cmd/godoc/zip.go b/src/cmd/godoc/zip.go deleted file mode 100644 index 620eb4f3c..000000000 --- a/src/cmd/godoc/zip.go +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file provides an implementation of the FileSystem -// interface based on the contents of a .zip file. -// -// Assumptions: -// -// - The file paths stored in the zip file must use a slash ('/') as path -// separator; and they must be relative (i.e., they must not start with -// a '/' - this is usually the case if the file was created w/o special -// options). -// - The zip file system treats the file paths found in the zip internally -// like absolute paths w/o a leading '/'; i.e., the paths are considered -// relative to the root of the file system. -// - All path arguments to file system methods must be absolute paths. - -package main - -import ( - "archive/zip" - "fmt" - "io" - "os" - "path" - "sort" - "strings" - "time" -) - -// zipFI is the zip-file based implementation of FileInfo -type zipFI struct { - name string // directory-local name - file *zip.File // nil for a directory -} - -func (fi zipFI) Name() string { - return fi.name -} - -func (fi zipFI) Size() int64 { - if f := fi.file; f != nil { - return int64(f.UncompressedSize) - } - return 0 // directory -} - -func (fi zipFI) ModTime() time.Time { - if f := fi.file; f != nil { - return f.ModTime() - } - return time.Time{} // directory has no modified time entry -} - -func (fi zipFI) Mode() os.FileMode { - if fi.file == nil { - // Unix directories typically are executable, hence 555. - return os.ModeDir | 0555 - } - return 0444 -} - -func (fi zipFI) IsDir() bool { - return fi.file == nil -} - -func (fi zipFI) Sys() interface{} { - return nil -} - -// zipFS is the zip-file based implementation of FileSystem -type zipFS struct { - *zip.ReadCloser - list zipList - name string -} - -func (fs *zipFS) String() string { - return "zip(" + fs.name + ")" -} - -func (fs *zipFS) Close() error { - fs.list = nil - return fs.ReadCloser.Close() -} - -func zipPath(name string) string { - name = path.Clean(name) - if !path.IsAbs(name) { - panic(fmt.Sprintf("stat: not an absolute path: %s", name)) - } - return name[1:] // strip leading '/' -} - -func (fs *zipFS) stat(abspath string) (int, zipFI, error) { - i, exact := fs.list.lookup(abspath) - if i < 0 { - // abspath has leading '/' stripped - print it explicitly - return -1, zipFI{}, fmt.Errorf("file not found: /%s", abspath) - } - _, name := path.Split(abspath) - var file *zip.File - if exact { - file = fs.list[i] // exact match found - must be a file - } - return i, zipFI{name, file}, nil -} - -func (fs *zipFS) Open(abspath string) (readSeekCloser, error) { - _, fi, err := fs.stat(zipPath(abspath)) - if err != nil { - return nil, err - } - if fi.IsDir() { - return nil, fmt.Errorf("Open: %s is a directory", abspath) - } - r, err := fi.file.Open() - if err != nil { - return nil, err - } - return &zipSeek{fi.file, r}, nil -} - -type zipSeek struct { - file *zip.File - io.ReadCloser -} - -func (f *zipSeek) Seek(offset int64, whence int) (int64, error) { - if whence == 0 && offset == 0 { - r, err := f.file.Open() - if err != nil { - return 0, err - } - f.Close() - f.ReadCloser = r - return 0, nil - } - return 0, fmt.Errorf("unsupported Seek in %s", f.file.Name) -} - -func (fs *zipFS) Lstat(abspath string) (os.FileInfo, error) { - _, fi, err := fs.stat(zipPath(abspath)) - return fi, err -} - -func (fs *zipFS) Stat(abspath string) (os.FileInfo, error) { - _, fi, err := fs.stat(zipPath(abspath)) - return fi, err -} - -func (fs *zipFS) ReadDir(abspath string) ([]os.FileInfo, error) { - path := zipPath(abspath) - i, fi, err := fs.stat(path) - if err != nil { - return nil, err - } - if !fi.IsDir() { - return nil, fmt.Errorf("ReadDir: %s is not a directory", abspath) - } - - var list []os.FileInfo - dirname := path + "/" - prevname := "" - for _, e := range fs.list[i:] { - if !strings.HasPrefix(e.Name, dirname) { - break // not in the same directory anymore - } - name := e.Name[len(dirname):] // local name - file := e - if i := strings.IndexRune(name, '/'); i >= 0 { - // We infer directories from files in subdirectories. - // If we have x/y, return a directory entry for x. - name = name[0:i] // keep local directory name only - file = nil - } - // If we have x/y and x/z, don't return two directory entries for x. - // TODO(gri): It should be possible to do this more efficiently - // by determining the (fs.list) range of local directory entries - // (via two binary searches). - if name != prevname { - list = append(list, zipFI{name, file}) - prevname = name - } - } - - return list, nil -} - -func NewZipFS(rc *zip.ReadCloser, name string) FileSystem { - list := make(zipList, len(rc.File)) - copy(list, rc.File) // sort a copy of rc.File - sort.Sort(list) - return &zipFS{rc, list, name} -} - -type zipList []*zip.File - -// zipList implements sort.Interface -func (z zipList) Len() int { return len(z) } -func (z zipList) Less(i, j int) bool { return z[i].Name < z[j].Name } -func (z zipList) Swap(i, j int) { z[i], z[j] = z[j], z[i] } - -// lookup returns the smallest index of an entry with an exact match -// for name, or an inexact match starting with name/. If there is no -// such entry, the result is -1, false. -func (z zipList) lookup(name string) (index int, exact bool) { - // look for exact match first (name comes before name/ in z) - i := sort.Search(len(z), func(i int) bool { - return name <= z[i].Name - }) - if i >= len(z) { - return -1, false - } - // 0 <= i < len(z) - if z[i].Name == name { - return i, true - } - - // look for inexact match (must be in z[i:], if present) - z = z[i:] - name += "/" - j := sort.Search(len(z), func(i int) bool { - return name <= z[i].Name - }) - if j >= len(z) { - return -1, false - } - // 0 <= j < len(z) - if strings.HasPrefix(z[j].Name, name) { - return i + j, false - } - - return -1, false -} diff --git a/src/cmd/gofmt/doc.go b/src/cmd/gofmt/doc.go index fffc7f06e..94e67fd89 100644 --- a/src/cmd/gofmt/doc.go +++ b/src/cmd/gofmt/doc.go @@ -71,6 +71,25 @@ To remove the parentheses: To convert the package tree from explicit slice upper bounds to implicit ones: gofmt -r 'α[β:len(α)] -> α[β:]' -w $GOROOT/src/pkg + +The simplify command + +When invoked with -s gofmt will make the following source transformations where possible. + + An array, slice, or map composite literal of the form: + []T{T{}, T{}} + will be simplified to: + []T{{}, {}} + + A slice expression of the form: + s[a:len(s)] + will be simplified to: + s[a:] + + A range of the form: + for x, _ = range v {...} + will be simplified to: + for x = range v {...} */ package main diff --git a/src/cmd/gofmt/simplify.go b/src/cmd/gofmt/simplify.go index e9a67a73a..45d000d67 100644 --- a/src/cmd/gofmt/simplify.go +++ b/src/cmd/gofmt/simplify.go @@ -90,6 +90,10 @@ func (s *simplifier) Visit(node ast.Node) ast.Visitor { // Note: We could also simplify slice expressions of the form s[0:b] to s[:b] // but we leave them as is since sometimes we want to be very explicit // about the lower bound. + // An example where the 0 helps: + // x, y, z := b[0:2], b[2:4], b[4:6] + // An example where it does not: + // x, y := b[:n], b[n:] case *ast.RangeStmt: // a range of the form: for x, _ = range v {...} diff --git a/src/cmd/gofmt/testdata/import.golden b/src/cmd/gofmt/testdata/import.golden index e8ee44988..51d7be79d 100644 --- a/src/cmd/gofmt/testdata/import.golden +++ b/src/cmd/gofmt/testdata/import.golden @@ -106,3 +106,21 @@ import ( "log" // for Fatal "math" ) + +// Test deduping and extended sorting +import ( + a "A" // aA + b "A" // bA1 + b "A" // bA2 + "B" // B + . "B" // .B + _ "B" // _b + "C" + a "D" // aD +) + +import ( + "dedup_by_group" + + "dedup_by_group" +) diff --git a/src/cmd/gofmt/testdata/import.input b/src/cmd/gofmt/testdata/import.input index cc36c3e01..9a4b09dbf 100644 --- a/src/cmd/gofmt/testdata/import.input +++ b/src/cmd/gofmt/testdata/import.input @@ -106,3 +106,26 @@ import ( "errors" "io" // for Reader ) + +// Test deduping and extended sorting +import ( + "B" // B + a "A" // aA + b "A" // bA2 + b "A" // bA1 + . "B" // .B + . "B" + "C" + "C" + "C" + a "D" // aD + "B" + _ "B" // _b +) + +import ( + "dedup_by_group" + "dedup_by_group" + + "dedup_by_group" +) diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c index 23fc23e5f..30d7c8185 100644 --- a/src/cmd/ld/data.c +++ b/src/cmd/ld/data.c @@ -178,12 +178,14 @@ relocsym(Sym *s) switch(r->type) { default: o = 0; - if(linkmode == LinkExternal || archreloc(r, s, &o) < 0) + if(archreloc(r, s, &o) < 0) diag("unknown reloc %d", r->type); break; case D_TLS: r->done = 0; o = 0; + if(thechar != '6') + o = r->add; break; case D_ADDR: if(linkmode == LinkExternal && r->sym->type != SCONST) { @@ -305,8 +307,6 @@ void dynrelocsym(Sym *s) { Reloc *r; - Sym *rel; - Sym *got; if(HEADTYPE == Hwindows) { Sym *rel, *targ; @@ -343,22 +343,9 @@ dynrelocsym(Sym *s) return; } - got = rel = nil; - if(flag_shared) { - rel = lookuprel(); - got = lookup(".got", 0); - } - s->rel_ro = 0; for(r=s->r; r<s->r+s->nr; r++) { if(r->sym != S && r->sym->type == SDYNIMPORT || r->type >= 256) adddynrel(s, r); - if(flag_shared && r->sym != S && s->type != SDYNIMPORT && r->type == D_ADDR - && (s == got || s->type == SDATA || s->type == SGOSTRING || s->type == STYPE || s->type == SRODATA)) { - // Create address based RELATIVE relocation - adddynrela(rel, s, r); - if(s->type < SNOPTRDATA) - s->rel_ro = 1; - } } } @@ -367,7 +354,7 @@ dynreloc(void) { Sym *s; - // -d supresses dynamic loader format, so we may as well not + // -d suppresses dynamic loader format, so we may as well not // compute these sections or mark their symbols as reachable. if(debug['d'] && HEADTYPE != Hwindows) return; @@ -389,6 +376,12 @@ symgrow(Sym *s, int32 siz) if(s->np >= siz) return; + if(s->np > s->maxp) { + cursym = s; + diag("corrupt symbol data: np=%lld > maxp=%lld", (vlong)s->np, (vlong)s->maxp); + errorexit(); + } + if(s->maxp < siz) { if(s->maxp == 0) s->maxp = 8; @@ -763,7 +756,7 @@ setuintxx(Sym *s, vlong off, uint64 v, vlong wid) s->p[off+i] = cast[inuxi8[i]]; break; } - return off; + return off+wid; } vlong @@ -800,28 +793,28 @@ adduint64(Sym *s, uint64 v) return adduintxx(s, v, 8); } -void +vlong setuint8(Sym *s, vlong r, uint8 v) { - setuintxx(s, r, v, 1); + return setuintxx(s, r, v, 1); } -void +vlong setuint16(Sym *s, vlong r, uint16 v) { - setuintxx(s, r, v, 2); + return setuintxx(s, r, v, 2); } -void +vlong setuint32(Sym *s, vlong r, uint32 v) { - setuintxx(s, r, v, 4); + return setuintxx(s, r, v, 4); } -void +vlong setuint64(Sym *s, vlong r, uint64 v) { - setuintxx(s, r, v, 8); + return setuintxx(s, r, v, 8); } vlong @@ -842,7 +835,7 @@ addaddrplus(Sym *s, Sym *t, vlong add) r->siz = PtrSize; r->type = D_ADDR; r->add = add; - return i; + return i + r->siz; } static vlong @@ -863,7 +856,7 @@ addaddrplus4(Sym *s, Sym *t, vlong add) r->siz = 4; r->type = D_ADDR; r->add = add; - return i; + return i + r->siz; } vlong @@ -884,7 +877,7 @@ addpcrelplus(Sym *s, Sym *t, vlong add) r->add = add; r->type = D_PCREL; r->siz = 4; - return i; + return i + r->siz; } vlong @@ -911,7 +904,7 @@ setaddrplus(Sym *s, vlong off, Sym *t, vlong add) r->siz = PtrSize; r->type = D_ADDR; r->add = add; - return off; + return off + r->siz; } vlong @@ -937,7 +930,7 @@ addsize(Sym *s, Sym *t) r->off = i; r->siz = PtrSize; r->type = D_SIZE; - return i; + return i + r->siz; } void @@ -1040,6 +1033,7 @@ dodata(void) int32 n; vlong datsize; Section *sect; + Segment *segro; Sym *s, *last, **l; Sym *gcdata1, *gcbss1; @@ -1047,13 +1041,8 @@ dodata(void) Bprint(&bso, "%5.2f dodata\n", cputime()); Bflush(&bso); - // define garbage collection symbols gcdata1 = lookup("gcdata", 0); - gcdata1->type = STYPE; - gcdata1->reachable = 1; gcbss1 = lookup("gcbss", 0); - gcbss1->type = STYPE; - gcbss1->reachable = 1; // size of .data and .bss section. the zero value is later replaced by the actual size of the section. adduintxx(gcdata1, 0, PtrSize); @@ -1103,12 +1092,6 @@ dodata(void) } *l = nil; - if(flag_shared) { - for(s=datap; s != nil; s = s->next) { - if(s->rel_ro) - s->type = SDATARELRO; - } - } datap = listsort(datap, datcmp, offsetof(Sym, next)); /* @@ -1135,40 +1118,37 @@ dodata(void) sect->vaddr = datsize; s->sect = sect; s->type = SDATA; - s->value = datsize; + s->value = datsize - sect->vaddr; growdatsize(&datsize, s); sect->len = datsize - sect->vaddr; } /* pointer-free data */ sect = addsection(&segdata, ".noptrdata", 06); - sect->align = maxalign(s, SDATARELRO-1); + sect->align = maxalign(s, SINITARR-1); datsize = rnd(datsize, sect->align); sect->vaddr = datsize; lookup("noptrdata", 0)->sect = sect; lookup("enoptrdata", 0)->sect = sect; - for(; s != nil && s->type < SDATARELRO; s = s->next) { + for(; s != nil && s->type < SINITARR; s = s->next) { datsize = aligndatsize(datsize, s); s->sect = sect; s->type = SDATA; - s->value = datsize; + s->value = datsize - sect->vaddr; growdatsize(&datsize, s); } sect->len = datsize - sect->vaddr; - /* dynamic relocated rodata */ + /* shared library initializer */ if(flag_shared) { - sect = addsection(&segdata, ".data.rel.ro", 06); - sect->align = maxalign(s, SDATARELRO); + sect = addsection(&segdata, ".init_array", 06); + sect->align = maxalign(s, SINITARR); datsize = rnd(datsize, sect->align); sect->vaddr = datsize; - lookup("datarelro", 0)->sect = sect; - lookup("edatarelro", 0)->sect = sect; - for(; s != nil && s->type == SDATARELRO; s = s->next) { + for(; s != nil && s->type == SINITARR; s = s->next) { datsize = aligndatsize(datsize, s); s->sect = sect; - s->type = SDATA; - s->value = datsize; + s->value = datsize - sect->vaddr; growdatsize(&datsize, s); } sect->len = datsize - sect->vaddr; @@ -1182,14 +1162,14 @@ dodata(void) lookup("data", 0)->sect = sect; lookup("edata", 0)->sect = sect; for(; s != nil && s->type < SBSS; s = s->next) { - if(s->type == SDATARELRO) { + if(s->type == SINITARR) { cursym = s; diag("unexpected symbol type %d", s->type); } s->sect = sect; s->type = SDATA; datsize = aligndatsize(datsize, s); - s->value = datsize; + s->value = datsize - sect->vaddr; gcaddsym(gcdata1, s, datsize - sect->vaddr); // gc growdatsize(&datsize, s); } @@ -1208,7 +1188,7 @@ dodata(void) for(; s != nil && s->type < SNOPTRBSS; s = s->next) { s->sect = sect; datsize = aligndatsize(datsize, s); - s->value = datsize; + s->value = datsize - sect->vaddr; gcaddsym(gcbss1, s, datsize - sect->vaddr); // gc growdatsize(&datsize, s); } @@ -1227,7 +1207,7 @@ dodata(void) for(; s != nil && s->type == SNOPTRBSS; s = s->next) { datsize = aligndatsize(datsize, s); s->sect = sect; - s->value = datsize; + s->value = datsize - sect->vaddr; growdatsize(&datsize, s); } sect->len = datsize - sect->vaddr; @@ -1246,7 +1226,7 @@ dodata(void) for(; s != nil && s->type == STLSBSS; s = s->next) { datsize = aligndatsize(datsize, s); s->sect = sect; - s->value = datsize; + s->value = datsize - sect->vaddr; growdatsize(&datsize, s); } sect->len = datsize; @@ -1257,27 +1237,56 @@ dodata(void) diag("unexpected symbol type %d for %s", s->type, s->name); } - /* we finished segdata, begin segtext */ + /* + * We finished data, begin read-only data. + * Not all systems support a separate read-only non-executable data section. + * ELF systems do. + * OS X and Plan 9 do not. + * Windows PE may, but if so we have not implemented it. + * And if we're using external linking mode, the point is moot, + * since it's not our decision; that code expects the sections in + * segtext. + */ + if(iself && linkmode == LinkInternal) + segro = &segrodata; + else + segro = &segtext; + s = datap; + + datsize = 0; + + /* read-only executable ELF, Mach-O sections */ + for(; s != nil && s->type < STYPE; s = s->next) { + sect = addsection(&segtext, s->name, 04); + sect->align = symalign(s); + datsize = rnd(datsize, sect->align); + sect->vaddr = datsize; + s->sect = sect; + s->type = SRODATA; + s->value = datsize - sect->vaddr; + growdatsize(&datsize, s); + sect->len = datsize - sect->vaddr; + } /* read-only data */ - sect = addsection(&segtext, ".rodata", 04); + sect = addsection(segro, ".rodata", 04); sect->align = maxalign(s, STYPELINK-1); + datsize = rnd(datsize, sect->align); sect->vaddr = 0; lookup("rodata", 0)->sect = sect; lookup("erodata", 0)->sect = sect; - datsize = 0; for(; s != nil && s->type < STYPELINK; s = s->next) { datsize = aligndatsize(datsize, s); s->sect = sect; s->type = SRODATA; - s->value = datsize; + s->value = datsize - sect->vaddr; growdatsize(&datsize, s); } sect->len = datsize - sect->vaddr; /* typelink */ - sect = addsection(&segtext, ".typelink", 04); + sect = addsection(segro, ".typelink", 04); sect->align = maxalign(s, STYPELINK); datsize = rnd(datsize, sect->align); sect->vaddr = datsize; @@ -1287,13 +1296,13 @@ dodata(void) datsize = aligndatsize(datsize, s); s->sect = sect; s->type = SRODATA; - s->value = datsize; + s->value = datsize - sect->vaddr; growdatsize(&datsize, s); } sect->len = datsize - sect->vaddr; /* gosymtab */ - sect = addsection(&segtext, ".gosymtab", 04); + sect = addsection(segro, ".gosymtab", 04); sect->align = maxalign(s, SPCLNTAB-1); datsize = rnd(datsize, sect->align); sect->vaddr = datsize; @@ -1303,13 +1312,13 @@ dodata(void) datsize = aligndatsize(datsize, s); s->sect = sect; s->type = SRODATA; - s->value = datsize; + s->value = datsize - sect->vaddr; growdatsize(&datsize, s); } sect->len = datsize - sect->vaddr; /* gopclntab */ - sect = addsection(&segtext, ".gopclntab", 04); + sect = addsection(segro, ".gopclntab", 04); sect->align = maxalign(s, SELFROSECT-1); datsize = rnd(datsize, sect->align); sect->vaddr = datsize; @@ -1319,33 +1328,35 @@ dodata(void) datsize = aligndatsize(datsize, s); s->sect = sect; s->type = SRODATA; - s->value = datsize; + s->value = datsize - sect->vaddr; growdatsize(&datsize, s); } sect->len = datsize - sect->vaddr; /* read-only ELF, Mach-O sections */ for(; s != nil && s->type < SELFSECT; s = s->next) { - sect = addsection(&segtext, s->name, 04); + sect = addsection(segro, s->name, 04); sect->align = symalign(s); datsize = rnd(datsize, sect->align); sect->vaddr = datsize; s->sect = sect; s->type = SRODATA; - s->value = datsize; + s->value = datsize - sect->vaddr; growdatsize(&datsize, s); sect->len = datsize - sect->vaddr; } // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits. if(datsize != (uint32)datsize) { - diag("text segment too large"); + diag("read-only data segment too large"); } /* number the sections */ n = 1; for(sect = segtext.sect; sect != nil; sect = sect->next) sect->extnum = n++; + for(sect = segrodata.sect; sect != nil; sect = sect->next) + sect->extnum = n++; for(sect = segdata.sect; sect != nil; sect = sect->next) sect->extnum = n++; } @@ -1396,7 +1407,7 @@ textaddress(void) void address(void) { - Section *s, *text, *data, *rodata, *symtab, *pclntab, *noptr, *bss, *noptrbss, *datarelro; + Section *s, *text, *data, *rodata, *symtab, *pclntab, *noptr, *bss, *noptrbss; Section *typelink; Sym *sym, *sub; uvlong va; @@ -1407,6 +1418,7 @@ address(void) segtext.vaddr = va; segtext.fileoff = HEADR; for(s=segtext.sect; s != nil; s=s->next) { +//print("%s at %#llux + %#llux\n", s->name, va, (vlong)s->len); va = rnd(va, s->align); s->vaddr = va; va += s->len; @@ -1414,8 +1426,25 @@ address(void) segtext.len = va - INITTEXT; segtext.filelen = segtext.len; - va = rnd(va, INITRND); + if(segrodata.sect != nil) { + // align to page boundary so as not to mix + // rodata and executable text. + va = rnd(va, INITRND); + + segrodata.rwx = 04; + segrodata.vaddr = va; + segrodata.fileoff = va - segtext.vaddr + segtext.fileoff; + segrodata.filelen = 0; + for(s=segrodata.sect; s != nil; s=s->next) { + va = rnd(va, s->align); + s->vaddr = va; + va += s->len; + } + segrodata.len = va - segrodata.vaddr; + segrodata.filelen = segrodata.len; + } + va = rnd(va, INITRND); segdata.rwx = 06; segdata.vaddr = va; segdata.fileoff = va - segtext.vaddr + segtext.fileoff; @@ -1428,7 +1457,6 @@ address(void) noptr = nil; bss = nil; noptrbss = nil; - datarelro = nil; for(s=segdata.sect; s != nil; s=s->next) { vlen = s->len; if(s->next) @@ -1444,23 +1472,21 @@ address(void) bss = s; if(strcmp(s->name, ".noptrbss") == 0) noptrbss = s; - if(strcmp(s->name, ".data.rel.ro") == 0) - datarelro = s; } segdata.filelen = bss->vaddr - segdata.vaddr; text = segtext.sect; - rodata = text->next; + if(segrodata.sect) + rodata = segrodata.sect; + else + rodata = text->next; typelink = rodata->next; symtab = typelink->next; pclntab = symtab->next; for(sym = datap; sym != nil; sym = sym->next) { cursym = sym; - if(sym->type < SNOPTRDATA) - sym->value += rodata->vaddr; - else - sym->value += segdata.sect->vaddr; + sym->value += sym->sect->vaddr; for(sub = sym->sub; sub != nil; sub = sub->sub) sub->value += sym->value; } @@ -1471,17 +1497,13 @@ address(void) xdefine("erodata", SRODATA, rodata->vaddr + rodata->len); xdefine("typelink", SRODATA, typelink->vaddr); xdefine("etypelink", SRODATA, typelink->vaddr + typelink->len); - if(datarelro != nil) { - xdefine("datarelro", SRODATA, datarelro->vaddr); - xdefine("edatarelro", SRODATA, datarelro->vaddr + datarelro->len); - } sym = lookup("gcdata", 0); - xdefine("egcdata", STYPE, symaddr(sym) + sym->size); + xdefine("egcdata", SRODATA, symaddr(sym) + sym->size); lookup("egcdata", 0)->sect = sym->sect; sym = lookup("gcbss", 0); - xdefine("egcbss", STYPE, symaddr(sym) + sym->size); + xdefine("egcbss", SRODATA, symaddr(sym) + sym->size); lookup("egcbss", 0)->sect = sym->sect; xdefine("symtab", SRODATA, symtab->vaddr); diff --git a/src/cmd/ld/doc.go b/src/cmd/ld/doc.go index 3493f41d8..2adda25f2 100644 --- a/src/cmd/ld/doc.go +++ b/src/cmd/ld/doc.go @@ -33,6 +33,8 @@ Options new in this version: linker. This flag cannot be used when $GOOS is windows. -H darwin (only in 6l/8l) Write Apple Mach-O binaries (default when $GOOS is darwin) + -H dragonfly (only in 6l/8l) + Write DragonFly ELF binaries (default when $GOOS is dragonfly) -H linux Write Linux ELF binaries (default when $GOOS is linux) -H freebsd @@ -52,6 +54,8 @@ Options new in this version: The default is the single location $GOROOT/pkg/$GOOS_$GOARCH. -r dir1:dir2:... Set the dynamic linker search path when using ELF. + -s + Omit the symbol table and debug information. -V Print the linker version. -X symbol value diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c index 98b03f1c3..c832bcc94 100644 --- a/src/cmd/ld/dwarf.c +++ b/src/cmd/ld/dwarf.c @@ -39,6 +39,8 @@ static Sym* infosym; static vlong infosympos; static vlong frameo; static vlong framesize; +static Sym* framesym; +static vlong framesympos; static vlong pubnameso; static vlong pubnamessize; static vlong pubtypeso; @@ -60,6 +62,10 @@ static Sym *linesec; static vlong linereloco; static vlong linerelocsize; +static Sym *framesec; +static vlong framereloco; +static vlong framerelocsize; + static char gdbscript[1024]; /* @@ -133,7 +139,7 @@ sleb128put(vlong v) /* * Defining Abbrevs. This is hardcoded, and there will be * only a handful of them. The DWARF spec places no restriction on - * the ordering of atributes in the Abbrevs and DIEs, and we will + * the ordering of attributes in the Abbrevs and DIEs, and we will * always write them out in the order of declaration in the abbrev. * This implementation relies on tag, attr < 127, so they serialize as * a char. Higher numbered user-defined tags or attributes can be used @@ -1646,7 +1652,7 @@ guesslang(char *s) } /* - * Generate short opcodes when possible, long ones when neccesary. + * Generate short opcodes when possible, long ones when necessary. * See section 6.2.5 */ @@ -1938,7 +1944,7 @@ enum { CIERESERVE = 16, DATAALIGNMENTFACTOR = -4, // TODO -PtrSize? - FAKERETURNCOLUMN = 16 // TODO gdb6 doesnt like > 15? + FAKERETURNCOLUMN = 16 // TODO gdb6 doesn't like > 15? }; static void @@ -1968,6 +1974,9 @@ writeframes(void) Sym *s; vlong fdeo, fdesize, pad, cfa, pc; + if(framesec == S) + framesec = lookup(".dwarfframe", 0); + framesec->nr = 0; frameo = cpos(); // Emit the CIE, Section 6.4.1 @@ -2026,8 +2035,14 @@ writeframes(void) // Emit the FDE header for real, Section 6.4.1. cseek(fdeo); LPUT(fdesize); - LPUT(0); - addrput(p->pc); + if(linkmode == LinkExternal) { + adddwarfrel(framesec, framesym, frameo, 4, 0); + adddwarfrel(framesec, s, frameo, PtrSize, 0); + } + else { + LPUT(0); + addrput(p->pc); + } addrput(s->size); cseek(fdeo + 4 + fdesize); } @@ -2360,6 +2375,10 @@ dwarfemitdebugsections(void) linereloco = writedwarfreloc(linesec); linerelocsize = cpos() - linereloco; align(linerelocsize); + + framereloco = writedwarfreloc(framesec); + framerelocsize = cpos() - framereloco; + align(framerelocsize); } /* @@ -2382,6 +2401,7 @@ enum ElfStrRelDebugInfo, ElfStrRelDebugAranges, ElfStrRelDebugLine, + ElfStrRelDebugFrame, NElfStrDbg }; @@ -2410,10 +2430,12 @@ dwarfaddshstrings(Sym *shstrtab) elfstrdbg[ElfStrRelDebugInfo] = addstring(shstrtab, ".rela.debug_info"); elfstrdbg[ElfStrRelDebugAranges] = addstring(shstrtab, ".rela.debug_aranges"); elfstrdbg[ElfStrRelDebugLine] = addstring(shstrtab, ".rela.debug_line"); + elfstrdbg[ElfStrRelDebugFrame] = addstring(shstrtab, ".rela.debug_frame"); } else { elfstrdbg[ElfStrRelDebugInfo] = addstring(shstrtab, ".rel.debug_info"); elfstrdbg[ElfStrRelDebugAranges] = addstring(shstrtab, ".rel.debug_aranges"); elfstrdbg[ElfStrRelDebugLine] = addstring(shstrtab, ".rel.debug_line"); + elfstrdbg[ElfStrRelDebugFrame] = addstring(shstrtab, ".rel.debug_frame"); } infosym = lookup(".debug_info", 0); @@ -2424,6 +2446,9 @@ dwarfaddshstrings(Sym *shstrtab) linesym = lookup(".debug_line", 0); linesym->hide = 1; + + framesym = lookup(".debug_frame", 0); + framesym->hide = 1; } } @@ -2444,6 +2469,10 @@ dwarfaddelfsectionsyms() linesympos = cpos(); putelfsectionsym(linesym, 0); } + if(framesym != nil) { + framesympos = cpos(); + putelfsectionsym(framesym, 0); + } } static void @@ -2469,7 +2498,7 @@ dwarfaddelfrelocheader(int elfstr, ElfShdr *shdata, vlong off, vlong size) void dwarfaddelfheaders(void) { - ElfShdr *sh, *shinfo, *sharanges, *shline; + ElfShdr *sh, *shinfo, *sharanges, *shline, *shframe; if(debug['w']) // disable dwarf return; @@ -2496,6 +2525,9 @@ dwarfaddelfheaders(void) sh->off = frameo; sh->size = framesize; sh->addralign = 1; + if(framesympos > 0) + putelfsymshndx(framesympos, sh->shnum); + shframe = sh; sh = newElfShdr(elfstrdbg[ElfStrDebugInfo]); sh->type = SHT_PROGBITS; @@ -2548,6 +2580,9 @@ dwarfaddelfheaders(void) if(linerelocsize) dwarfaddelfrelocheader(ElfStrRelDebugLine, shline, linereloco, linerelocsize); + + if(framerelocsize) + dwarfaddelfrelocheader(ElfStrRelDebugFrame, shframe, framereloco, framerelocsize); } /* diff --git a/src/cmd/ld/elf.c b/src/cmd/ld/elf.c index daef5793f..6b3638ec5 100644 --- a/src/cmd/ld/elf.c +++ b/src/cmd/ld/elf.c @@ -864,6 +864,8 @@ elfemitreloc(void) elfrelocsect(segtext.sect, textp); for(sect=segtext.sect->next; sect!=nil; sect=sect->next) elfrelocsect(sect, datap); + for(sect=segrodata.sect; sect!=nil; sect=sect->next) + elfrelocsect(sect, datap); for(sect=segdata.sect; sect!=nil; sect=sect->next) elfrelocsect(sect, datap); } @@ -887,7 +889,12 @@ doelf(void) addstring(shstrtab, ".data"); addstring(shstrtab, ".bss"); addstring(shstrtab, ".noptrbss"); - if(linkmode == LinkExternal && HEADTYPE != Hopenbsd) + // generate .tbss section (except for OpenBSD where it's not supported) + // for dynamic internal linker or external linking, so that various + // binutils could correctly calculate PT_TLS size. + // see http://golang.org/issue/5200. + if(HEADTYPE != Hopenbsd) + if(!debug['d'] || linkmode == LinkExternal) addstring(shstrtab, ".tbss"); if(HEADTYPE == Hnetbsd) addstring(shstrtab, ".note.netbsd.ident"); @@ -898,14 +905,11 @@ doelf(void) addstring(shstrtab, ".elfdata"); addstring(shstrtab, ".rodata"); addstring(shstrtab, ".typelink"); - if(flag_shared) - addstring(shstrtab, ".data.rel.ro"); - addstring(shstrtab, ".gcdata"); - addstring(shstrtab, ".gcbss"); addstring(shstrtab, ".gosymtab"); addstring(shstrtab, ".gopclntab"); if(linkmode == LinkExternal) { + debug_s = debug['s']; debug['s'] = 0; debug['d'] = 1; @@ -913,8 +917,6 @@ doelf(void) addstring(shstrtab, ".rela.text"); addstring(shstrtab, ".rela.rodata"); addstring(shstrtab, ".rela.typelink"); - addstring(shstrtab, ".rela.gcdata"); - addstring(shstrtab, ".rela.gcbss"); addstring(shstrtab, ".rela.gosymtab"); addstring(shstrtab, ".rela.gopclntab"); addstring(shstrtab, ".rela.noptrdata"); @@ -923,8 +925,6 @@ doelf(void) addstring(shstrtab, ".rel.text"); addstring(shstrtab, ".rel.rodata"); addstring(shstrtab, ".rel.typelink"); - addstring(shstrtab, ".rel.gcdata"); - addstring(shstrtab, ".rel.gcbss"); addstring(shstrtab, ".rel.gosymtab"); addstring(shstrtab, ".rel.gopclntab"); addstring(shstrtab, ".rel.noptrdata"); @@ -934,6 +934,14 @@ doelf(void) addstring(shstrtab, ".note.GNU-stack"); } + if(flag_shared) { + addstring(shstrtab, ".init_array"); + if(thechar == '6') + addstring(shstrtab, ".rela.init_array"); + else + addstring(shstrtab, ".rel.init_array"); + } + if(!debug['s']) { addstring(shstrtab, ".symtab"); addstring(shstrtab, ".strtab"); @@ -1001,7 +1009,7 @@ doelf(void) s = lookup(".plt", 0); s->reachable = 1; - s->type = SELFROSECT; + s->type = SELFRXSECT; elfsetupplt(); @@ -1062,13 +1070,6 @@ doelf(void) elfwritedynent(s, DT_DEBUG, 0); - if(flag_shared) { - Sym *init_sym = lookup(LIBINITENTRY, 0); - if(init_sym->type != STEXT) - diag("entry not text: %s", init_sym->name); - elfwritedynentsym(s, DT_INIT, init_sym); - } - // Do not write DT_NULL. elfdynhash will finish it. } } @@ -1105,6 +1106,8 @@ asmbelfsetup(void) for(sect=segtext.sect; sect!=nil; sect=sect->next) elfshalloc(sect); + for(sect=segrodata.sect; sect!=nil; sect=sect->next) + elfshalloc(sect); for(sect=segdata.sect; sect!=nil; sect=sect->next) elfshalloc(sect); } @@ -1186,6 +1189,9 @@ asmbelf(vlong symo) case Hopenbsd: interpreter = openbsddynld; break; + case Hdragonfly: + interpreter = dragonflydynld; + break; } } resoff -= elfinterp(sh, startva, resoff, interpreter); @@ -1232,6 +1238,8 @@ asmbelf(vlong symo) USED(resoff); elfphload(&segtext); + if(segrodata.sect != nil) + elfphload(&segrodata); elfphload(&segdata); /* Dynamic linking sections */ @@ -1397,12 +1405,16 @@ elfobj: for(sect=segtext.sect; sect!=nil; sect=sect->next) elfshbits(sect); + for(sect=segrodata.sect; sect!=nil; sect=sect->next) + elfshbits(sect); for(sect=segdata.sect; sect!=nil; sect=sect->next) elfshbits(sect); if(linkmode == LinkExternal) { for(sect=segtext.sect; sect!=nil; sect=sect->next) elfshreloc(sect); + for(sect=segrodata.sect; sect!=nil; sect=sect->next) + elfshreloc(sect); for(sect=segdata.sect; sect!=nil; sect=sect->next) elfshreloc(sect); // add a .note.GNU-stack section to mark the stack as non-executable @@ -1412,6 +1424,16 @@ elfobj: sh->flags = 0; } + // generate .tbss section for dynamic internal linking (except for OpenBSD) + // external linking generates .tbss in data.c + if(linkmode == LinkInternal && !debug['d'] && HEADTYPE != Hopenbsd) { + sh = elfshname(".tbss"); + sh->type = SHT_NOBITS; + sh->addralign = PtrSize; + sh->size = -tlsoffset; + sh->flags = SHF_ALLOC | SHF_TLS | SHF_WRITE; + } + if(!debug['s']) { sh = elfshname(".symtab"); sh->type = SHT_SYMTAB; @@ -1442,6 +1464,8 @@ elfobj: eh->ident[EI_OSABI] = ELFOSABI_NETBSD; else if(HEADTYPE == Hopenbsd) eh->ident[EI_OSABI] = ELFOSABI_OPENBSD; + else if(HEADTYPE == Hdragonfly) + eh->ident[EI_OSABI] = ELFOSABI_NONE; if(PtrSize == 8) eh->ident[EI_CLASS] = ELFCLASS64; else @@ -1449,9 +1473,7 @@ elfobj: eh->ident[EI_DATA] = ELFDATA2LSB; eh->ident[EI_VERSION] = EV_CURRENT; - if(flag_shared) - eh->type = ET_DYN; - else if(linkmode == LinkExternal) + if(linkmode == LinkExternal) eh->type = ET_REL; else eh->type = ET_EXEC; diff --git a/src/cmd/ld/elf.h b/src/cmd/ld/elf.h index 24c0ac43e..5b2ff041a 100644 --- a/src/cmd/ld/elf.h +++ b/src/cmd/ld/elf.h @@ -569,6 +569,8 @@ typedef struct { #define R_ARM_GOT_PREL 96 #define R_ARM_GNU_VTENTRY 100 #define R_ARM_GNU_VTINHERIT 101 +#define R_ARM_TLS_IE32 107 +#define R_ARM_TLS_LE32 108 #define R_ARM_RSBREL32 250 #define R_ARM_THM_RPC22 251 #define R_ARM_RREL32 252 @@ -576,7 +578,7 @@ typedef struct { #define R_ARM_RPC24 254 #define R_ARM_RBASE 255 -#define R_ARM_COUNT 37 /* Count of defined relocation types. */ +#define R_ARM_COUNT 38 /* Count of defined relocation types. */ #define R_386_NONE 0 /* No relocation. */ @@ -1007,6 +1009,7 @@ extern char linuxdynld[]; extern char freebsddynld[]; extern char netbsddynld[]; extern char openbsddynld[]; +extern char dragonflydynld[]; int elfreloc1(Reloc*, vlong sectoff); void putelfsectionsyms(void); diff --git a/src/cmd/ld/go.c b/src/cmd/ld/go.c index 47fdbe944..39ffa3d87 100644 --- a/src/cmd/ld/go.c +++ b/src/cmd/ld/go.c @@ -37,13 +37,12 @@ static void imported(char *pkg, char *import); static int hashstr(char *name) { - int h; + uint32 h; char *cp; h = 0; for(cp = name; *cp; h += *cp++) h *= 1119; - // not if(h < 0) h = ~h, because gcc 4.3 -O2 miscompiles it. h &= 0xffffff; return h; } @@ -499,6 +498,9 @@ loadcgo(char *file, char *pkg, char *p, int n) local = expandpkg(local, pkg); s = lookup(local, 0); + if(flag_shared && s == lookup("main", 0)) + continue; + // export overrides import, for openbsd/cgo. // see issue 4878. if(s->dynimplib != nil) { @@ -613,7 +615,7 @@ markflood(void) } static char* -morename[] = +markextra[] = { "runtime.morestack", "runtime.morestackx", @@ -629,6 +631,12 @@ morename[] = "runtime.morestack32", "runtime.morestack40", "runtime.morestack48", + + // on arm, lock in the div/mod helpers too + "_div", + "_divu", + "_mod", + "_modu", }; static int @@ -674,10 +682,8 @@ deadcode(void) Bprint(&bso, "%5.2f deadcode\n", cputime()); mark(lookup(INITENTRY, 0)); - if(flag_shared) - mark(lookup(LIBINITENTRY, 0)); - for(i=0; i<nelem(morename); i++) - mark(lookup(morename[i], 0)); + for(i=0; i<nelem(markextra); i++) + mark(lookup(markextra[i], 0)); for(i=0; i<ndynexp; i++) mark(dynexp[i]); @@ -794,6 +800,8 @@ Zconv(Fmt *fp) return fmtstrcpy(fp, "<nil>"); se = s + strlen(s); + + // NOTE: Keep in sync with ../gc/go.c:/^Zconv. while(s < se) { n = chartorune(&r, s); s += n; @@ -822,6 +830,9 @@ Zconv(Fmt *fp) fmtrune(fp, '\\'); fmtrune(fp, r); break; + case 0xFEFF: // BOM, basically disallowed in source code + fmtstrcpy(fp, "\\uFEFF"); + break; } } return 0; diff --git a/src/cmd/ld/ldmacho.c b/src/cmd/ld/ldmacho.c index d384a5094..e0f5405f6 100644 --- a/src/cmd/ld/ldmacho.c +++ b/src/cmd/ld/ldmacho.c @@ -432,7 +432,7 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) int64 base; MachoSect *sect; MachoRel *rel; - Sym *s, *outer; + Sym *s, *s1, *outer; MachoCmd *c; MachoSymtab *symtab; MachoDysymtab *dsymtab; @@ -611,6 +611,8 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) if(!(sym->type&N_EXT)) v = version; s = lookup(name, v); + if(!(sym->type&N_EXT)) + s->dupok = 1; sym->sym = s; if(sym->sectnum == 0) // undefined continue; @@ -635,10 +637,6 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) outer->sub = s; s->outer = outer; s->value = sym->value - sect->addr; - if(i+1 < symtab->nsym) - s->size = (sym+1)->value - sym->value; - else - s->size = sect->addr + sect->size - sym->value; if(!(s->cgoexport & CgoExportDynamic)) s->dynimplib = nil; // satisfy dynimport if(outer->type == STEXT) { @@ -668,17 +666,26 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) sect = &c->seg.sect[i]; if((s = sect->sym) == S) continue; - if(s->sub) + if(s->sub) { s->sub = listsort(s->sub, valuecmp, offsetof(Sym, sub)); + + // assign sizes, now that we know symbols in sorted order. + for(s1 = s->sub; s1 != S; s1 = s1->sub) { + if(s1->sub) + s1->size = s1->sub->value - s1->value; + else + s1->size = s->value + s->size - s1->value; + } + } if(s->type == STEXT) { if(etextp) etextp->next = s; else textp = s; etextp = s; - for(s = s->sub; s != S; s = s->sub) { - etextp->next = s; - etextp = s; + for(s1 = s->sub; s1 != S; s1 = s1->sub) { + etextp->next = s1; + etextp = s1; } } } diff --git a/src/cmd/ld/ldpe.c b/src/cmd/ld/ldpe.c index 033e522f2..6bcda2cb6 100644 --- a/src/cmd/ld/ldpe.c +++ b/src/cmd/ld/ldpe.c @@ -468,7 +468,9 @@ readsym(PeObj *obj, int i, PeSym **y) break; case IMAGE_SYM_CLASS_NULL: case IMAGE_SYM_CLASS_STATIC: + case IMAGE_SYM_CLASS_LABEL: s = lookup(name, version); + s->dupok = 1; break; default: werrstr("%s: invalid symbol binding %d", sym->name, sym->sclass); diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index 0a6bd3e8f..da522dc0c 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -33,9 +33,17 @@ #include "lib.h" #include "../ld/elf.h" #include "../../pkg/runtime/stack.h" +#include "../../pkg/runtime/funcdata.h" #include <ar.h> +enum +{ + // Whether to assume that the external linker is "gold" + // (http://sourceware.org/ml/binutils/2008-03/msg00162.html). + AssumeGoldLinker = 0, +}; + int iconv(Fmt*); char symname[] = SYMDEF; @@ -45,6 +53,14 @@ int nlibdir = 0; static int maxlibdir = 0; static int cout = -1; +// symbol version, incremented each time a file is loaded. +// version==1 is reserved for savehist. +enum +{ + HistVersion = 1, +}; +int version = HistVersion; + // Set if we see an object compiled by the host compiler that is not // from a package that is known to support internal linking mode. static int externalobj = 0; @@ -75,7 +91,7 @@ Lflag(char *arg) void libinit(void) { - char *race; + char *suffix, *suffixsep; fmtinstall('i', iconv); fmtinstall('Y', Yconv); @@ -85,10 +101,16 @@ libinit(void) print("goarch is not known: %s\n", goarch); // add goroot to the end of the libdir list. - race = ""; - if(flag_race) - race = "_race"; - Lflag(smprint("%s/pkg/%s_%s%s", goroot, goos, goarch, race)); + suffix = ""; + suffixsep = ""; + if(flag_installsuffix != nil) { + suffixsep = "_"; + suffix = flag_installsuffix; + } else if(flag_race) { + suffixsep = "_"; + suffix = "race"; + } + Lflag(smprint("%s/pkg/%s_%s%s%s", goroot, goos, goarch, suffixsep, suffix)); // Unix doesn't like it when we write to a running (or, sometimes, // recently run) binary, so remove the output file before writing it. @@ -103,17 +125,14 @@ libinit(void) } if(INITENTRY == nil) { - INITENTRY = mal(strlen(goarch)+strlen(goos)+10); - sprint(INITENTRY, "_rt0_%s_%s", goarch, goos); - } - lookup(INITENTRY, 0)->type = SXREF; - if(flag_shared) { - if(LIBINITENTRY == nil) { - LIBINITENTRY = mal(strlen(goarch)+strlen(goos)+20); - sprint(LIBINITENTRY, "_rt0_%s_%s_lib", goarch, goos); + INITENTRY = mal(strlen(goarch)+strlen(goos)+20); + if(!flag_shared) { + sprint(INITENTRY, "_rt0_%s_%s", goarch, goos); + } else { + sprint(INITENTRY, "_rt0_%s_%s_lib", goarch, goos); } - lookup(LIBINITENTRY, 0)->type = SXREF; } + lookup(INITENTRY, 0)->type = SXREF; } void @@ -215,7 +234,7 @@ addlib(char *src, char *obj) if(p != nil) *p = '\0'; - if(debug['v']) + if(debug['v'] > 1) Bprint(&bso, "%5.2f addlib: %s %s pulls in %s\n", cputime(), obj, src, pname); addlibpath(src, obj, pname, name); @@ -292,7 +311,13 @@ void loadlib(void) { int i, w, x; - Sym *s; + Sym *s, *gmsym; + + if(flag_shared) { + s = lookup("runtime.islibrary", 0); + s->dupok = 1; + adduint8(s, 1); + } loadinternal("runtime"); if(thechar == '5') @@ -314,7 +339,7 @@ loadlib(void) } for(i=0; i<libraryp; i++) { - if(debug['v']) + if(debug['v'] > 1) Bprint(&bso, "%5.2f autolib: %s (from %s)\n", cputime(), library[i].file, library[i].objref); iscgo |= strcmp(library[i].pkg, "runtime/cgo") == 0; objfile(library[i].file, library[i].pkg); @@ -343,6 +368,15 @@ loadlib(void) } } + gmsym = lookup("runtime.tlsgm", 0); + gmsym->type = STLSBSS; + gmsym->size = 2*PtrSize; + gmsym->hide = 1; + if(linkmode == LinkExternal && iself && HEADTYPE != Hopenbsd) + gmsym->reachable = 1; + else + gmsym->reachable = 0; + // Now that we know the link mode, trim the dynexp list. x = CgoExportDynamic; if(linkmode == LinkExternal) @@ -417,7 +451,7 @@ objfile(char *file, char *pkg) pkg = smprint("%i", pkg); - if(debug['v']) + if(debug['v'] > 1) Bprint(&bso, "%5.2f ldobj: %s (%s)\n", cputime(), file, pkg); Bflush(&bso); f = Bopen(file, 0); @@ -554,6 +588,16 @@ ldhostobj(void (*ld)(Biobuf*, char*, int64, char*), Biobuf *f, char *pkg, int64 } } + // DragonFly declares errno with __thread, which results in a symbol + // type of R_386_TLS_GD or R_X86_64_TLSGD. The Go linker does not + // currently know how to handle TLS relocations, hence we have to + // force external linking for any libraries that link in code that + // uses errno. This can be removed if the Go linker ever supports + // these relocation types. + if(HEADTYPE == Hdragonfly) + if(strcmp(pkg, "net") == 0 || strcmp(pkg, "os/user") == 0) + isinternal = 0; + if(!isinternal) externalobj = 1; @@ -653,7 +697,7 @@ hostlink(void) p = strchr(p + 1, ' '); } - argv = malloc((10+nhostobj+nldflag+c)*sizeof argv[0]); + argv = malloc((13+nhostobj+nldflag+c)*sizeof argv[0]); argc = 0; if(extld == nil) extld = "gcc"; @@ -665,11 +709,25 @@ hostlink(void) case '6': argv[argc++] = "-m64"; break; + case '5': + argv[argc++] = "-marm"; + break; } - if(!debug['s']) + if(!debug['s'] && !debug_s) { argv[argc++] = "-gdwarf-2"; + } else { + argv[argc++] = "-s"; + } if(HEADTYPE == Hdarwin) argv[argc++] = "-Wl,-no_pie,-pagezero_size,4000000"; + + if(iself && AssumeGoldLinker) + argv[argc++] = "-Wl,--rosegment"; + + if(flag_shared) { + argv[argc++] = "-Wl,-Bsymbolic"; + argv[argc++] = "-shared"; + } argv[argc++] = "-o"; argv[argc++] = outfile; @@ -759,10 +817,10 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, char *file, int whence) pn = estrdup(pn); - c1 = Bgetc(f); - c2 = Bgetc(f); - c3 = Bgetc(f); - c4 = Bgetc(f); + c1 = BGETC(f); + c2 = BGETC(f); + c3 = BGETC(f); + c4 = BGETC(f); Bungetc(f); Bungetc(f); Bungetc(f); @@ -840,12 +898,12 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, char *file, int whence) /* skip over exports and other info -- ends with \n!\n */ import0 = Boffset(f); c1 = '\n'; // the last line ended in \n - c2 = Bgetc(f); - c3 = Bgetc(f); + c2 = BGETC(f); + c3 = BGETC(f); while(c1 != '\n' || c2 != '!' || c3 != '\n') { c1 = c2; c2 = c3; - c3 = Bgetc(f); + c3 = BGETC(f); if(c3 == Beof) goto eof; } @@ -899,7 +957,7 @@ _lookup(char *symb, int v, int creat) { Sym *s; char *p; - int32 h; + uint32 h; int c; h = v; @@ -1179,16 +1237,6 @@ zerosig(char *sp) s->sig = 0; } -int32 -Bget4(Biobuf *f) -{ - uchar p[4]; - - if(Bread(f, p, 4) != 4) - return 0; - return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); -} - void mywhatsys(void) { @@ -1334,100 +1382,192 @@ addsection(Segment *seg, char *name, int rwx) } void -pclntab(void) +addvarint(Sym *s, uint32 val) { - vlong oldpc; + int32 n; + uint32 v; + uchar *p; + + n = 0; + for(v = val; v >= 0x80; v >>= 7) + n++; + n++; + + symgrow(s, s->np+n); + + p = s->p + s->np - n; + for(v = val; v >= 0x80; v >>= 7) + *p++ = v | 0x80; + *p = v; +} + +// funcpctab appends to dst a pc-value table mapping the code in func to the values +// returned by valfunc parameterized by arg. The invocation of valfunc to update the +// current value is, for each p, +// +// val = valfunc(func, val, p, 0, arg); +// record val as value at p->pc; +// val = valfunc(func, val, p, 1, arg); +// +// where func is the function, val is the current value, p is the instruction being +// considered, and arg can be used to further parameterize valfunc. +void +funcpctab(Sym *dst, Sym *func, char *desc, int32 (*valfunc)(Sym*, int32, Prog*, int32, int32), int32 arg) +{ + int dbg, i, start; + int32 oldval, val, started; + uint32 delta; + vlong pc; Prog *p; - int32 oldlc, v, s; - Sym *sym; - uchar *bp; + + // To debug a specific function, uncomment second line and change name. + dbg = 0; + //dbg = strcmp(func->name, "main.main") == 0; + + debug['O'] += dbg; + + start = dst->np; + + if(debug['O']) + Bprint(&bso, "funcpctab %s -> %s [valfunc=%s]\n", func->name, dst->name, desc); + + val = -1; + oldval = val; + pc = func->value; - sym = lookup("pclntab", 0); - sym->type = SPCLNTAB; - sym->reachable = 1; - if(debug['s']) - return; + if(debug['O']) + Bprint(&bso, "%6llux %6d %P\n", pc, val, func->text); + + started = 0; + for(p=func->text; p != P; p = p->link) { + // Update val. If it's not changing, keep going. + val = valfunc(func, val, p, 0, arg); + if(val == oldval && started) { + val = valfunc(func, val, p, 1, arg); + if(debug['O']) + Bprint(&bso, "%6llux %6s %P\n", (vlong)p->pc, "", p); + continue; + } - oldpc = INITTEXT; - oldlc = 0; - for(cursym = textp; cursym != nil; cursym = cursym->next) { - for(p = cursym->text; p != P; p = p->link) { - if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) { - if(debug['O']) - Bprint(&bso, "%6llux %P\n", - (vlong)p->pc, p); - continue; - } + // If the pc of the next instruction is the same as the + // pc of this instruction, this instruction is not a real + // instruction. Keep going, so that we only emit a delta + // for a true instruction boundary in the program. + if(p->link && p->link->pc == p->pc) { + val = valfunc(func, val, p, 1, arg); if(debug['O']) - Bprint(&bso, "\t\t%6d", lcsize); - v = (p->pc - oldpc) / MINLC; - while(v) { - s = 127; - if(v < 127) - s = v; - symgrow(sym, lcsize+1); - bp = sym->p + lcsize; - *bp = s+128; /* 129-255 +pc */ - if(debug['O']) - Bprint(&bso, " pc+%d*%d(%d)", s, MINLC, s+128); - v -= s; - lcsize++; - } - s = p->line - oldlc; - oldlc = p->line; - oldpc = p->pc + MINLC; - if(s > 64 || s < -64) { - symgrow(sym, lcsize+5); - bp = sym->p + lcsize; - *bp++ = 0; /* 0 vv +lc */ - *bp++ = s>>24; - *bp++ = s>>16; - *bp++ = s>>8; - *bp = s; - if(debug['O']) { - if(s > 0) - Bprint(&bso, " lc+%d(%d,%d)\n", - s, 0, s); - else - Bprint(&bso, " lc%d(%d,%d)\n", - s, 0, s); - Bprint(&bso, "%6llux %P\n", - (vlong)p->pc, p); - } - lcsize += 5; - continue; - } - symgrow(sym, lcsize+1); - bp = sym->p + lcsize; - if(s > 0) { - *bp = 0+s; /* 1-64 +lc */ - if(debug['O']) { - Bprint(&bso, " lc+%d(%d)\n", s, 0+s); - Bprint(&bso, "%6llux %P\n", - (vlong)p->pc, p); - } - } else { - *bp = 64-s; /* 65-128 -lc */ - if(debug['O']) { - Bprint(&bso, " lc%d(%d)\n", s, 64-s); - Bprint(&bso, "%6llux %P\n", - (vlong)p->pc, p); - } - } - lcsize++; + Bprint(&bso, "%6llux %6s %P\n", (vlong)p->pc, "", p); + continue; + } + + // The table is a sequence of (value, pc) pairs, where each + // pair states that the given value is in effect from the current position + // up to the given pc, which becomes the new current position. + // To generate the table as we scan over the program instructions, + // we emit a "(value" when pc == func->value, and then + // each time we observe a change in value we emit ", pc) (value". + // When the scan is over, we emit the closing ", pc)". + // + // The table is delta-encoded. The value deltas are signed and + // transmitted in zig-zag form, where a complement bit is placed in bit 0, + // and the pc deltas are unsigned. Both kinds of deltas are sent + // as variable-length little-endian base-128 integers, + // where the 0x80 bit indicates that the integer continues. + + if(debug['O']) + Bprint(&bso, "%6llux %6d %P\n", (vlong)p->pc, val, p); + + if(started) { + addvarint(dst, (p->pc - pc) / MINLC); + pc = p->pc; } + delta = val - oldval; + if(delta>>31) + delta = 1 | ~(delta<<1); + else + delta <<= 1; + addvarint(dst, delta); + oldval = val; + started = 1; + val = valfunc(func, val, p, 1, arg); } - if(lcsize & 1) { - symgrow(sym, lcsize+1); - sym->p[lcsize] = 129; - lcsize++; + + if(started) { + if(debug['O']) + Bprint(&bso, "%6llux done\n", (vlong)func->value+func->size); + addvarint(dst, (func->value+func->size - pc) / MINLC); + addvarint(dst, 0); // terminator } - sym->size = lcsize; - lcsize = 0; - if(debug['v'] || debug['O']) - Bprint(&bso, "lcsize = %d\n", lcsize); - Bflush(&bso); + if(debug['O']) { + Bprint(&bso, "wrote %d bytes\n", dst->np - start); + for(i=start; i<dst->np; i++) + Bprint(&bso, " %02ux", dst->p[i]); + Bprint(&bso, "\n"); + } + + debug['O'] -= dbg; +} + +// pctofileline computes either the file number (arg == 0) +// or the line number (arg == 1) to use at p. +// Because p->lineno applies to p, phase == 0 (before p) +// takes care of the update. +static int32 +pctofileline(Sym *sym, int32 oldval, Prog *p, int32 phase, int32 arg) +{ + int32 f, l; + + if(p->as == ATEXT || p->as == ANOP || p->as == AUSEFIELD || p->line == 0 || phase == 1) + return oldval; + getline(sym->hist, p->line, &f, &l); + if(f == 0) { + // print("getline failed for %s %P\n", cursym->name, p); + return oldval; + } + if(arg == 0) + return f; + return l; +} + +// pctospadj computes the sp adjustment in effect. +// It is oldval plus any adjustment made by p itself. +// The adjustment by p takes effect only after p, so we +// apply the change during phase == 1. +static int32 +pctospadj(Sym *sym, int32 oldval, Prog *p, int32 phase, int32 arg) +{ + USED(arg); + USED(sym); + + if(oldval == -1) // starting + oldval = 0; + if(phase == 0) + return oldval; + if(oldval + p->spadj < -10000 || oldval + p->spadj > 1100000000) { + diag("overflow in spadj: %d + %d = %d", oldval, p->spadj, oldval + p->spadj); + errorexit(); + } + return oldval + p->spadj; +} + +// pctopcdata computes the pcdata value in effect at p. +// A PCDATA instruction sets the value in effect at future +// non-PCDATA instructions. +// Since PCDATA instructions have no width in the final code, +// it does not matter which phase we use for the update. +static int32 +pctopcdata(Sym *sym, int32 oldval, Prog *p, int32 phase, int32 arg) +{ + USED(sym); + + if(phase == 0 || p->as != APCDATA || p->from.offset != arg) + return oldval; + if((int32)p->to.offset != p->to.offset) { + diag("overflow in PCDATA instruction: %P", p); + errorexit(); + } + return p->to.offset; } #define LOG 5 @@ -1479,7 +1619,7 @@ le16(uchar *b) uint32 le32(uchar *b) { - return b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24; + return b[0] | b[1]<<8 | b[2]<<16 | (uint32)b[3]<<24; } uint64 @@ -1497,7 +1637,7 @@ be16(uchar *b) uint32 be32(uchar *b) { - return b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3]; + return (uint32)b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3]; } uint64 @@ -1900,24 +2040,8 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) if(s->text == nil) continue; - /* filenames first */ - for(a=s->autom; a; a=a->link) - if(a->type == D_FILE) - put(nil, a->asym->name, 'z', a->aoffset, 0, 0, 0); - else - if(a->type == D_FILE1) - put(nil, a->asym->name, 'Z', a->aoffset, 0, 0, 0); - put(s, s->name, 'T', s->value, s->size, s->version, s->gotype); - /* frame, locals, args, auto and param after */ - put(nil, ".frame", 'm', (uint32)s->text->to.offset+PtrSize, 0, 0, 0); - put(nil, ".locals", 'm', s->locals, 0, 0, 0); - if(s->text->textflag & NOSPLIT) - put(nil, ".args", 'm', ArgsSizeUnknown, 0, 0, 0); - else - put(nil, ".args", 'm', s->args, 0, 0, 0); - for(a=s->autom; a; a=a->link) { // Emit a or p according to actual offset, even if label is wrong. // This avoids negative offsets, which cannot be encoded. @@ -1947,7 +2071,7 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) } } if(debug['v'] || debug['n']) - Bprint(&bso, "symsize = %ud\n", symsize); + Bprint(&bso, "%5.2f symsize = %ud\n", cputime(), symsize); Bflush(&bso); } @@ -1974,3 +2098,438 @@ erealloc(void *p, long n) } return p; } + +// Saved history stacks encountered while reading archives. +// Keeping them allows us to answer virtual lineno -> file:line +// queries. +// +// The history stack is a complex data structure, described best at the +// bottom of http://plan9.bell-labs.com/magic/man2html/6/a.out. +// One of the key benefits of interpreting it here is that the runtime +// does not have to. Perhaps some day the compilers could generate +// a simpler linker input too. + +struct Hist +{ + int32 line; + int32 off; + Sym *file; +}; + +static Hist *histcopy; +static Hist *hist; +static int32 nhist; +static int32 maxhist; +static int32 histdepth; +static int32 nhistfile; +static Sym *filesyms; + +// savehist processes a single line, off history directive +// found in the input object file. +void +savehist(int32 line, int32 off) +{ + char tmp[1024]; + Sym *file; + Hist *h; + + // NOTE(rsc): We used to do the copyhistfrog first and this + // condition was if(tmp[0] != '\0') to check for an empty string, + // implying that histfrogp == 0, implying that this is a history pop. + // However, on Windows in the misc/cgo test, the linker is + // presented with an ANAME corresponding to an empty string, + // that ANAME ends up being the only histfrog, and thus we have + // a situation where histfrogp > 0 (not a pop) but the path we find + // is the empty string. Really that shouldn't happen, but it doesn't + // seem to be bothering anyone yet, and it's easier to fix the condition + // to test histfrogp than to track down where that empty string is + // coming from. Probably it is coming from go tool pack's P command. + if(histfrogp > 0) { + tmp[0] = '\0'; + copyhistfrog(tmp, sizeof tmp); + file = lookup(tmp, HistVersion); + if(file->type != SFILEPATH) { + file->value = ++nhistfile; + file->type = SFILEPATH; + file->next = filesyms; + filesyms = file; + } + } else + file = nil; + + if(file != nil && line == 1 && off == 0) { + // start of new stack + if(histdepth != 0) { + diag("history stack phase error: unexpected start of new stack depth=%d file=%s", histdepth, tmp); + errorexit(); + } + nhist = 0; + histcopy = nil; + } + + if(nhist >= maxhist) { + if(maxhist == 0) + maxhist = 1; + maxhist *= 2; + hist = erealloc(hist, maxhist*sizeof hist[0]); + } + h = &hist[nhist++]; + h->line = line; + h->off = off; + h->file = file; + + if(file != nil) { + if(off == 0) + histdepth++; + } else { + if(off != 0) { + diag("history stack phase error: bad offset in pop"); + errorexit(); + } + histdepth--; + } +} + +// gethist returns the history stack currently in effect. +// The result is valid indefinitely. +Hist* +gethist(void) +{ + if(histcopy == nil) { + if(nhist == 0) + return nil; + histcopy = mal((nhist+1)*sizeof hist[0]); + memmove(histcopy, hist, nhist*sizeof hist[0]); + histcopy[nhist].line = -1; + } + return histcopy; +} + +typedef struct Hstack Hstack; +struct Hstack +{ + Hist *h; + int delta; +}; + +// getline sets *f to the file number and *l to the line number +// of the virtual line number line according to the history stack h. +void +getline(Hist *h, int32 line, int32 *f, int32 *l) +{ + Hstack stk[100]; + int nstk, start; + Hist *top, *h0; + static Hist *lasth; + static int32 laststart, lastend, lastdelta, lastfile; + + h0 = h; + *f = 0; + *l = 0; + start = 0; + if(h == nil || line == 0) { + print("%s: getline: h=%p line=%d\n", cursym->name, h, line); + return; + } + + // Cache span used during last lookup, so that sequential + // translation of line numbers in compiled code is efficient. + if(!debug['O'] && lasth == h && laststart <= line && line < lastend) { + *f = lastfile; + *l = line - lastdelta; + return; + } + + if(debug['O']) + print("getline %d laststart=%d lastend=%d\n", line, laststart, lastend); + + nstk = 0; + for(; h->line != -1; h++) { + if(debug['O']) + print("\t%s %d %d\n", h->file ? h->file->name : "?", h->line, h->off); + + if(h->line > line) { + if(nstk == 0) { + diag("history stack phase error: empty stack at line %d", (int)line); + errorexit(); + } + top = stk[nstk-1].h; + lasth = h; + lastfile = top->file->value; + laststart = start; + lastend = h->line; + lastdelta = stk[nstk-1].delta; + *f = lastfile; + *l = line - lastdelta; + if(debug['O']) + print("\tgot %d %d [%d %d %d]\n", *f, *l, laststart, lastend, lastdelta); + return; + } + if(h->file == nil) { + // pop included file + if(nstk == 0) { + diag("history stack phase error: stack underflow"); + errorexit(); + } + nstk--; + if(nstk > 0) + stk[nstk-1].delta += h->line - stk[nstk].h->line; + start = h->line; + } else if(h->off == 0) { + // push included file + if(nstk >= nelem(stk)) { + diag("history stack phase error: stack overflow"); + errorexit(); + } + start = h->line; + stk[nstk].h = h; + stk[nstk].delta = h->line - 1; + nstk++; + } else { + // #line directive + if(nstk == 0) { + diag("history stack phase error: stack underflow"); + errorexit(); + } + stk[nstk-1].h = h; + stk[nstk-1].delta = h->line - h->off; + start = h->line; + } + if(debug['O']) + print("\t\tnstk=%d delta=%d\n", nstk, stk[nstk].delta); + } + + diag("history stack phase error: cannot find line for %d", line); + nstk = 0; + for(h = h0; h->line != -1; h++) { + print("\t%d %d %s\n", h->line, h->off, h->file ? h->file->name : ""); + if(h->file == nil) + nstk--; + else if(h->off == 0) + nstk++; + } +} + +// defgostring returns a symbol for the Go string containing text. +Sym* +defgostring(char *text) +{ + char *p; + Sym *s; + int32 n; + + n = strlen(text); + p = smprint("go.string.\"%Z\"", text); + s = lookup(p, 0); + if(s->size == 0) { + s->type = SGOSTRING; + s->reachable = 1; + s->size = 2*PtrSize+n; + symgrow(s, 2*PtrSize+n); + setaddrplus(s, 0, s, 2*PtrSize); + setuintxx(s, PtrSize, n, PtrSize); + memmove(s->p+2*PtrSize, text, n); + } + s->reachable = 1; + return s; +} + +// addpctab appends to f a pc-value table, storing its offset at off. +// The pc-value table is for func and reports the value of valfunc +// parameterized by arg. +static int32 +addpctab(Sym *f, int32 off, Sym *func, char *desc, int32 (*valfunc)(Sym*, int32, Prog*, int32, int32), int32 arg) +{ + int32 start; + + start = f->np; + funcpctab(f, func, desc, valfunc, arg); + if(start == f->np) { + // no table + return setuint32(f, off, 0); + } + if((int32)start > (int32)f->np) { + diag("overflow adding pc-table: symbol too large"); + errorexit(); + } + return setuint32(f, off, start); +} + +static int32 +ftabaddstring(Sym *ftab, char *s) +{ + int32 n, start; + + n = strlen(s)+1; + start = ftab->np; + symgrow(ftab, start+n+1); + strcpy((char*)ftab->p + start, s); + return start; +} + +// pclntab initializes the pclntab symbol with +// runtime function and file name information. +void +pclntab(void) +{ + Prog *p; + int32 i, n, nfunc, start, funcstart; + uint32 *havepc, *havefunc; + Sym *ftab, *s; + int32 npcdata, nfuncdata, off, end; + int64 funcdata_bytes; + + funcdata_bytes = 0; + ftab = lookup("pclntab", 0); + ftab->type = SPCLNTAB; + ftab->reachable = 1; + + // See golang.org/s/go12symtab for the format. Briefly: + // 8-byte header + // nfunc [PtrSize bytes] + // function table, alternating PC and offset to func struct [each entry PtrSize bytes] + // end PC [PtrSize bytes] + // offset to file table [4 bytes] + nfunc = 0; + for(cursym = textp; cursym != nil; cursym = cursym->next) + nfunc++; + symgrow(ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize+4); + setuint32(ftab, 0, 0xfffffffb); + setuint8(ftab, 6, MINLC); + setuint8(ftab, 7, PtrSize); + setuintxx(ftab, 8, nfunc, PtrSize); + + nfunc = 0; + for(cursym = textp; cursym != nil; cursym = cursym->next, nfunc++) { + funcstart = ftab->np; + funcstart += -ftab->np & (PtrSize-1); + + setaddr(ftab, 8+PtrSize+nfunc*2*PtrSize, cursym); + setuintxx(ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize, funcstart, PtrSize); + + npcdata = 0; + nfuncdata = 0; + for(p = cursym->text; p != P; p = p->link) { + if(p->as == APCDATA && p->from.offset >= npcdata) + npcdata = p->from.offset+1; + if(p->as == AFUNCDATA && p->from.offset >= nfuncdata) + nfuncdata = p->from.offset+1; + } + + // fixed size of struct, checked below + off = funcstart; + end = funcstart + PtrSize + 3*4 + 5*4 + npcdata*4 + nfuncdata*PtrSize; + if(nfuncdata > 0 && (end&(PtrSize-1))) + end += 4; + symgrow(ftab, end); + + // entry uintptr + off = setaddr(ftab, off, cursym); + + // name int32 + off = setuint32(ftab, off, ftabaddstring(ftab, cursym->name)); + + // args int32 + // TODO: Move into funcinfo. + if(cursym->text == nil) + off = setuint32(ftab, off, ArgsSizeUnknown); + else + off = setuint32(ftab, off, cursym->args); + + // frame int32 + // TODO: Remove entirely. The pcsp table is more precise. + // This is only used by a fallback case during stack walking + // when a called function doesn't have argument information. + // We need to make sure everything has argument information + // and then remove this. + if(cursym->text == nil) + off = setuint32(ftab, off, 0); + else + off = setuint32(ftab, off, (uint32)cursym->text->to.offset+PtrSize); + + // pcsp table (offset int32) + off = addpctab(ftab, off, cursym, "pctospadj", pctospadj, 0); + + // pcfile table (offset int32) + off = addpctab(ftab, off, cursym, "pctofileline file", pctofileline, 0); + + // pcln table (offset int32) + off = addpctab(ftab, off, cursym, "pctofileline line", pctofileline, 1); + + // npcdata int32 + off = setuint32(ftab, off, npcdata); + + // nfuncdata int32 + off = setuint32(ftab, off, nfuncdata); + + // tabulate which pc and func data we have. + n = ((npcdata+31)/32 + (nfuncdata+31)/32)*4; + havepc = mal(n); + havefunc = havepc + (npcdata+31)/32; + for(p = cursym->text; p != P; p = p->link) { + if(p->as == AFUNCDATA) { + if((havefunc[p->from.offset/32]>>(p->from.offset%32))&1) + diag("multiple definitions for FUNCDATA $%d", p->from.offset); + havefunc[p->from.offset/32] |= 1<<(p->from.offset%32); + } + if(p->as == APCDATA) + havepc[p->from.offset/32] |= 1<<(p->from.offset%32); + } + + // pcdata. + for(i=0; i<npcdata; i++) { + if(!(havepc[i/32]>>(i%32))&1) { + off = setuint32(ftab, off, 0); + continue; + } + off = addpctab(ftab, off, cursym, "pctopcdata", pctopcdata, i); + } + + unmal(havepc, n); + + // funcdata, must be pointer-aligned and we're only int32-aligned. + // Unlike pcdata, can gather in a single pass. + // Missing funcdata will be 0 (nil pointer). + if(nfuncdata > 0) { + if(off&(PtrSize-1)) + off += 4; + for(p = cursym->text; p != P; p = p->link) { + if(p->as == AFUNCDATA) { + i = p->from.offset; + if(p->to.type == D_CONST) + setuintxx(ftab, off+PtrSize*i, p->to.offset, PtrSize); + else { + // TODO: Dedup. + funcdata_bytes += p->to.sym->size; + setaddrplus(ftab, off+PtrSize*i, p->to.sym, p->to.offset); + } + } + } + off += nfuncdata*PtrSize; + } + + if(off != end) { + diag("bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d)", funcstart, off, end, npcdata, nfuncdata); + errorexit(); + } + + // Final entry of table is just end pc. + if(cursym->next == nil) + setaddrplus(ftab, 8+PtrSize+(nfunc+1)*2*PtrSize, cursym, cursym->size); + } + + // Start file table. + start = ftab->np; + start += -ftab->np & (PtrSize-1); + setuint32(ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize, start); + + symgrow(ftab, start+(nhistfile+1)*4); + setuint32(ftab, start, nhistfile); + for(s = filesyms; s != S; s = s->next) + setuint32(ftab, start + s->value*4, ftabaddstring(ftab, s->name)); + + ftab->size = ftab->np; + + if(debug['v']) + Bprint(&bso, "%5.2f pclntab=%lld bytes, funcdata total %lld bytes\n", cputime(), (vlong)ftab->size, (vlong)funcdata_bytes); +} diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h index e552deb02..be95bb46e 100644 --- a/src/cmd/ld/lib.h +++ b/src/cmd/ld/lib.h @@ -33,33 +33,43 @@ enum Sxxx, /* order here is order in output file */ + /* readonly, executable */ STEXT, + SELFRXSECT, + + /* readonly, non-executable */ STYPE, SSTRING, SGOSTRING, + SGOFUNC, SRODATA, + SFUNCTAB, STYPELINK, - SSYMTAB, + SSYMTAB, // TODO: move to unmapped section SPCLNTAB, SELFROSECT, + + /* writable, non-executable */ SMACHOPLT, SELFSECT, SMACHO, /* Mach-O __nl_symbol_ptr */ SMACHOGOT, SNOPTRDATA, - SDATARELRO, + SINITARR, SDATA, SWINDOWS, SBSS, SNOPTRBSS, STLSBSS, + /* not mapped */ SXREF, SMACHOSYMSTR, SMACHOSYMTAB, SMACHOINDIRECTPLT, SMACHOINDIRECTGOT, SFILE, + SFILEPATH, SCONST, SDYNIMPORT, SHOSTOBJ, @@ -71,13 +81,6 @@ enum NHASH = 100003, }; -enum -{ - // This value is known to the garbage collector and should be kept in - // sync with runtime/pkg/runtime.h - ArgsSizeUnknown = 0x80000000 -}; - typedef struct Library Library; struct Library { @@ -122,9 +125,14 @@ struct Section uvlong rellen; }; +typedef struct Hist Hist; + +#pragma incomplete struct Hist + extern char symname[]; extern char **libdir; extern int nlibdir; +extern int version; EXTERN char* INITENTRY; EXTERN char* thestring; @@ -152,6 +160,7 @@ EXTERN char** ldflag; EXTERN int havedynamic; EXTERN int iscgo; EXTERN int elfglobalsymndx; +EXTERN char* flag_installsuffix; EXTERN int flag_race; EXTERN int flag_shared; EXTERN char* tracksym; @@ -159,6 +168,7 @@ EXTERN char* interpreter; EXTERN char* tmpdir; EXTERN char* extld; EXTERN char* extldflags; +EXTERN int debug_s; // backup old value of debug['s'] enum { @@ -176,6 +186,7 @@ enum }; EXTERN Segment segtext; +EXTERN Segment segrodata; EXTERN Segment segdata; EXTERN Segment segdwarf; @@ -185,6 +196,9 @@ void addlibpath(char *srcref, char *objref, char *file, char *pkg); Section* addsection(Segment*, char*, int); void copyhistfrog(char *buf, int nbuf); void addhist(int32 line, int type); +void savehist(int32 line, int32 off); +Hist* gethist(void); +void getline(Hist*, int32 line, int32 *f, int32 *l); void asmlc(void); void histtoauto(void); void collapsefrog(Sym *s); @@ -199,7 +213,6 @@ double ieeedtod(Ieee *e); void undefsym(Sym *s); void zerosig(char *sp); void readundefs(char *f, int t); -int32 Bget4(Biobuf *f); void loadlib(void); void errorexit(void); void mangle(char*); @@ -211,7 +224,6 @@ void Lflag(char *arg); void usage(void); void adddynrel(Sym*, Reloc*); void adddynrela(Sym*, Sym*, Reloc*); -Sym* lookuprel(void); void ldobj1(Biobuf *f, char*, int64 len, char *pn); void ldobj(Biobuf*, char*, int64, char*, char*, int); void ldelf(Biobuf*, char*, int64, char*); @@ -242,10 +254,11 @@ vlong addpcrelplus(Sym*, Sym*, vlong); vlong addsize(Sym*, Sym*); vlong setaddrplus(Sym*, vlong, Sym*, vlong); vlong setaddr(Sym*, vlong, Sym*); -void setuint8(Sym*, vlong, uint8); -void setuint16(Sym*, vlong, uint16); -void setuint32(Sym*, vlong, uint32); -void setuint64(Sym*, vlong, uint64); +vlong setuint8(Sym*, vlong, uint8); +vlong setuint16(Sym*, vlong, uint16); +vlong setuint32(Sym*, vlong, uint32); +vlong setuint64(Sym*, vlong, uint64); +vlong setuintxx(Sym*, vlong, uint64, vlong); void asmsym(void); void asmelfsym(void); void asmplan9sym(void); @@ -275,6 +288,7 @@ void hostobjs(void); void hostlink(void); char* estrdup(char*); void* erealloc(void*, long); +Sym* defgostring(char*); int pathchar(void); void* mal(uint32); @@ -330,6 +344,7 @@ enum { Hfreebsd, // FreeBSD ELF Hwindows, // MS Windows PE Hopenbsd, // OpenBSD ELF + Hdragonfly, // DragonFly ELF }; typedef struct Header Header; diff --git a/src/cmd/ld/pe.c b/src/cmd/ld/pe.c index 090d083f5..7b9a596fc 100644 --- a/src/cmd/ld/pe.c +++ b/src/cmd/ld/pe.c @@ -151,6 +151,7 @@ peinit(void) // some mingw libs depend on this symbol, for example, FindPESectionByName xdefine("__image_base__", SDATA, PEBASE); + xdefine("_image_base__", SDATA, PEBASE); } static void diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c index 7c8ba642f..c9b4657f7 100644 --- a/src/cmd/ld/symtab.c +++ b/src/cmd/ld/symtab.c @@ -181,22 +181,13 @@ asmelfsym(void) genasmsym(putelfsym); if(linkmode == LinkExternal && HEADTYPE != Hopenbsd) { - s = lookup("runtime.m", 0); + s = lookup("runtime.tlsgm", 0); if(s->sect == nil) { cursym = nil; diag("missing section for %s", s->name); errorexit(); } - putelfsyment(putelfstr(s->name), 0, PtrSize, (STB_LOCAL<<4)|STT_TLS, s->sect->elfsect->shnum, 0); - s->elfsym = numelfsym++; - - s = lookup("runtime.g", 0); - if(s->sect == nil) { - cursym = nil; - diag("missing section for %s", s->name); - errorexit(); - } - putelfsyment(putelfstr(s->name), PtrSize, PtrSize, (STB_LOCAL<<4)|STT_TLS, s->sect->elfsect->shnum, 0); + putelfsyment(putelfstr(s->name), 0, 2*PtrSize, (STB_LOCAL<<4)|STT_TLS, s->sect->elfsect->shnum, 0); s->elfsym = numelfsym++; } @@ -371,7 +362,7 @@ putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ) Reloc *rel; USED(size); - + // type byte if('A' <= t && t <= 'Z') c = t - 'A' + (ver ? 26 : 0); @@ -466,7 +457,8 @@ putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ) void symtab(void) { - Sym *s, *symtype, *symtypelink, *symgostring; + Sym *s, *symtype, *symtypelink, *symgostring, *symgofunc; + dosymtype(); // Define these so that they'll get put into the symbol table. @@ -477,12 +469,6 @@ symtab(void) xdefine("etypelink", SRODATA, 0); xdefine("rodata", SRODATA, 0); xdefine("erodata", SRODATA, 0); - if(flag_shared) { - xdefine("datarelro", SDATARELRO, 0); - xdefine("edatarelro", SDATARELRO, 0); - } - xdefine("egcdata", STYPE, 0); - xdefine("egcbss", STYPE, 0); xdefine("noptrdata", SNOPTRDATA, 0); xdefine("enoptrdata", SNOPTRDATA, 0); xdefine("data", SDATA, 0); @@ -495,6 +481,19 @@ symtab(void) xdefine("epclntab", SRODATA, 0); xdefine("esymtab", SRODATA, 0); + // garbage collection symbols + s = lookup("gcdata", 0); + s->type = SRODATA; + s->size = 0; + s->reachable = 1; + xdefine("egcdata", SRODATA, 0); + + s = lookup("gcbss", 0); + s->type = SRODATA; + s->size = 0; + s->reachable = 1; + xdefine("egcbss", SRODATA, 0); + // pseudo-symbols to mark locations of type, string, and go string data. s = lookup("type.*", 0); s->type = STYPE; @@ -508,6 +507,12 @@ symtab(void) s->reachable = 1; symgostring = s; + s = lookup("go.func.*", 0); + s->type = SGOFUNC; + s->size = 0; + s->reachable = 1; + symgofunc = s; + symtypelink = lookup("typelink", 0); symt = lookup("symtab", 0); @@ -537,6 +542,11 @@ symtab(void) s->hide = 1; s->outer = symgostring; } + if(strncmp(s->name, "go.func.", 8) == 0) { + s->type = SGOFUNC; + s->hide = 1; + s->outer = symgofunc; + } } if(debug['s']) diff --git a/src/cmd/ld/textflag.h b/src/cmd/ld/textflag.h new file mode 100644 index 000000000..1d62db736 --- /dev/null +++ b/src/cmd/ld/textflag.h @@ -0,0 +1,21 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file defines flags attached to various functions +// and data objects. The compilers, assemblers, and linker must +// all agree on these values. + +// Don't profile the marked routine. This flag is deprecated. +#define NOPROF 1 +// It is ok for the linker to get multiple of these symbols. It will +// pick one of the duplicates to use. +#define DUPOK 2 +// Don't insert stack check preamble. +#define NOSPLIT 4 +// Put this data in a read-only section. +#define RODATA 8 +// This data contains no pointers. +#define NOPTR 16 +// This is a wrapper function and should not count as disabling 'recover'. +#define WRAPPER 32 diff --git a/src/cmd/nm/doc.go b/src/cmd/nm/doc.go index 480c1c3dd..8e88e2e63 100644 --- a/src/cmd/nm/doc.go +++ b/src/cmd/nm/doc.go @@ -17,7 +17,7 @@ This implementation adds the flag -S, which prints each symbol's size in decimal after its address. Usage: - go tool nm [-aghnsTu] file + go tool nm [-aghnsSTu] file */ package main diff --git a/src/cmd/nm/nm.c b/src/cmd/nm/nm.c index aa056b882..820942426 100644 --- a/src/cmd/nm/nm.c +++ b/src/cmd/nm/nm.c @@ -72,7 +72,7 @@ void zenter(Sym*); void usage(void) { - fprint(2, "usage: nm [-aghnsTu] file ...\n"); + fprint(2, "usage: nm [-aghnsSTu] file ...\n"); exits("usage"); } @@ -299,6 +299,37 @@ psym(Sym *s, void* p) symptr[nsym++] = s; } +const char *skipnames[] = { + "bss", + "data", + "ebss", + "edata", + "egcbss", + "egcdata", + "enoptrbss", + "enoptrdata", + "epclntab", + "erodata", + "esymtab", + "etext", + "etypelink", + "noptrbss", + "noptrdata", + "rodata", + "text", +}; + +int +skipsize(char *name) +{ + int i; + + for(i=0; i<nelem(skipnames); i++) + if(strcmp(skipnames[i], name) == 0) + return 1; + return 0; +} + void printsyms(Sym **symptr, long nsym) { @@ -332,12 +363,12 @@ printsyms(Sym **symptr, long nsym) Bprint(&bout, "%*llux ", wid, s->value); else Bprint(&bout, "%*s ", wid, ""); - if(Sflag) { + if(Sflag && !skipsize(cp)) { vlong siz; siz = 0; for(j=i+1; j<nsym; j++) { - if(symptr[j]->type != 'a' && symptr[j]->type != 'p') { + if(!skipsize(symptr[j]->name) && symptr[j]->type != 'a' && symptr[j]->type != 'p') { siz = symptr[j]->value - s->value; break; } diff --git a/src/cmd/pack/ar.c b/src/cmd/pack/ar.c index 7053f841f..5b300dbb9 100644 --- a/src/cmd/pack/ar.c +++ b/src/cmd/pack/ar.c @@ -700,7 +700,7 @@ scanobj(Biobuf *b, Arfile *ap, long size) // the ! comes immediately. Catch that so we can avoid // the call to scanpkg below, since scanpkg assumes that the // Go metadata is present. - if(Bgetc(b) == '!') + if(BGETC(b) == '!') goobject = 0; Bseek(b, offset1, 0); @@ -807,13 +807,13 @@ scanpkg(Biobuf *b, long size) * scan until $$ */ for (n=0; n<size; ) { - c = Bgetc(b); + c = BGETC(b); if(c == Beof) break; n++; if(c != '$') continue; - c = Bgetc(b); + c = BGETC(b); if(c == Beof) break; n++; @@ -828,7 +828,7 @@ scanpkg(Biobuf *b, long size) foundstart: /* found $$; skip rest of line */ - while((c = Bgetc(b)) != '\n') + while((c = BGETC(b)) != '\n') if(c == Beof) goto bad; @@ -937,21 +937,12 @@ objsym(Sym *s, void *p) int hashstr(char *name) { - int h; + uint32 h; char *cp; h = 0; for(cp = name; *cp; h += *cp++) h *= 1119; - - // the code used to say - // if(h < 0) - // h = ~h; - // but on gcc 4.3 with -O2 on some systems, - // the if(h < 0) gets compiled away as not possible. - // use a mask instead, leaving plenty of bits but - // definitely not the sign bit. - return h & 0xfffffff; } @@ -1263,7 +1254,7 @@ rl(int fd) wrsym(&b, len, aend->sym); if(symdefsize&0x01) - Bputc(&b, 0); + BPUTC(&b, 0); if (gflag) { len = pkgdefsize; @@ -1283,7 +1274,7 @@ rl(int fd) if (Bwrite(&b, pkgdefdata, pkgdefsize) != pkgdefsize) wrerr(); if(len&0x01) - Bputc(&b, 0); + BPUTC(&b, 0); } Bterm(&b); } @@ -1297,12 +1288,9 @@ wrsym(Biobuf *bp, long offset, Arsymref *as) int off; while(as) { - Bputc(bp, as->type); + BPUTC(bp, as->type); off = as->offset+offset; - Bputc(bp, off); - Bputc(bp, off>>8); - Bputc(bp, off>>16); - Bputc(bp, off>>24); + BPUTLE4(bp, off); if (Bwrite(bp, as->name, as->len+1) != as->len+1) wrerr(); as = as->next; @@ -1454,7 +1442,7 @@ select(int *ap, long mode) n = *ap++; while(--n>=0 && (mode&*ap++)==0) ap++; - Bputc(&bout, *ap); + BPUTC(&bout, *ap); } /* @@ -1506,7 +1494,7 @@ arread(Biobuf *b, Armember *bp) /* read an image into a member buffer */ rderr(); } if(bp->size&1) - Bgetc(b); + BGETC(b); } /* @@ -1618,6 +1606,25 @@ int (*reader[256])(Biobuf*, Prog*) = { [Obj386] = _read8, }; +#define isdelim(c) ((c) == '/' || (c) == '\\') + +/* + * check if p is start of windows full path, like C:\ or c:/. + * return 1 if so. also set drive parameter to its + * upper-case drive letter. + */ +int +iswinpathstart(char *p, char *drive) +{ + if('A' <= p[0] || p[0] <= 'Z') + *drive = p[0]; + else if('a' <= p[0] || p[0] <= 'z') + *drive = p[0] - ('a' - 'A'); + else + return 0; + return p[1] == ':' && isdelim(p[2]); +} + /* * copy b into bp->member but rewrite object * during copy to drop prefix from all file names. @@ -1630,7 +1637,7 @@ arread_cutprefix(Biobuf *b, Armember *bp) vlong offset, o, end; int n, t; int (*rd)(Biobuf*, Prog*); - char *w, *inprefix; + char *w, *inprefix, d1, d2; Prog p; offset = Boffset(b); @@ -1666,12 +1673,15 @@ arread_cutprefix(Biobuf *b, Armember *bp) if(inprefix == nil && prefix[0] == '/' && p.id[1] == '/' && p.id[2] == '\0') { // leading / inprefix = prefix+1; + } else if(inprefix == nil && iswinpathstart(prefix, &d1) && iswinpathstart(p.id + 1, &d2) && d1 == d2 && p.id[4] == '\0') { + // leading c:\ ... + inprefix = prefix+3; } else if(inprefix != nil) { // handle subsequent elements n = strlen(p.id+1); - if(strncmp(p.id+1, inprefix, n) == 0 && (inprefix[n] == '/' || inprefix[n] == '\0')) { + if(strncmp(p.id+1, inprefix, n) == 0 && (isdelim(inprefix[n]) || inprefix[n] == '\0')) { inprefix += n; - if(inprefix[0] == '/') + if(isdelim(inprefix[0])) inprefix++; } } @@ -1712,6 +1722,6 @@ arread_cutprefix(Biobuf *b, Armember *bp) strncpy(bp->hdr.fmag, ARFMAG, 2); Bseek(b, end, 0); if(Boffset(b)&1) - Bgetc(b); + BGETC(b); return 1; } diff --git a/src/cmd/vet/Makefile b/src/cmd/vet/Makefile deleted file mode 100644 index 67c7e1997..000000000 --- a/src/cmd/vet/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright 2010 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -# Assumes go/types is installed -test testshort: - go build -tags 'vet_test gotypes' - ../../../test/errchk ./vet -printfuncs='Warn:1,Warnf:1' test_*.go test_*.s - -test_notypes: - go build -tags 'vet_test' - # Only those tests that do not depend on types. - # Excluded: test_print.go - ../../../test/errchk ./vet -printfuncs='Warn:1,Warnf:1' test_asm.go test_assign.go test_atomic.go test_buildtag.go test_buildtag_bad.go test_deadcode.go test_method.go test_rangeloop.go test_structtag.go test_taglit.go test_*.s diff --git a/src/cmd/vet/asmdecl.go b/src/cmd/vet/asmdecl.go deleted file mode 100644 index c23514c28..000000000 --- a/src/cmd/vet/asmdecl.go +++ /dev/null @@ -1,533 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Identify mismatches between assembly files and Go func declarations. - -package main - -import ( - "bytes" - "fmt" - "go/ast" - "go/token" - "regexp" - "strconv" - "strings" -) - -// 'kind' is a kind of assembly variable. -// The kinds 1, 2, 4, 8 stand for values of that size. -type asmKind int - -// These special kinds are not valid sizes. -const ( - asmString asmKind = 100 + iota - asmSlice - asmInterface - asmEmptyInterface -) - -// An asmArch describes assembly parameters for an architecture -type asmArch struct { - name string - ptrSize int - intSize int - bigEndian bool -} - -// An asmFunc describes the expected variables for a function on a given architecture. -type asmFunc struct { - arch *asmArch - size int // size of all arguments - vars map[string]*asmVar - varByOffset map[int]*asmVar -} - -// An asmVar describes a single assembly variable. -type asmVar struct { - name string - kind asmKind - typ string - off int - size int - inner []*asmVar -} - -var ( - asmArch386 = asmArch{"386", 4, 4, false} - asmArchArm = asmArch{"arm", 4, 4, false} - asmArchAmd64 = asmArch{"amd64", 8, 8, false} - - arches = []*asmArch{ - &asmArch386, - &asmArchArm, - &asmArchAmd64, - } -) - -var ( - re = regexp.MustCompile - asmPlusBuild = re(`//\s+\+build\s+([^\n]+)`) - asmTEXT = re(`\bTEXT\b.*·([^\(]+)\(SB\)(?:\s*,\s*([0-9]+))?(?:\s*,\s*\$([0-9]+)(?:-([0-9]+))?)?`) - asmDATA = re(`\b(DATA|GLOBL)\b`) - asmNamedFP = re(`([a-zA-Z0-9_\xFF-\x{10FFFF}]+)(?:\+([0-9]+))\(FP\)`) - asmUnnamedFP = re(`[^+\-0-9]](([0-9]+)\(FP\))`) - asmOpcode = re(`^\s*(?:[A-Z0-9a-z_]+:)?\s*([A-Z]+)\s*([^,]*)(?:,\s*(.*))?`) -) - -func asmCheck(pkg *Package) { - if !vet("asmdecl") { - return - } - - // No work if no assembly files. - if !pkg.hasFileWithSuffix(".s") { - return - } - - // Gather declarations. knownFunc[name][arch] is func description. - knownFunc := make(map[string]map[string]*asmFunc) - - for _, f := range pkg.files { - if f.file != nil { - for _, decl := range f.file.Decls { - if decl, ok := decl.(*ast.FuncDecl); ok && decl.Body == nil { - knownFunc[decl.Name.Name] = f.asmParseDecl(decl) - } - } - } - } - - var fn *asmFunc - for _, f := range pkg.files { - if !strings.HasSuffix(f.name, ".s") { - continue - } - Println("Checking file", f.name) - - // Determine architecture from file name if possible. - var arch string - for _, a := range arches { - if strings.HasSuffix(f.name, "_"+a.name+".s") { - arch = a.name - break - } - } - - lines := strings.SplitAfter(string(f.content), "\n") - for lineno, line := range lines { - lineno++ - - warnf := func(format string, args ...interface{}) { - f.Warnf(token.NoPos, "%s:%d: [%s] %s", f.name, lineno, arch, fmt.Sprintf(format, args...)) - } - - if arch == "" { - // Determine architecture from +build line if possible. - if m := asmPlusBuild.FindStringSubmatch(line); m != nil { - Fields: - for _, fld := range strings.Fields(m[1]) { - for _, a := range arches { - if a.name == fld { - arch = a.name - break Fields - } - } - } - } - } - - if m := asmTEXT.FindStringSubmatch(line); m != nil { - if arch == "" { - f.Warnf(token.NoPos, "%s: cannot determine architecture for assembly file", f.name) - return - } - fn = knownFunc[m[1]][arch] - if fn != nil { - size, _ := strconv.Atoi(m[4]) - if size != fn.size && (m[2] != "7" || size != 0) { - warnf("wrong argument size %d; expected $...-%d", size, fn.size) - } - } - continue - } else if strings.Contains(line, "TEXT") && strings.Contains(line, "SB") { - // function, but not visible from Go (didn't match asmTEXT), so stop checking - fn = nil - continue - } - - if asmDATA.FindStringSubmatch(line) != nil { - fn = nil - } - if fn == nil { - continue - } - - for _, m := range asmUnnamedFP.FindAllStringSubmatch(line, -1) { - warnf("use of unnamed argument %s", m[1]) - } - - for _, m := range asmNamedFP.FindAllStringSubmatch(line, -1) { - name := m[1] - off := 0 - if m[2] != "" { - off, _ = strconv.Atoi(m[2]) - } - v := fn.vars[name] - if v == nil { - // Allow argframe+0(FP). - if name == "argframe" && off == 0 { - continue - } - v = fn.varByOffset[off] - if v != nil { - warnf("unknown variable %s; offset %d is %s+%d(FP)", name, off, v.name, v.off) - } else { - warnf("unknown variable %s", name) - } - continue - } - asmCheckVar(warnf, fn, line, m[0], off, v) - } - } - } -} - -// asmParseDecl parses a function decl for expected assembly variables. -func (f *File) asmParseDecl(decl *ast.FuncDecl) map[string]*asmFunc { - var ( - arch *asmArch - fn *asmFunc - offset int - failed bool - ) - - addVar := func(outer string, v asmVar) { - if vo := fn.vars[outer]; vo != nil { - vo.inner = append(vo.inner, &v) - } - fn.vars[v.name] = &v - for i := 0; i < v.size; i++ { - fn.varByOffset[v.off+i] = &v - } - } - - addParams := func(list []*ast.Field) { - for i, fld := range list { - // Determine alignment, size, and kind of type in declaration. - var align, size int - var kind asmKind - names := fld.Names - typ := f.gofmt(fld.Type) - switch t := fld.Type.(type) { - default: - switch typ { - default: - f.Warnf(fld.Type.Pos(), "unknown assembly argument type %s", typ) - failed = true - return - case "int8", "uint8", "byte", "bool": - size = 1 - case "int16", "uint16": - size = 2 - case "int32", "uint32", "float32": - size = 4 - case "int64", "uint64", "float64": - align = arch.ptrSize - size = 8 - case "int", "uint": - size = arch.intSize - case "uintptr", "iword", "Word", "Errno", "unsafe.Pointer": - size = arch.ptrSize - case "string": - size = arch.ptrSize * 2 - align = arch.ptrSize - kind = asmString - } - case *ast.ChanType, *ast.FuncType, *ast.MapType, *ast.StarExpr: - size = arch.ptrSize - case *ast.InterfaceType: - align = arch.ptrSize - size = 2 * arch.ptrSize - if len(t.Methods.List) > 0 { - kind = asmInterface - } else { - kind = asmEmptyInterface - } - case *ast.ArrayType: - if t.Len == nil { - size = arch.ptrSize + 2*arch.intSize - align = arch.ptrSize - kind = asmSlice - break - } - f.Warnf(fld.Type.Pos(), "unsupported assembly argument type %s", typ) - failed = true - case *ast.StructType: - f.Warnf(fld.Type.Pos(), "unsupported assembly argument type %s", typ) - failed = true - } - if align == 0 { - align = size - } - if kind == 0 { - kind = asmKind(size) - } - offset += -offset & (align - 1) - - // Create variable for each name being declared with this type. - if len(names) == 0 { - name := "unnamed" - if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 && &list[0] == &decl.Type.Results.List[0] && i == 0 { - // Assume assembly will refer to single unnamed result as r. - name = "ret" - } - names = []*ast.Ident{{Name: name}} - } - for _, id := range names { - name := id.Name - addVar("", asmVar{ - name: name, - kind: kind, - typ: typ, - off: offset, - size: size, - }) - switch kind { - case 8: - if arch.ptrSize == 4 { - w1, w2 := "lo", "hi" - if arch.bigEndian { - w1, w2 = w2, w1 - } - addVar(name, asmVar{ - name: name + "_" + w1, - kind: 4, - typ: "half " + typ, - off: offset, - size: 4, - }) - addVar(name, asmVar{ - name: name + "_" + w2, - kind: 4, - typ: "half " + typ, - off: offset + 4, - size: 4, - }) - } - - case asmEmptyInterface: - addVar(name, asmVar{ - name: name + "_type", - kind: asmKind(arch.ptrSize), - typ: "interface type", - off: offset, - size: arch.ptrSize, - }) - addVar(name, asmVar{ - name: name + "_data", - kind: asmKind(arch.ptrSize), - typ: "interface data", - off: offset + arch.ptrSize, - size: arch.ptrSize, - }) - - case asmInterface: - addVar(name, asmVar{ - name: name + "_itable", - kind: asmKind(arch.ptrSize), - typ: "interface itable", - off: offset, - size: arch.ptrSize, - }) - addVar(name, asmVar{ - name: name + "_data", - kind: asmKind(arch.ptrSize), - typ: "interface data", - off: offset + arch.ptrSize, - size: arch.ptrSize, - }) - - case asmSlice: - addVar(name, asmVar{ - name: name + "_base", - kind: asmKind(arch.ptrSize), - typ: "slice base", - off: offset, - size: arch.ptrSize, - }) - addVar(name, asmVar{ - name: name + "_len", - kind: asmKind(arch.intSize), - typ: "slice len", - off: offset + arch.ptrSize, - size: arch.intSize, - }) - addVar(name, asmVar{ - name: name + "_cap", - kind: asmKind(arch.intSize), - typ: "slice cap", - off: offset + arch.ptrSize + arch.intSize, - size: arch.intSize, - }) - - case asmString: - addVar(name, asmVar{ - name: name + "_base", - kind: asmKind(arch.ptrSize), - typ: "string base", - off: offset, - size: arch.ptrSize, - }) - addVar(name, asmVar{ - name: name + "_len", - kind: asmKind(arch.intSize), - typ: "string len", - off: offset + arch.ptrSize, - size: arch.intSize, - }) - } - offset += size - } - } - } - - m := make(map[string]*asmFunc) - for _, arch = range arches { - fn = &asmFunc{ - arch: arch, - vars: make(map[string]*asmVar), - varByOffset: make(map[int]*asmVar), - } - offset = 0 - addParams(decl.Type.Params.List) - if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 { - offset += -offset & (arch.ptrSize - 1) - addParams(decl.Type.Results.List) - } - fn.size = offset - m[arch.name] = fn - } - - if failed { - return nil - } - return m -} - -// asmCheckVar checks a single variable reference. -func asmCheckVar(warnf func(string, ...interface{}), fn *asmFunc, line, expr string, off int, v *asmVar) { - m := asmOpcode.FindStringSubmatch(line) - if m == nil { - warnf("cannot find assembly opcode") - } - - // Determine operand sizes from instruction. - // Typically the suffix suffices, but there are exceptions. - var src, dst, kind asmKind - op := m[1] - switch fn.arch.name + "." + op { - case "386.FMOVLP": - src, dst = 8, 4 - case "arm.MOVD": - src = 8 - case "arm.MOVW": - src = 4 - case "arm.MOVH", "arm.MOVHU": - src = 2 - case "arm.MOVB", "arm.MOVBU": - src = 1 - default: - if fn.arch.name == "386" || fn.arch.name == "amd64" { - if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "D") || strings.HasSuffix(op, "DP")) { - // FMOVDP, FXCHD, etc - src = 8 - break - } - if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "F") || strings.HasSuffix(op, "FP")) { - // FMOVFP, FXCHF, etc - src = 4 - break - } - if strings.HasSuffix(op, "SD") { - // MOVSD, SQRTSD, etc - src = 8 - break - } - if strings.HasSuffix(op, "SS") { - // MOVSS, SQRTSS, etc - src = 4 - break - } - if strings.HasPrefix(op, "SET") { - // SETEQ, etc - src = 1 - break - } - switch op[len(op)-1] { - case 'B': - src = 1 - case 'W': - src = 2 - case 'L': - src = 4 - case 'D', 'Q': - src = 8 - } - } - } - if dst == 0 { - dst = src - } - - // Determine whether the match we're holding - // is the first or second argument. - if strings.Index(line, expr) > strings.Index(line, ",") { - kind = dst - } else { - kind = src - } - - vk := v.kind - vt := v.typ - switch vk { - case asmInterface, asmEmptyInterface, asmString, asmSlice: - // allow reference to first word (pointer) - vk = v.inner[0].kind - vt = v.inner[0].typ - } - - if off != v.off { - var inner bytes.Buffer - for i, vi := range v.inner { - if len(v.inner) > 1 { - fmt.Fprintf(&inner, ",") - } - fmt.Fprintf(&inner, " ") - if i == len(v.inner)-1 { - fmt.Fprintf(&inner, "or ") - } - fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off) - } - warnf("invalid offset %s; expected %s+%d(FP)%s", expr, v.name, v.off, inner.String()) - return - } - if kind != 0 && kind != vk { - var inner bytes.Buffer - if len(v.inner) > 0 { - fmt.Fprintf(&inner, " containing") - for i, vi := range v.inner { - if i > 0 && len(v.inner) > 2 { - fmt.Fprintf(&inner, ",") - } - fmt.Fprintf(&inner, " ") - if i > 0 && i == len(v.inner)-1 { - fmt.Fprintf(&inner, "and ") - } - fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off) - } - } - warnf("invalid %s of %s; %s is %d-byte value%s", op, expr, vt, vk, inner.String()) - } -} diff --git a/src/cmd/vet/assign.go b/src/cmd/vet/assign.go deleted file mode 100644 index a11f0f875..000000000 --- a/src/cmd/vet/assign.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* -This file contains the code to check for useless assignments. -*/ - -package main - -import ( - "go/ast" - "go/token" - "reflect" -) - -// TODO: should also check for assignments to struct fields inside methods -// that are on T instead of *T. - -// checkAssignStmt checks for assignments of the form "<expr> = <expr>". -// These are almost always useless, and even when they aren't they are usually a mistake. -func (f *File) checkAssignStmt(stmt *ast.AssignStmt) { - if !vet("assign") { - return - } - if stmt.Tok != token.ASSIGN { - return // ignore := - } - if len(stmt.Lhs) != len(stmt.Rhs) { - // If LHS and RHS have different cardinality, they can't be the same. - return - } - for i, lhs := range stmt.Lhs { - rhs := stmt.Rhs[i] - if reflect.TypeOf(lhs) != reflect.TypeOf(rhs) { - continue // short-circuit the heavy-weight gofmt check - } - le := f.gofmt(lhs) - re := f.gofmt(rhs) - if le == re { - f.Warnf(stmt.Pos(), "self-assignment of %s to %s", re, le) - } - } -} diff --git a/src/cmd/vet/atomic.go b/src/cmd/vet/atomic.go deleted file mode 100644 index 4ab256f64..000000000 --- a/src/cmd/vet/atomic.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" - "go/token" -) - -// checkAtomicAssignment walks the assignment statement checking for common -// mistaken usage of atomic package, such as: x = atomic.AddUint64(&x, 1) -func (f *File) checkAtomicAssignment(n *ast.AssignStmt) { - if !vet("atomic") { - return - } - - if len(n.Lhs) != len(n.Rhs) { - return - } - - for i, right := range n.Rhs { - call, ok := right.(*ast.CallExpr) - if !ok { - continue - } - sel, ok := call.Fun.(*ast.SelectorExpr) - if !ok { - continue - } - pkg, ok := sel.X.(*ast.Ident) - if !ok || pkg.Name != "atomic" { - continue - } - - switch sel.Sel.Name { - case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr": - f.checkAtomicAddAssignment(n.Lhs[i], call) - } - } -} - -// checkAtomicAddAssignment walks the atomic.Add* method calls checking for assigning the return value -// to the same variable being used in the operation -func (f *File) checkAtomicAddAssignment(left ast.Expr, call *ast.CallExpr) { - arg := call.Args[0] - broken := false - - if uarg, ok := arg.(*ast.UnaryExpr); ok && uarg.Op == token.AND { - broken = f.gofmt(left) == f.gofmt(uarg.X) - } else if star, ok := left.(*ast.StarExpr); ok { - broken = f.gofmt(star.X) == f.gofmt(arg) - } - - if broken { - f.Warn(left.Pos(), "direct assignment to atomic value") - } -} diff --git a/src/cmd/vet/buildtag.go b/src/cmd/vet/buildtag.go deleted file mode 100644 index 0ab13cb8a..000000000 --- a/src/cmd/vet/buildtag.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "bytes" - "fmt" - "os" - "strings" - "unicode" -) - -var ( - nl = []byte("\n") - slashSlash = []byte("//") - plusBuild = []byte("+build") -) - -// checkBuildTag checks that build tags are in the correct location and well-formed. -func checkBuildTag(name string, data []byte) { - if !vet("buildtags") { - return - } - lines := bytes.SplitAfter(data, nl) - - // Determine cutpoint where +build comments are no longer valid. - // They are valid in leading // comments in the file followed by - // a blank line. - var cutoff int - for i, line := range lines { - line = bytes.TrimSpace(line) - if len(line) == 0 { - cutoff = i - continue - } - if bytes.HasPrefix(line, slashSlash) { - continue - } - break - } - - for i, line := range lines { - line = bytes.TrimSpace(line) - if !bytes.HasPrefix(line, slashSlash) { - continue - } - text := bytes.TrimSpace(line[2:]) - if bytes.HasPrefix(text, plusBuild) { - fields := bytes.Fields(text) - if !bytes.Equal(fields[0], plusBuild) { - // Comment is something like +buildasdf not +build. - fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1) - continue - } - if i >= cutoff { - fmt.Fprintf(os.Stderr, "%s:%d: +build comment appears too late in file\n", name, i+1) - setExit(1) - continue - } - // Check arguments. - Args: - for _, arg := range fields[1:] { - for _, elem := range strings.Split(string(arg), ",") { - if strings.HasPrefix(elem, "!!") { - fmt.Fprintf(os.Stderr, "%s:%d: invalid double negative in build constraint: %s\n", name, i+1, arg) - setExit(1) - break Args - } - if strings.HasPrefix(elem, "!") { - elem = elem[1:] - } - for _, c := range elem { - if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' { - fmt.Fprintf(os.Stderr, "%s:%d: invalid non-alphanumeric build constraint: %s\n", name, i+1, arg) - setExit(1) - break Args - } - } - } - } - continue - } - // Comment with +build but not at beginning. - if bytes.Contains(line, plusBuild) && i < cutoff { - fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1) - continue - } - } -} diff --git a/src/cmd/vet/deadcode.go b/src/cmd/vet/deadcode.go deleted file mode 100644 index f90fc14a4..000000000 --- a/src/cmd/vet/deadcode.go +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Check for syntactically unreachable code. - -package main - -import ( - "go/ast" - "go/token" -) - -type deadState struct { - f *File - hasBreak map[ast.Stmt]bool - hasGoto map[string]bool - labels map[string]ast.Stmt - breakTarget ast.Stmt - - reachable bool -} - -// checkUnreachable checks a function body for dead code. -func (f *File) checkUnreachable(body *ast.BlockStmt) { - if !vet("unreachable") || body == nil { - return - } - - d := &deadState{ - f: f, - hasBreak: make(map[ast.Stmt]bool), - hasGoto: make(map[string]bool), - labels: make(map[string]ast.Stmt), - } - - d.findLabels(body) - - d.reachable = true - d.findDead(body) -} - -// findLabels gathers information about the labels defined and used by stmt -// and about which statements break, whether a label is involved or not. -func (d *deadState) findLabels(stmt ast.Stmt) { - switch x := stmt.(type) { - default: - d.f.Warnf(x.Pos(), "internal error in findLabels: unexpected statement %T", x) - - case *ast.AssignStmt, - *ast.BadStmt, - *ast.DeclStmt, - *ast.DeferStmt, - *ast.EmptyStmt, - *ast.ExprStmt, - *ast.GoStmt, - *ast.IncDecStmt, - *ast.ReturnStmt, - *ast.SendStmt: - // no statements inside - - case *ast.BlockStmt: - for _, stmt := range x.List { - d.findLabels(stmt) - } - - case *ast.BranchStmt: - switch x.Tok { - case token.GOTO: - d.hasGoto[x.Label.Name] = true - - case token.BREAK: - stmt := d.breakTarget - if x.Label != nil { - stmt = d.labels[x.Label.Name] - } - if stmt != nil { - d.hasBreak[stmt] = true - } - } - - case *ast.IfStmt: - d.findLabels(x.Body) - if x.Else != nil { - d.findLabels(x.Else) - } - - case *ast.LabeledStmt: - d.labels[x.Label.Name] = x.Stmt - d.findLabels(x.Stmt) - - // These cases are all the same, but the x.Body only works - // when the specific type of x is known, so the cases cannot - // be merged. - case *ast.ForStmt: - outer := d.breakTarget - d.breakTarget = x - d.findLabels(x.Body) - d.breakTarget = outer - - case *ast.RangeStmt: - outer := d.breakTarget - d.breakTarget = x - d.findLabels(x.Body) - d.breakTarget = outer - - case *ast.SelectStmt: - outer := d.breakTarget - d.breakTarget = x - d.findLabels(x.Body) - d.breakTarget = outer - - case *ast.SwitchStmt: - outer := d.breakTarget - d.breakTarget = x - d.findLabels(x.Body) - d.breakTarget = outer - - case *ast.TypeSwitchStmt: - outer := d.breakTarget - d.breakTarget = x - d.findLabels(x.Body) - d.breakTarget = outer - - case *ast.CommClause: - for _, stmt := range x.Body { - d.findLabels(stmt) - } - - case *ast.CaseClause: - for _, stmt := range x.Body { - d.findLabels(stmt) - } - } -} - -// findDead walks the statement looking for dead code. -// If d.reachable is false on entry, stmt itself is dead. -// When findDead returns, d.reachable tells whether the -// statement following stmt is reachable. -func (d *deadState) findDead(stmt ast.Stmt) { - // Is this a labeled goto target? - // If so, assume it is reachable due to the goto. - // This is slightly conservative, in that we don't - // check that the goto is reachable, so - // L: goto L - // will not provoke a warning. - // But it's good enough. - if x, isLabel := stmt.(*ast.LabeledStmt); isLabel && d.hasGoto[x.Label.Name] { - d.reachable = true - } - - if !d.reachable { - switch stmt.(type) { - case *ast.EmptyStmt: - // do not warn about unreachable empty statements - default: - d.f.Warnf(stmt.Pos(), "unreachable code") - d.reachable = true // silence error about next statement - } - } - - switch x := stmt.(type) { - default: - d.f.Warnf(x.Pos(), "internal error in findDead: unexpected statement %T", x) - - case *ast.AssignStmt, - *ast.BadStmt, - *ast.DeclStmt, - *ast.DeferStmt, - *ast.EmptyStmt, - *ast.GoStmt, - *ast.IncDecStmt, - *ast.SendStmt: - // no control flow - - case *ast.BlockStmt: - for _, stmt := range x.List { - d.findDead(stmt) - } - - case *ast.BranchStmt: - switch x.Tok { - case token.BREAK, token.GOTO, token.FALLTHROUGH: - d.reachable = false - case token.CONTINUE: - // NOTE: We accept "continue" statements as terminating. - // They are not necessary in the spec definition of terminating, - // because a continue statement cannot be the final statement - // before a return. But for the more general problem of syntactically - // identifying dead code, continue redirects control flow just - // like the other terminating statements. - d.reachable = false - } - - case *ast.ExprStmt: - // Call to panic? - call, ok := x.X.(*ast.CallExpr) - if ok { - name, ok := call.Fun.(*ast.Ident) - if ok && name.Name == "panic" && name.Obj == nil { - d.reachable = false - } - } - - case *ast.ForStmt: - d.findDead(x.Body) - d.reachable = x.Cond != nil || d.hasBreak[x] - - case *ast.IfStmt: - d.findDead(x.Body) - if x.Else != nil { - r := d.reachable - d.reachable = true - d.findDead(x.Else) - d.reachable = d.reachable || r - } else { - // might not have executed if statement - d.reachable = true - } - - case *ast.LabeledStmt: - d.findDead(x.Stmt) - - case *ast.RangeStmt: - d.findDead(x.Body) - d.reachable = true - - case *ast.ReturnStmt: - d.reachable = false - - case *ast.SelectStmt: - // NOTE: Unlike switch and type switch below, we don't care - // whether a select has a default, because a select without a - // default blocks until one of the cases can run. That's different - // from a switch without a default, which behaves like it has - // a default with an empty body. - anyReachable := false - for _, comm := range x.Body.List { - d.reachable = true - for _, stmt := range comm.(*ast.CommClause).Body { - d.findDead(stmt) - } - anyReachable = anyReachable || d.reachable - } - d.reachable = anyReachable || d.hasBreak[x] - - case *ast.SwitchStmt: - anyReachable := false - hasDefault := false - for _, cas := range x.Body.List { - cc := cas.(*ast.CaseClause) - if cc.List == nil { - hasDefault = true - } - d.reachable = true - for _, stmt := range cc.Body { - d.findDead(stmt) - } - anyReachable = anyReachable || d.reachable - } - d.reachable = anyReachable || d.hasBreak[x] || !hasDefault - - case *ast.TypeSwitchStmt: - anyReachable := false - hasDefault := false - for _, cas := range x.Body.List { - cc := cas.(*ast.CaseClause) - if cc.List == nil { - hasDefault = true - } - d.reachable = true - for _, stmt := range cc.Body { - d.findDead(stmt) - } - anyReachable = anyReachable || d.reachable - } - d.reachable = anyReachable || d.hasBreak[x] || !hasDefault - } -} diff --git a/src/cmd/vet/doc.go b/src/cmd/vet/doc.go deleted file mode 100644 index eb1e436f0..000000000 --- a/src/cmd/vet/doc.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* - -Vet examines Go source code and reports suspicious constructs, such as Printf -calls whose arguments do not align with the format string. Vet uses heuristics -that do not guarantee all reports are genuine problems, but it can find errors -not caught by the compilers. - -Its exit code is 2 for erroneous invocation of the tool, 1 if a -problem was reported, and 0 otherwise. Note that the tool does not -check every possible problem and depends on unreliable heuristics -so it should be used as guidance only, not as a firm indicator of -program correctness. - -By default all checks are performed, but if explicit flags are provided, only -those identified by the flags are performed. - -Available checks: - -1. Printf family, flag -printf - -Suspicious calls to functions in the Printf family, including any functions -with these names: - Print Printf Println - Fprint Fprintf Fprintln - Sprint Sprintf Sprintln - Error Errorf - Fatal Fatalf - Panic Panicf Panicln -If the function name ends with an 'f', the function is assumed to take -a format descriptor string in the manner of fmt.Printf. If not, vet -complains about arguments that look like format descriptor strings. - -It also checks for errors such as using a Writer as the first argument of -Printf. - -2. Methods, flag -methods - -Non-standard signatures for methods with familiar names, including: - Format GobEncode GobDecode MarshalJSON MarshalXML - Peek ReadByte ReadFrom ReadRune Scan Seek - UnmarshalJSON UnreadByte UnreadRune WriteByte - WriteTo - -3. Struct tags, flag -structtags - -Struct tags that do not follow the format understood by reflect.StructTag.Get. - -4. Untagged composite literals, flag -composites - -Composite struct literals that do not use the type-tagged syntax. - - -Usage: - - go tool vet [flag] [file.go ...] - go tool vet [flag] [directory ...] # Scan all .go files under directory, recursively - -The other flags are: - -v - Verbose mode - -printfuncs - A comma-separated list of print-like functions to supplement - the standard list. Each entry is in the form Name:N where N - is the zero-based argument position of the first argument - involved in the print: either the format or the first print - argument for non-formatted prints. For example, - if you have Warn and Warnf functions that take an - io.Writer as their first argument, like Fprintf, - -printfuncs=Warn:1,Warnf:1 - -*/ -package main diff --git a/src/cmd/vet/main.go b/src/cmd/vet/main.go deleted file mode 100644 index 2fefa0b47..000000000 --- a/src/cmd/vet/main.go +++ /dev/null @@ -1,422 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Vet is a simple checker for static errors in Go source code. -// See doc.go for more information. -package main - -import ( - "bytes" - "flag" - "fmt" - "go/ast" - "go/build" - "go/parser" - "go/printer" - "go/token" - "io/ioutil" - "os" - "path/filepath" - "strconv" - "strings" -) - -var verbose = flag.Bool("v", false, "verbose") -var exitCode = 0 - -// Flags to control which checks to perform. "all" is set to true here, and disabled later if -// a flag is set explicitly. -var report = map[string]*bool{ - "all": flag.Bool("all", true, "check everything; disabled if any explicit check is requested"), - "asmdecl": flag.Bool("asmdecl", false, "check assembly against Go declarations"), - "assign": flag.Bool("assign", false, "check for useless assignments"), - "atomic": flag.Bool("atomic", false, "check for common mistaken usages of the sync/atomic package"), - "buildtags": flag.Bool("buildtags", false, "check that +build tags are valid"), - "composites": flag.Bool("composites", false, "check that composite literals used type-tagged elements"), - "methods": flag.Bool("methods", false, "check that canonically named methods are canonically defined"), - "printf": flag.Bool("printf", false, "check printf-like invocations"), - "rangeloops": flag.Bool("rangeloops", false, "check that range loop variables are used correctly"), - "structtags": flag.Bool("structtags", false, "check that struct field tags have canonical format"), - "unreachable": flag.Bool("unreachable", false, "check for unreachable code"), -} - -// vet tells whether to report errors for the named check, a flag name. -func vet(name string) bool { - return *report["all"] || *report[name] -} - -// setExit sets the value for os.Exit when it is called, later. It -// remembers the highest value. -func setExit(err int) { - if err > exitCode { - exitCode = err - } -} - -// Usage is a replacement usage function for the flags package. -func Usage() { - fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) - fmt.Fprintf(os.Stderr, "\tvet [flags] directory...\n") - fmt.Fprintf(os.Stderr, "\tvet [flags] files... # Must be a single package\n") - flag.PrintDefaults() - os.Exit(2) -} - -// File is a wrapper for the state of a file used in the parser. -// The parse tree walkers are all methods of this type. -type File struct { - pkg *Package - fset *token.FileSet - name string - content []byte - file *ast.File - b bytes.Buffer // for use by methods -} - -func main() { - flag.Usage = Usage - flag.Parse() - - // If a check is named explicitly, turn off the 'all' flag. - for name, ptr := range report { - if name != "all" && *ptr { - *report["all"] = false - break - } - } - - if *printfuncs != "" { - for _, name := range strings.Split(*printfuncs, ",") { - if len(name) == 0 { - flag.Usage() - } - skip := 0 - if colon := strings.LastIndex(name, ":"); colon > 0 { - var err error - skip, err = strconv.Atoi(name[colon+1:]) - if err != nil { - errorf(`illegal format for "Func:N" argument %q; %s`, name, err) - } - name = name[:colon] - } - name = strings.ToLower(name) - if name[len(name)-1] == 'f' { - printfList[name] = skip - } else { - printList[name] = skip - } - } - } - - if flag.NArg() == 0 { - Usage() - } - dirs := false - files := false - for _, name := range flag.Args() { - // Is it a directory? - fi, err := os.Stat(name) - if err != nil { - warnf("error walking tree: %s", err) - continue - } - if fi.IsDir() { - dirs = true - } else { - files = true - } - } - if dirs && files { - Usage() - } - if dirs { - for _, name := range flag.Args() { - walkDir(name) - } - return - } - doPackage(flag.Args()) - os.Exit(exitCode) -} - -// prefixDirectory places the directory name on the beginning of each name in the list. -func prefixDirectory(directory string, names []string) { - if directory != "." { - for i, name := range names { - names[i] = filepath.Join(directory, name) - } - } -} - -// doPackageDir analyzes the single package found in the directory, if there is one, -// plus a test package, if there is one. -func doPackageDir(directory string) { - pkg, err := build.Default.ImportDir(directory, 0) - if err != nil { - // If it's just that there are no go source files, that's fine. - if _, nogo := err.(*build.NoGoError); nogo { - return - } - // Non-fatal: we are doing a recursive walk and there may be other directories. - warnf("cannot process directory %s: %s", directory, err) - return - } - var names []string - names = append(names, pkg.GoFiles...) - names = append(names, pkg.CgoFiles...) - names = append(names, pkg.TestGoFiles...) // These are also in the "foo" package. - names = append(names, pkg.SFiles...) - prefixDirectory(directory, names) - doPackage(names) - // Is there also a "foo_test" package? If so, do that one as well. - if len(pkg.XTestGoFiles) > 0 { - names = pkg.XTestGoFiles - prefixDirectory(directory, names) - doPackage(names) - } -} - -type Package struct { - types map[ast.Expr]Type - values map[ast.Expr]interface{} - files []*File -} - -// doPackage analyzes the single package constructed from the named files. -func doPackage(names []string) { - var files []*File - var astFiles []*ast.File - fs := token.NewFileSet() - for _, name := range names { - f, err := os.Open(name) - if err != nil { - // Warn but continue to next package. - warnf("%s: %s", name, err) - return - } - defer f.Close() - data, err := ioutil.ReadAll(f) - if err != nil { - warnf("%s: %s", name, err) - return - } - checkBuildTag(name, data) - var parsedFile *ast.File - if strings.HasSuffix(name, ".go") { - parsedFile, err = parser.ParseFile(fs, name, bytes.NewReader(data), 0) - if err != nil { - warnf("%s: %s", name, err) - return - } - astFiles = append(astFiles, parsedFile) - } - files = append(files, &File{fset: fs, content: data, name: name, file: parsedFile}) - } - pkg := new(Package) - pkg.files = files - // Type check the package. - err := pkg.check(fs, astFiles) - if err != nil && *verbose { - warnf("%s", err) - } - for _, file := range files { - file.pkg = pkg - if file.file != nil { - file.walkFile(file.name, file.file) - } - } - asmCheck(pkg) -} - -func visit(path string, f os.FileInfo, err error) error { - if err != nil { - warnf("walk error: %s", err) - return err - } - // One package per directory. Ignore the files themselves. - if !f.IsDir() { - return nil - } - doPackageDir(path) - return nil -} - -func (pkg *Package) hasFileWithSuffix(suffix string) bool { - for _, f := range pkg.files { - if strings.HasSuffix(f.name, suffix) { - return true - } - } - return false -} - -// walkDir recursively walks the tree looking for Go packages. -func walkDir(root string) { - filepath.Walk(root, visit) -} - -// errorf formats the error to standard error, adding program -// identification and a newline, and exits. -func errorf(format string, args ...interface{}) { - fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...) - os.Exit(2) -} - -// warnf formats the error to standard error, adding program -// identification and a newline, but does not exit. -func warnf(format string, args ...interface{}) { - fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...) - setExit(1) -} - -// Println is fmt.Println guarded by -v. -func Println(args ...interface{}) { - if !*verbose { - return - } - fmt.Println(args...) -} - -// Printf is fmt.Printf guarded by -v. -func Printf(format string, args ...interface{}) { - if !*verbose { - return - } - fmt.Printf(format+"\n", args...) -} - -// Bad reports an error and sets the exit code.. -func (f *File) Bad(pos token.Pos, args ...interface{}) { - f.Warn(pos, args...) - setExit(1) -} - -// Badf reports a formatted error and sets the exit code. -func (f *File) Badf(pos token.Pos, format string, args ...interface{}) { - f.Warnf(pos, format, args...) - setExit(1) -} - -func (f *File) loc(pos token.Pos) string { - if pos == token.NoPos { - return "" - } - // Do not print columns. Because the pos often points to the start of an - // expression instead of the inner part with the actual error, the - // precision can mislead. - posn := f.fset.Position(pos) - return fmt.Sprintf("%s:%d: ", posn.Filename, posn.Line) -} - -// Warn reports an error but does not set the exit code. -func (f *File) Warn(pos token.Pos, args ...interface{}) { - fmt.Fprint(os.Stderr, f.loc(pos)+fmt.Sprintln(args...)) -} - -// Warnf reports a formatted error but does not set the exit code. -func (f *File) Warnf(pos token.Pos, format string, args ...interface{}) { - fmt.Fprintf(os.Stderr, f.loc(pos)+format+"\n", args...) -} - -// walkFile walks the file's tree. -func (f *File) walkFile(name string, file *ast.File) { - Println("Checking file", name) - ast.Walk(f, file) -} - -// Visit implements the ast.Visitor interface. -func (f *File) Visit(node ast.Node) ast.Visitor { - switch n := node.(type) { - case *ast.AssignStmt: - f.walkAssignStmt(n) - case *ast.CallExpr: - f.walkCallExpr(n) - case *ast.CompositeLit: - f.walkCompositeLit(n) - case *ast.Field: - f.walkFieldTag(n) - case *ast.FuncDecl: - f.walkFuncDecl(n) - case *ast.FuncLit: - f.walkFuncLit(n) - case *ast.InterfaceType: - f.walkInterfaceType(n) - case *ast.RangeStmt: - f.walkRangeStmt(n) - } - return f -} - -// walkAssignStmt walks an assignment statement -func (f *File) walkAssignStmt(stmt *ast.AssignStmt) { - f.checkAssignStmt(stmt) - f.checkAtomicAssignment(stmt) -} - -// walkCall walks a call expression. -func (f *File) walkCall(call *ast.CallExpr, name string) { - f.checkFmtPrintfCall(call, name) -} - -// walkCallExpr walks a call expression. -func (f *File) walkCallExpr(call *ast.CallExpr) { - switch x := call.Fun.(type) { - case *ast.Ident: - f.walkCall(call, x.Name) - case *ast.SelectorExpr: - f.walkCall(call, x.Sel.Name) - } -} - -// walkCompositeLit walks a composite literal. -func (f *File) walkCompositeLit(c *ast.CompositeLit) { - f.checkUntaggedLiteral(c) -} - -// walkFieldTag walks a struct field tag. -func (f *File) walkFieldTag(field *ast.Field) { - if field.Tag == nil { - return - } - f.checkCanonicalFieldTag(field) -} - -// walkMethod walks the method's signature. -func (f *File) walkMethod(id *ast.Ident, t *ast.FuncType) { - f.checkCanonicalMethod(id, t) -} - -// walkFuncDecl walks a function declaration. -func (f *File) walkFuncDecl(d *ast.FuncDecl) { - f.checkUnreachable(d.Body) - if d.Recv != nil { - f.walkMethod(d.Name, d.Type) - } -} - -// walkFuncLit walks a function literal. -func (f *File) walkFuncLit(x *ast.FuncLit) { - f.checkUnreachable(x.Body) -} - -// walkInterfaceType walks the method signatures of an interface. -func (f *File) walkInterfaceType(t *ast.InterfaceType) { - for _, field := range t.Methods.List { - for _, id := range field.Names { - f.walkMethod(id, field.Type.(*ast.FuncType)) - } - } -} - -// walkRangeStmt walks a range statement. -func (f *File) walkRangeStmt(n *ast.RangeStmt) { - checkRangeLoop(f, n) -} - -// gofmt returns a string representation of the expression. -func (f *File) gofmt(x ast.Expr) string { - f.b.Reset() - printer.Fprint(&f.b, f.fset, x) - return f.b.String() -} diff --git a/src/cmd/vet/method.go b/src/cmd/vet/method.go deleted file mode 100644 index 8064235f4..000000000 --- a/src/cmd/vet/method.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains the code to check canonical methods. - -package main - -import ( - "fmt" - "go/ast" - "go/printer" - "strings" -) - -type MethodSig struct { - args []string - results []string -} - -// canonicalMethods lists the input and output types for Go methods -// that are checked using dynamic interface checks. Because the -// checks are dynamic, such methods would not cause a compile error -// if they have the wrong signature: instead the dynamic check would -// fail, sometimes mysteriously. If a method is found with a name listed -// here but not the input/output types listed here, vet complains. -// -// A few of the canonical methods have very common names. -// For example, a type might implement a Scan method that -// has nothing to do with fmt.Scanner, but we still want to check -// the methods that are intended to implement fmt.Scanner. -// To do that, the arguments that have a = prefix are treated as -// signals that the canonical meaning is intended: if a Scan -// method doesn't have a fmt.ScanState as its first argument, -// we let it go. But if it does have a fmt.ScanState, then the -// rest has to match. -var canonicalMethods = map[string]MethodSig{ - // "Flush": {{}, {"error"}}, // http.Flusher and jpeg.writer conflict - "Format": {[]string{"=fmt.State", "rune"}, []string{}}, // fmt.Formatter - "GobDecode": {[]string{"[]byte"}, []string{"error"}}, // gob.GobDecoder - "GobEncode": {[]string{}, []string{"[]byte", "error"}}, // gob.GobEncoder - "MarshalJSON": {[]string{}, []string{"[]byte", "error"}}, // json.Marshaler - "MarshalXML": {[]string{}, []string{"[]byte", "error"}}, // xml.Marshaler - "Peek": {[]string{"=int"}, []string{"[]byte", "error"}}, // image.reader (matching bufio.Reader) - "ReadByte": {[]string{}, []string{"byte", "error"}}, // io.ByteReader - "ReadFrom": {[]string{"=io.Reader"}, []string{"int64", "error"}}, // io.ReaderFrom - "ReadRune": {[]string{}, []string{"rune", "int", "error"}}, // io.RuneReader - "Scan": {[]string{"=fmt.ScanState", "rune"}, []string{"error"}}, // fmt.Scanner - "Seek": {[]string{"=int64", "int"}, []string{"int64", "error"}}, // io.Seeker - "UnmarshalJSON": {[]string{"[]byte"}, []string{"error"}}, // json.Unmarshaler - "UnreadByte": {[]string{}, []string{"error"}}, - "UnreadRune": {[]string{}, []string{"error"}}, - "WriteByte": {[]string{"byte"}, []string{"error"}}, // jpeg.writer (matching bufio.Writer) - "WriteTo": {[]string{"=io.Writer"}, []string{"int64", "error"}}, // io.WriterTo -} - -func (f *File) checkCanonicalMethod(id *ast.Ident, t *ast.FuncType) { - if !vet("methods") { - return - } - // Expected input/output. - expect, ok := canonicalMethods[id.Name] - if !ok { - return - } - - // Actual input/output - args := typeFlatten(t.Params.List) - var results []ast.Expr - if t.Results != nil { - results = typeFlatten(t.Results.List) - } - - // Do the =s (if any) all match? - if !f.matchParams(expect.args, args, "=") || !f.matchParams(expect.results, results, "=") { - return - } - - // Everything must match. - if !f.matchParams(expect.args, args, "") || !f.matchParams(expect.results, results, "") { - expectFmt := id.Name + "(" + argjoin(expect.args) + ")" - if len(expect.results) == 1 { - expectFmt += " " + argjoin(expect.results) - } else if len(expect.results) > 1 { - expectFmt += " (" + argjoin(expect.results) + ")" - } - - f.b.Reset() - if err := printer.Fprint(&f.b, f.fset, t); err != nil { - fmt.Fprintf(&f.b, "<%s>", err) - } - actual := f.b.String() - actual = strings.TrimPrefix(actual, "func") - actual = id.Name + actual - - f.Badf(id.Pos(), "method %s should have signature %s", actual, expectFmt) - } -} - -func argjoin(x []string) string { - y := make([]string, len(x)) - for i, s := range x { - if s[0] == '=' { - s = s[1:] - } - y[i] = s - } - return strings.Join(y, ", ") -} - -// Turn parameter list into slice of types -// (in the ast, types are Exprs). -// Have to handle f(int, bool) and f(x, y, z int) -// so not a simple 1-to-1 conversion. -func typeFlatten(l []*ast.Field) []ast.Expr { - var t []ast.Expr - for _, f := range l { - if len(f.Names) == 0 { - t = append(t, f.Type) - continue - } - for _ = range f.Names { - t = append(t, f.Type) - } - } - return t -} - -// Does each type in expect with the given prefix match the corresponding type in actual? -func (f *File) matchParams(expect []string, actual []ast.Expr, prefix string) bool { - for i, x := range expect { - if !strings.HasPrefix(x, prefix) { - continue - } - if i >= len(actual) { - return false - } - if !f.matchParamType(x, actual[i]) { - return false - } - } - if prefix == "" && len(actual) > len(expect) { - return false - } - return true -} - -// Does this one type match? -func (f *File) matchParamType(expect string, actual ast.Expr) bool { - if strings.HasPrefix(expect, "=") { - expect = expect[1:] - } - // Strip package name if we're in that package. - if n := len(f.file.Name.Name); len(expect) > n && expect[:n] == f.file.Name.Name && expect[n] == '.' { - expect = expect[n+1:] - } - - // Overkill but easy. - f.b.Reset() - printer.Fprint(&f.b, f.fset, actual) - return f.b.String() == expect -} diff --git a/src/cmd/vet/print.go b/src/cmd/vet/print.go deleted file mode 100644 index debfbf0bf..000000000 --- a/src/cmd/vet/print.go +++ /dev/null @@ -1,351 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains the printf-checker. - -package main - -import ( - "flag" - "go/ast" - "go/token" - "strconv" - "strings" - "unicode/utf8" -) - -var printfuncs = flag.String("printfuncs", "", "comma-separated list of print function names to check") - -// printfList records the formatted-print functions. The value is the location -// of the format parameter. Names are lower-cased so the lookup is -// case insensitive. -var printfList = map[string]int{ - "errorf": 0, - "fatalf": 0, - "fprintf": 1, - "panicf": 0, - "printf": 0, - "sprintf": 0, -} - -// printList records the unformatted-print functions. The value is the location -// of the first parameter to be printed. Names are lower-cased so the lookup is -// case insensitive. -var printList = map[string]int{ - "error": 0, - "fatal": 0, - "fprint": 1, "fprintln": 1, - "panic": 0, "panicln": 0, - "print": 0, "println": 0, - "sprint": 0, "sprintln": 0, -} - -// checkCall triggers the print-specific checks if the call invokes a print function. -func (f *File) checkFmtPrintfCall(call *ast.CallExpr, Name string) { - if !vet("printf") { - return - } - name := strings.ToLower(Name) - if skip, ok := printfList[name]; ok { - f.checkPrintf(call, Name, skip) - return - } - if skip, ok := printList[name]; ok { - f.checkPrint(call, Name, skip) - return - } -} - -// literal returns the literal value represented by the expression, or nil if it is not a literal. -func (f *File) literal(value ast.Expr) *ast.BasicLit { - switch v := value.(type) { - case *ast.BasicLit: - return v - case *ast.ParenExpr: - return f.literal(v.X) - case *ast.BinaryExpr: - if v.Op != token.ADD { - break - } - litX := f.literal(v.X) - litY := f.literal(v.Y) - if litX != nil && litY != nil { - lit := *litX - x, errX := strconv.Unquote(litX.Value) - y, errY := strconv.Unquote(litY.Value) - if errX == nil && errY == nil { - return &ast.BasicLit{ - ValuePos: lit.ValuePos, - Kind: lit.Kind, - Value: strconv.Quote(x + y), - } - } - } - case *ast.Ident: - // See if it's a constant or initial value (we can't tell the difference). - if v.Obj == nil || v.Obj.Decl == nil { - return nil - } - valueSpec, ok := v.Obj.Decl.(*ast.ValueSpec) - if ok && len(valueSpec.Names) == len(valueSpec.Values) { - // Find the index in the list of names - var i int - for i = 0; i < len(valueSpec.Names); i++ { - if valueSpec.Names[i].Name == v.Name { - if lit, ok := valueSpec.Values[i].(*ast.BasicLit); ok { - return lit - } - return nil - } - } - } - } - return nil -} - -// checkPrintf checks a call to a formatted print routine such as Printf. -// call.Args[formatIndex] is (well, should be) the format argument. -func (f *File) checkPrintf(call *ast.CallExpr, name string, formatIndex int) { - if formatIndex >= len(call.Args) { - return - } - lit := f.literal(call.Args[formatIndex]) - if lit == nil { - if *verbose { - f.Warn(call.Pos(), "can't check non-literal format in call to", name) - } - return - } - if lit.Kind != token.STRING { - f.Badf(call.Pos(), "literal %v not a string in call to", lit.Value, name) - } - format, err := strconv.Unquote(lit.Value) - if err != nil { - // Shouldn't happen if parser returned no errors, but be safe. - f.Badf(call.Pos(), "invalid quoted string literal") - } - firstArg := formatIndex + 1 // Arguments are immediately after format string. - if !strings.Contains(format, "%") { - if len(call.Args) > firstArg { - f.Badf(call.Pos(), "no formatting directive in %s call", name) - } - return - } - // Hard part: check formats against args. - argNum := firstArg - for i, w := 0, 0; i < len(format); i += w { - w = 1 - if format[i] == '%' { - verb, flags, nbytes, nargs := f.parsePrintfVerb(call, format[i:]) - w = nbytes - if verb == '%' { // "%%" does nothing interesting. - continue - } - // If we've run out of args, print after loop will pick that up. - if argNum+nargs <= len(call.Args) { - f.checkPrintfArg(call, verb, flags, argNum, nargs) - } - argNum += nargs - } - } - // TODO: Dotdotdot is hard. - if call.Ellipsis.IsValid() && argNum != len(call.Args) { - return - } - if argNum != len(call.Args) { - expect := argNum - firstArg - numArgs := len(call.Args) - firstArg - f.Badf(call.Pos(), "wrong number of args for format in %s call: %d needed but %d args", name, expect, numArgs) - } -} - -// parsePrintfVerb returns the verb that begins the format string, along with its flags, -// the number of bytes to advance the format to step past the verb, and number of -// arguments it consumes. -func (f *File) parsePrintfVerb(call *ast.CallExpr, format string) (verb rune, flags []byte, nbytes, nargs int) { - // There's guaranteed a percent sign. - flags = make([]byte, 0, 5) - nbytes = 1 - end := len(format) - // There may be flags. -FlagLoop: - for nbytes < end { - switch format[nbytes] { - case '#', '0', '+', '-', ' ': - flags = append(flags, format[nbytes]) - nbytes++ - default: - break FlagLoop - } - } - getNum := func() { - if nbytes < end && format[nbytes] == '*' { - nbytes++ - nargs++ - } else { - for nbytes < end && '0' <= format[nbytes] && format[nbytes] <= '9' { - nbytes++ - } - } - } - // There may be a width. - getNum() - // If there's a period, there may be a precision. - if nbytes < end && format[nbytes] == '.' { - flags = append(flags, '.') // Treat precision as a flag. - nbytes++ - getNum() - } - // Now a verb. - c, w := utf8.DecodeRuneInString(format[nbytes:]) - nbytes += w - verb = c - if c != '%' { - nargs++ - } - return -} - -// printfArgType encodes the types of expressions a printf verb accepts. It is a bitmask. -type printfArgType int - -const ( - argBool printfArgType = 1 << iota - argInt - argRune - argString - argFloat - argPointer - anyType printfArgType = ^0 -) - -type printVerb struct { - verb rune - flags string // known flags are all ASCII - typ printfArgType -} - -// Common flag sets for printf verbs. -const ( - numFlag = " -+.0" - sharpNumFlag = " -+.0#" - allFlags = " -+.0#" -) - -// printVerbs identifies which flags are known to printf for each verb. -// TODO: A type that implements Formatter may do what it wants, and vet -// will complain incorrectly. -var printVerbs = []printVerb{ - // '-' is a width modifier, always valid. - // '.' is a precision for float, max width for strings. - // '+' is required sign for numbers, Go format for %v. - // '#' is alternate format for several verbs. - // ' ' is spacer for numbers - {'b', numFlag, argInt | argFloat}, - {'c', "-", argRune | argInt}, - {'d', numFlag, argInt}, - {'e', numFlag, argFloat}, - {'E', numFlag, argFloat}, - {'f', numFlag, argFloat}, - {'F', numFlag, argFloat}, - {'g', numFlag, argFloat}, - {'G', numFlag, argFloat}, - {'o', sharpNumFlag, argInt}, - {'p', "-#", argPointer}, - {'q', " -+.0#", argRune | argInt | argString}, - {'s', " -+.0", argString}, - {'t', "-", argBool}, - {'T', "-", anyType}, - {'U', "-#", argRune | argInt}, - {'v', allFlags, anyType}, - {'x', sharpNumFlag, argRune | argInt | argString}, - {'X', sharpNumFlag, argRune | argInt | argString}, -} - -const printfVerbs = "bcdeEfFgGopqstTvxUX" - -func (f *File) checkPrintfArg(call *ast.CallExpr, verb rune, flags []byte, argNum, nargs int) { - // Linear scan is fast enough for a small list. - for _, v := range printVerbs { - if v.verb == verb { - for _, flag := range flags { - if !strings.ContainsRune(v.flags, rune(flag)) { - f.Badf(call.Pos(), "unrecognized printf flag for verb %q: %q", verb, flag) - return - } - } - // Verb is good. If nargs>1, we have something like %.*s and all but the final - // arg must be integer. - for i := 0; i < nargs-1; i++ { - if !f.matchArgType(argInt, call.Args[argNum+i]) { - f.Badf(call.Pos(), "arg %s for * in printf format not of type int", f.gofmt(call.Args[argNum+i])) - } - } - for _, v := range printVerbs { - if v.verb == verb { - arg := call.Args[argNum+nargs-1] - if !f.matchArgType(v.typ, arg) { - typeString := "" - if typ := f.pkg.types[arg]; typ != nil { - typeString = typ.String() - } - f.Badf(call.Pos(), "arg %s for printf verb %%%c of wrong type: %s", f.gofmt(arg), verb, typeString) - } - break - } - } - return - } - } - f.Badf(call.Pos(), "unrecognized printf verb %q", verb) -} - -// checkPrint checks a call to an unformatted print routine such as Println. -// call.Args[firstArg] is the first argument to be printed. -func (f *File) checkPrint(call *ast.CallExpr, name string, firstArg int) { - isLn := strings.HasSuffix(name, "ln") - isF := strings.HasPrefix(name, "F") - args := call.Args - // check for Println(os.Stderr, ...) - if firstArg == 0 && !isF && len(args) > 0 { - if sel, ok := args[0].(*ast.SelectorExpr); ok { - if x, ok := sel.X.(*ast.Ident); ok { - if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") { - f.Badf(call.Pos(), "first argument to %s is %s.%s", name, x.Name, sel.Sel.Name) - } - } - } - } - if len(args) <= firstArg { - // If we have a call to a method called Error that satisfies the Error interface, - // then it's ok. Otherwise it's something like (*T).Error from the testing package - // and we need to check it. - if name == "Error" && f.isErrorMethodCall(call) { - return - } - // If it's an Error call now, it's probably for printing errors. - if !isLn { - // Check the signature to be sure: there are niladic functions called "error". - if firstArg != 0 || f.numArgsInSignature(call) != firstArg { - f.Badf(call.Pos(), "no args in %s call", name) - } - } - return - } - arg := args[firstArg] - if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { - if strings.Contains(lit.Value, "%") { - f.Badf(call.Pos(), "possible formatting directive in %s call", name) - } - } - if isLn { - // The last item, if a string, should not have a newline. - arg = args[len(call.Args)-1] - if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { - if strings.HasSuffix(lit.Value, `\n"`) { - f.Badf(call.Pos(), "%s call ends with newline", name) - } - } - } -} diff --git a/src/cmd/vet/rangeloop.go b/src/cmd/vet/rangeloop.go deleted file mode 100644 index ecc595427..000000000 --- a/src/cmd/vet/rangeloop.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* -This file contains the code to check range loop variables bound inside function -literals that are deferred or launched in new goroutines. We only check -instances where the defer or go statement is the last statement in the loop -body, as otherwise we would need whole program analysis. - -For example: - - for i, v := range s { - go func() { - println(i, v) // not what you might expect - }() - } - -See: http://golang.org/doc/go_faq.html#closures_and_goroutines -*/ - -package main - -import "go/ast" - -// checkRangeLoop walks the body of the provided range statement, checking if -// its index or value variables are used unsafely inside goroutines or deferred -// function literals. -func checkRangeLoop(f *File, n *ast.RangeStmt) { - if !vet("rangeloops") { - return - } - key, _ := n.Key.(*ast.Ident) - val, _ := n.Value.(*ast.Ident) - if key == nil && val == nil { - return - } - sl := n.Body.List - if len(sl) == 0 { - return - } - var last *ast.CallExpr - switch s := sl[len(sl)-1].(type) { - case *ast.GoStmt: - last = s.Call - case *ast.DeferStmt: - last = s.Call - default: - return - } - lit, ok := last.Fun.(*ast.FuncLit) - if !ok { - return - } - ast.Inspect(lit.Body, func(n ast.Node) bool { - id, ok := n.(*ast.Ident) - if !ok || id.Obj == nil { - return true - } - if key != nil && id.Obj == key.Obj || val != nil && id.Obj == val.Obj { - f.Warn(id.Pos(), "range variable", id.Name, "enclosed by function") - } - return true - }) -} diff --git a/src/cmd/vet/structtag.go b/src/cmd/vet/structtag.go deleted file mode 100644 index d83578836..000000000 --- a/src/cmd/vet/structtag.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains the test for canonical struct tags. - -package main - -import ( - "go/ast" - "reflect" - "strconv" -) - -// checkField checks a struct field tag. -func (f *File) checkCanonicalFieldTag(field *ast.Field) { - if !vet("structtags") { - return - } - if field.Tag == nil { - return - } - - tag, err := strconv.Unquote(field.Tag.Value) - if err != nil { - f.Badf(field.Pos(), "unable to read struct tag %s", field.Tag.Value) - return - } - - // Check tag for validity by appending - // new key:value to end and checking that - // the tag parsing code can find it. - if reflect.StructTag(tag+` _gofix:"_magic"`).Get("_gofix") != "_magic" { - f.Badf(field.Pos(), "struct field tag %s not compatible with reflect.StructTag.Get", field.Tag.Value) - return - } -} diff --git a/src/cmd/vet/taglit.go b/src/cmd/vet/taglit.go deleted file mode 100644 index bcad2fe0a..000000000 --- a/src/cmd/vet/taglit.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains the test for untagged struct literals. - -package main - -import ( - "flag" - "go/ast" - "strings" -) - -var compositeWhiteList = flag.Bool("compositewhitelist", true, "use composite white list; for testing only") - -// checkUntaggedLiteral checks if a composite literal is a struct literal with -// untagged fields. -func (f *File) checkUntaggedLiteral(c *ast.CompositeLit) { - if !vet("composites") { - return - } - - typ := c.Type - for { - if typ1, ok := c.Type.(*ast.ParenExpr); ok { - typ = typ1 - continue - } - break - } - - switch typ.(type) { - case *ast.ArrayType: - return - case *ast.MapType: - return - case *ast.StructType: - return // a literal struct type does not need to use tags - case *ast.Ident: - // A simple type name like t or T does not need tags either, - // since it is almost certainly declared in the current package. - // (The exception is names being used via import . "pkg", but - // those are already breaking the Go 1 compatibility promise, - // so not reporting potential additional breakage seems okay.) - return - } - - // Otherwise the type is a selector like pkg.Name. - // We only care if pkg.Name is a struct, not if it's a map, array, or slice. - isStruct, typeString := f.pkg.isStruct(c) - if !isStruct { - return - } - - if typeString == "" { // isStruct doesn't know - typeString = f.gofmt(typ) - } - - // It's a struct, or we can't tell it's not a struct because we don't have types. - - // Check if the CompositeLit contains an untagged field. - allKeyValue := true - for _, e := range c.Elts { - if _, ok := e.(*ast.KeyValueExpr); !ok { - allKeyValue = false - break - } - } - if allKeyValue { - return - } - - // Check that the CompositeLit's type has the form pkg.Typ. - s, ok := c.Type.(*ast.SelectorExpr) - if !ok { - return - } - pkg, ok := s.X.(*ast.Ident) - if !ok { - return - } - - // Convert the package name to an import path, and compare to a whitelist. - path := pkgPath(f, pkg.Name) - if path == "" { - f.Badf(c.Pos(), "unresolvable package for %s.%s literal", pkg.Name, s.Sel.Name) - return - } - typeName := path + "." + s.Sel.Name - if *compositeWhiteList && untaggedLiteralWhitelist[typeName] { - return - } - - f.Warn(c.Pos(), typeString+" composite literal uses untagged fields") -} - -// pkgPath returns the import path "image/png" for the package name "png". -// -// This is based purely on syntax and convention, and not on the imported -// package's contents. It will be incorrect if a package name differs from the -// leaf element of the import path, or if the package was a dot import. -func pkgPath(f *File, pkgName string) (path string) { - for _, x := range f.file.Imports { - s := strings.Trim(x.Path.Value, `"`) - if x.Name != nil { - // Catch `import pkgName "foo/bar"`. - if x.Name.Name == pkgName { - return s - } - } else { - // Catch `import "pkgName"` or `import "foo/bar/pkgName"`. - if s == pkgName || strings.HasSuffix(s, "/"+pkgName) { - return s - } - } - } - return "" -} - -var untaggedLiteralWhitelist = map[string]bool{ - /* - These types are actually slices. Syntactically, we cannot tell - whether the Typ in pkg.Typ{1, 2, 3} is a slice or a struct, so we - whitelist all the standard package library's exported slice types. - - find $GOROOT/src/pkg -type f | grep -v _test.go | xargs grep '^type.*\[\]' | \ - grep -v ' map\[' | sed 's,/[^/]*go.type,,' | sed 's,.*src/pkg/,,' | \ - sed 's, ,.,' | sed 's, .*,,' | grep -v '\.[a-z]' | \ - sort | awk '{ print "\"" $0 "\": true," }' - */ - "crypto/x509/pkix.RDNSequence": true, - "crypto/x509/pkix.RelativeDistinguishedNameSET": true, - "database/sql.RawBytes": true, - "debug/macho.LoadBytes": true, - "encoding/asn1.ObjectIdentifier": true, - "encoding/asn1.RawContent": true, - "encoding/json.RawMessage": true, - "encoding/xml.CharData": true, - "encoding/xml.Comment": true, - "encoding/xml.Directive": true, - "go/scanner.ErrorList": true, - "image/color.Palette": true, - "net.HardwareAddr": true, - "net.IP": true, - "net.IPMask": true, - "sort.Float64Slice": true, - "sort.IntSlice": true, - "sort.StringSlice": true, - "unicode.SpecialCase": true, - - // These image and image/color struct types are frozen. We will never add fields to them. - "image/color.Alpha16": true, - "image/color.Alpha": true, - "image/color.Gray16": true, - "image/color.Gray": true, - "image/color.NRGBA64": true, - "image/color.NRGBA": true, - "image/color.RGBA64": true, - "image/color.RGBA": true, - "image/color.YCbCr": true, - "image.Point": true, - "image.Rectangle": true, -} diff --git a/src/cmd/vet/test_asm.go b/src/cmd/vet/test_asm.go deleted file mode 100644 index 098bdd15c..000000000 --- a/src/cmd/vet/test_asm.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build ignore - -// This file contains declarations to test the assembly in test_asm.s. - -package main - -func arg1(x int8, y uint8) -func arg2(x int16, y uint16) -func arg4(x int32, y uint32) -func arg8(x int64, y uint64) -func argint(x int, y uint) -func argptr(x *byte, y *byte, c chan int, m map[int]int, f func()) -func argstring(x, y string) -func argslice(x, y []string) -func argiface(x interface{}, y interface { - m() -}) -func returnint() int -func returnbyte(x int) byte -func returnnamed(x byte) (r1 int, r2 int16, r3 string, r4 byte) diff --git a/src/cmd/vet/test_asm1.s b/src/cmd/vet/test_asm1.s deleted file mode 100644 index 8cd9eeab6..000000000 --- a/src/cmd/vet/test_asm1.s +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build amd64 -// +build vet_test - -TEXT ·arg1(SB),0,$0-2 - MOVB x+0(FP), AX - MOVB y+1(FP), BX - MOVW x+0(FP), AX // ERROR "\[amd64\] invalid MOVW of x\+0\(FP\); int8 is 1-byte value" - MOVW y+1(FP), AX // ERROR "invalid MOVW of y\+1\(FP\); uint8 is 1-byte value" - MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int8 is 1-byte value" - MOVL y+1(FP), AX // ERROR "invalid MOVL of y\+1\(FP\); uint8 is 1-byte value" - MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int8 is 1-byte value" - MOVQ y+1(FP), AX // ERROR "invalid MOVQ of y\+1\(FP\); uint8 is 1-byte value" - MOVB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)" - MOVB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)" - TESTB x+0(FP), AX - TESTB y+1(FP), BX - TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int8 is 1-byte value" - TESTW y+1(FP), AX // ERROR "invalid TESTW of y\+1\(FP\); uint8 is 1-byte value" - TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int8 is 1-byte value" - TESTL y+1(FP), AX // ERROR "invalid TESTL of y\+1\(FP\); uint8 is 1-byte value" - TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int8 is 1-byte value" - TESTQ y+1(FP), AX // ERROR "invalid TESTQ of y\+1\(FP\); uint8 is 1-byte value" - TESTB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)" - TESTB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)" - RET - -TEXT ·arg2(SB),0,$0-4 - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int16 is 2-byte value" - MOVB y+2(FP), AX // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value" - MOVW x+0(FP), AX - MOVW y+2(FP), BX - MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int16 is 2-byte value" - MOVL y+2(FP), AX // ERROR "invalid MOVL of y\+2\(FP\); uint16 is 2-byte value" - MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int16 is 2-byte value" - MOVQ y+2(FP), AX // ERROR "invalid MOVQ of y\+2\(FP\); uint16 is 2-byte value" - MOVW x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)" - MOVW y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)" - TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int16 is 2-byte value" - TESTB y+2(FP), AX // ERROR "invalid TESTB of y\+2\(FP\); uint16 is 2-byte value" - TESTW x+0(FP), AX - TESTW y+2(FP), BX - TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int16 is 2-byte value" - TESTL y+2(FP), AX // ERROR "invalid TESTL of y\+2\(FP\); uint16 is 2-byte value" - TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int16 is 2-byte value" - TESTQ y+2(FP), AX // ERROR "invalid TESTQ of y\+2\(FP\); uint16 is 2-byte value" - TESTW x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)" - TESTW y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)" - RET - -TEXT ·arg4(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value" - MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int32 is 4-byte value" - MOVW y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); uint32 is 4-byte value" - MOVL x+0(FP), AX - MOVL y+4(FP), AX - MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int32 is 4-byte value" - MOVQ y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); uint32 is 4-byte value" - MOVL x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" - MOVL y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" - TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int32 is 4-byte value" - TESTB y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); uint32 is 4-byte value" - TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int32 is 4-byte value" - TESTW y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); uint32 is 4-byte value" - TESTL x+0(FP), AX - TESTL y+4(FP), AX - TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int32 is 4-byte value" - TESTQ y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); uint32 is 4-byte value" - TESTL x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" - TESTL y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" - RET - -TEXT ·arg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value" - MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value" - MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value" - MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int64 is 8-byte value" - MOVL y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); uint64 is 8-byte value" - MOVQ x+0(FP), AX - MOVQ y+8(FP), AX - MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" - MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" - TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int64 is 8-byte value" - TESTB y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); uint64 is 8-byte value" - TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int64 is 8-byte value" - TESTW y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); uint64 is 8-byte value" - TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int64 is 8-byte value" - TESTL y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); uint64 is 8-byte value" - TESTQ x+0(FP), AX - TESTQ y+8(FP), AX - TESTQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" - TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" - RET - -TEXT ·argint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int is 8-byte value" - MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint is 8-byte value" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int is 8-byte value" - MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint is 8-byte value" - MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int is 8-byte value" - MOVL y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); uint is 8-byte value" - MOVQ x+0(FP), AX - MOVQ y+8(FP), AX - MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" - MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" - TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int is 8-byte value" - TESTB y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); uint is 8-byte value" - TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int is 8-byte value" - TESTW y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); uint is 8-byte value" - TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int is 8-byte value" - TESTL y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); uint is 8-byte value" - TESTQ x+0(FP), AX - TESTQ y+8(FP), AX - TESTQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" - TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" - RET - -TEXT ·argptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-40" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 8-byte value" - MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); \*byte is 8-byte value" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); \*byte is 8-byte value" - MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); \*byte is 8-byte value" - MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); \*byte is 8-byte value" - MOVL y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); \*byte is 8-byte value" - MOVQ x+0(FP), AX - MOVQ y+8(FP), AX - MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" - MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" - TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); \*byte is 8-byte value" - TESTB y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); \*byte is 8-byte value" - TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); \*byte is 8-byte value" - TESTW y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); \*byte is 8-byte value" - TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); \*byte is 8-byte value" - TESTL y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); \*byte is 8-byte value" - TESTQ x+0(FP), AX - TESTQ y+8(FP), AX - TESTQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" - TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" - MOVL c+16(FP), AX // ERROR "invalid MOVL of c\+16\(FP\); chan int is 8-byte value" - MOVL m+24(FP), AX // ERROR "invalid MOVL of m\+24\(FP\); map\[int\]int is 8-byte value" - MOVL f+32(FP), AX // ERROR "invalid MOVL of f\+32\(FP\); func\(\) is 8-byte value" - RET - -TEXT ·argstring(SB),0,$32 // ERROR "wrong argument size 0; expected \$\.\.\.-32" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); string base is 8-byte value" - MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); string base is 8-byte value" - MOVQ x+0(FP), AX - MOVW x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); string base is 8-byte value" - MOVL x_base+0(FP), AX // ERROR "invalid MOVL of x_base\+0\(FP\); string base is 8-byte value" - MOVQ x_base+0(FP), AX - MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)" - MOVL x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)" - MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)" - MOVW x_len+8(FP), AX // ERROR "invalid MOVW of x_len\+8\(FP\); string len is 8-byte value" - MOVL x_len+8(FP), AX // ERROR "invalid MOVL of x_len\+8\(FP\); string len is 8-byte value" - MOVQ x_len+8(FP), AX - MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+16\(FP\)" - MOVQ y_len+8(FP), AX // ERROR "invalid offset y_len\+8\(FP\); expected y_len\+24\(FP\)" - RET - -TEXT ·argslice(SB),0,$48 // ERROR "wrong argument size 0; expected \$\.\.\.-48" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); slice base is 8-byte value" - MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); slice base is 8-byte value" - MOVQ x+0(FP), AX - MOVW x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); slice base is 8-byte value" - MOVL x_base+0(FP), AX // ERROR "invalid MOVL of x_base\+0\(FP\); slice base is 8-byte value" - MOVQ x_base+0(FP), AX - MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)" - MOVL x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)" - MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)" - MOVW x_len+8(FP), AX // ERROR "invalid MOVW of x_len\+8\(FP\); slice len is 8-byte value" - MOVL x_len+8(FP), AX // ERROR "invalid MOVL of x_len\+8\(FP\); slice len is 8-byte value" - MOVQ x_len+8(FP), AX - MOVW x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)" - MOVL x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)" - MOVQ x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)" - MOVW x_cap+16(FP), AX // ERROR "invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value" - MOVL x_cap+16(FP), AX // ERROR "invalid MOVL of x_cap\+16\(FP\); slice cap is 8-byte value" - MOVQ x_cap+16(FP), AX - MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+24\(FP\)" - MOVQ y_len+8(FP), AX // ERROR "invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)" - MOVQ y_cap+16(FP), AX // ERROR "invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)" - RET - -TEXT ·argiface(SB),0,$0-32 - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); interface type is 8-byte value" - MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); interface type is 8-byte value" - MOVQ x+0(FP), AX - MOVW x_type+0(FP), AX // ERROR "invalid MOVW of x_type\+0\(FP\); interface type is 8-byte value" - MOVL x_type+0(FP), AX // ERROR "invalid MOVL of x_type\+0\(FP\); interface type is 8-byte value" - MOVQ x_type+0(FP), AX - MOVQ x_itable+0(FP), AX // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)" - MOVQ x_itable+1(FP), AX // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)" - MOVW x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)" - MOVL x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)" - MOVQ x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)" - MOVW x_data+8(FP), AX // ERROR "invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value" - MOVL x_data+8(FP), AX // ERROR "invalid MOVL of x_data\+8\(FP\); interface data is 8-byte value" - MOVQ x_data+8(FP), AX - MOVW y+16(FP), AX // ERROR "invalid MOVW of y\+16\(FP\); interface itable is 8-byte value" - MOVL y+16(FP), AX // ERROR "invalid MOVL of y\+16\(FP\); interface itable is 8-byte value" - MOVQ y+16(FP), AX - MOVW y_itable+16(FP), AX // ERROR "invalid MOVW of y_itable\+16\(FP\); interface itable is 8-byte value" - MOVL y_itable+16(FP), AX // ERROR "invalid MOVL of y_itable\+16\(FP\); interface itable is 8-byte value" - MOVQ y_itable+16(FP), AX - MOVQ y_type+16(FP), AX // ERROR "unknown variable y_type; offset 16 is y_itable\+16\(FP\)" - MOVW y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)" - MOVL y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)" - MOVQ y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)" - MOVW y_data+24(FP), AX // ERROR "invalid MOVW of y_data\+24\(FP\); interface data is 8-byte value" - MOVL y_data+24(FP), AX // ERROR "invalid MOVL of y_data\+24\(FP\); interface data is 8-byte value" - MOVQ y_data+24(FP), AX - RET - -TEXT ·returnint(SB),0,$0-8 - MOVB AX, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 8-byte value" - MOVW AX, ret+0(FP) // ERROR "invalid MOVW of ret\+0\(FP\); int is 8-byte value" - MOVL AX, ret+0(FP) // ERROR "invalid MOVL of ret\+0\(FP\); int is 8-byte value" - MOVQ AX, ret+0(FP) - MOVQ AX, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)" - MOVQ AX, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)" - RET - -TEXT ·returnbyte(SB),0,$0-9 - MOVQ x+0(FP), AX - MOVB AX, ret+8(FP) - MOVW AX, ret+8(FP) // ERROR "invalid MOVW of ret\+8\(FP\); byte is 1-byte value" - MOVL AX, ret+8(FP) // ERROR "invalid MOVL of ret\+8\(FP\); byte is 1-byte value" - MOVQ AX, ret+8(FP) // ERROR "invalid MOVQ of ret\+8\(FP\); byte is 1-byte value" - MOVB AX, ret+7(FP) // ERROR "invalid offset ret\+7\(FP\); expected ret\+8\(FP\)" - RET - -TEXT ·returnnamed(SB),0,$0-41 - MOVB x+0(FP), AX - MOVQ AX, r1+8(FP) - MOVW AX, r2+16(FP) - MOVQ AX, r3+24(FP) - MOVQ AX, r3_base+24(FP) - MOVQ AX, r3_len+32(FP) - MOVB AX, r4+40(FP) - MOVL AX, r1+8(FP) // ERROR "invalid MOVL of r1\+8\(FP\); int is 8-byte value" - RET diff --git a/src/cmd/vet/test_asm2.s b/src/cmd/vet/test_asm2.s deleted file mode 100644 index d8679c574..000000000 --- a/src/cmd/vet/test_asm2.s +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build 386 -// +build vet_test - -TEXT ·arg1(SB),0,$0-2 - MOVB x+0(FP), AX - MOVB y+1(FP), BX - MOVW x+0(FP), AX // ERROR "\[386\] invalid MOVW of x\+0\(FP\); int8 is 1-byte value" - MOVW y+1(FP), AX // ERROR "invalid MOVW of y\+1\(FP\); uint8 is 1-byte value" - MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int8 is 1-byte value" - MOVL y+1(FP), AX // ERROR "invalid MOVL of y\+1\(FP\); uint8 is 1-byte value" - MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int8 is 1-byte value" - MOVQ y+1(FP), AX // ERROR "invalid MOVQ of y\+1\(FP\); uint8 is 1-byte value" - MOVB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)" - MOVB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)" - TESTB x+0(FP), AX - TESTB y+1(FP), BX - TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int8 is 1-byte value" - TESTW y+1(FP), AX // ERROR "invalid TESTW of y\+1\(FP\); uint8 is 1-byte value" - TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int8 is 1-byte value" - TESTL y+1(FP), AX // ERROR "invalid TESTL of y\+1\(FP\); uint8 is 1-byte value" - TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int8 is 1-byte value" - TESTQ y+1(FP), AX // ERROR "invalid TESTQ of y\+1\(FP\); uint8 is 1-byte value" - TESTB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)" - TESTB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)" - RET - -TEXT ·arg2(SB),0,$0-4 - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int16 is 2-byte value" - MOVB y+2(FP), AX // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value" - MOVW x+0(FP), AX - MOVW y+2(FP), BX - MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int16 is 2-byte value" - MOVL y+2(FP), AX // ERROR "invalid MOVL of y\+2\(FP\); uint16 is 2-byte value" - MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int16 is 2-byte value" - MOVQ y+2(FP), AX // ERROR "invalid MOVQ of y\+2\(FP\); uint16 is 2-byte value" - MOVW x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)" - MOVW y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)" - TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int16 is 2-byte value" - TESTB y+2(FP), AX // ERROR "invalid TESTB of y\+2\(FP\); uint16 is 2-byte value" - TESTW x+0(FP), AX - TESTW y+2(FP), BX - TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int16 is 2-byte value" - TESTL y+2(FP), AX // ERROR "invalid TESTL of y\+2\(FP\); uint16 is 2-byte value" - TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int16 is 2-byte value" - TESTQ y+2(FP), AX // ERROR "invalid TESTQ of y\+2\(FP\); uint16 is 2-byte value" - TESTW x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)" - TESTW y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)" - RET - -TEXT ·arg4(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value" - MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int32 is 4-byte value" - MOVW y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); uint32 is 4-byte value" - MOVL x+0(FP), AX - MOVL y+4(FP), AX - MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int32 is 4-byte value" - MOVQ y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); uint32 is 4-byte value" - MOVL x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" - MOVL y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" - TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int32 is 4-byte value" - TESTB y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); uint32 is 4-byte value" - TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int32 is 4-byte value" - TESTW y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); uint32 is 4-byte value" - TESTL x+0(FP), AX - TESTL y+4(FP), AX - TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int32 is 4-byte value" - TESTQ y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); uint32 is 4-byte value" - TESTL x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" - TESTL y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" - RET - -TEXT ·arg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value" - MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value" - MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value" - MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)" - MOVL x_lo+0(FP), AX - MOVL x_hi+4(FP), AX - MOVL y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)" - MOVL y_lo+8(FP), AX - MOVL y_hi+12(FP), AX - MOVQ x+0(FP), AX - MOVQ y+8(FP), AX - MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" - MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" - TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int64 is 8-byte value" - TESTB y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); uint64 is 8-byte value" - TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int64 is 8-byte value" - TESTW y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); uint64 is 8-byte value" - TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)" - TESTL y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)" - TESTQ x+0(FP), AX - TESTQ y+8(FP), AX - TESTQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" - TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" - RET - -TEXT ·argint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int is 4-byte value" - MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint is 4-byte value" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int is 4-byte value" - MOVW y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); uint is 4-byte value" - MOVL x+0(FP), AX - MOVL y+4(FP), AX - MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int is 4-byte value" - MOVQ y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); uint is 4-byte value" - MOVQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" - MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" - TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int is 4-byte value" - TESTB y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); uint is 4-byte value" - TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int is 4-byte value" - TESTW y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); uint is 4-byte value" - TESTL x+0(FP), AX - TESTL y+4(FP), AX - TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int is 4-byte value" - TESTQ y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); uint is 4-byte value" - TESTQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" - TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" - RET - -TEXT ·argptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-20" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 4-byte value" - MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); \*byte is 4-byte value" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); \*byte is 4-byte value" - MOVW y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); \*byte is 4-byte value" - MOVL x+0(FP), AX - MOVL y+4(FP), AX - MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); \*byte is 4-byte value" - MOVQ y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); \*byte is 4-byte value" - MOVQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" - MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" - TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); \*byte is 4-byte value" - TESTB y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); \*byte is 4-byte value" - TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); \*byte is 4-byte value" - TESTW y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); \*byte is 4-byte value" - TESTL x+0(FP), AX - TESTL y+4(FP), AX - TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); \*byte is 4-byte value" - TESTQ y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); \*byte is 4-byte value" - TESTQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" - TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" - MOVW c+8(FP), AX // ERROR "invalid MOVW of c\+8\(FP\); chan int is 4-byte value" - MOVW m+12(FP), AX // ERROR "invalid MOVW of m\+12\(FP\); map\[int\]int is 4-byte value" - MOVW f+16(FP), AX // ERROR "invalid MOVW of f\+16\(FP\); func\(\) is 4-byte value" - RET - -TEXT ·argstring(SB),0,$16 // ERROR "wrong argument size 0; expected \$\.\.\.-16" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); string base is 4-byte value" - MOVL x+0(FP), AX - MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); string base is 4-byte value" - MOVW x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); string base is 4-byte value" - MOVL x_base+0(FP), AX - MOVQ x_base+0(FP), AX // ERROR "invalid MOVQ of x_base\+0\(FP\); string base is 4-byte value" - MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVL x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVW x_len+4(FP), AX // ERROR "invalid MOVW of x_len\+4\(FP\); string len is 4-byte value" - MOVL x_len+4(FP), AX - MOVQ x_len+4(FP), AX // ERROR "invalid MOVQ of x_len\+4\(FP\); string len is 4-byte value" - MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+8\(FP\)" - MOVQ y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+12\(FP\)" - RET - -TEXT ·argslice(SB),0,$24 // ERROR "wrong argument size 0; expected \$\.\.\.-24" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); slice base is 4-byte value" - MOVL x+0(FP), AX - MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); slice base is 4-byte value" - MOVW x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); slice base is 4-byte value" - MOVL x_base+0(FP), AX - MOVQ x_base+0(FP), AX // ERROR "invalid MOVQ of x_base\+0\(FP\); slice base is 4-byte value" - MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVL x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVW x_len+4(FP), AX // ERROR "invalid MOVW of x_len\+4\(FP\); slice len is 4-byte value" - MOVL x_len+4(FP), AX - MOVQ x_len+4(FP), AX // ERROR "invalid MOVQ of x_len\+4\(FP\); slice len is 4-byte value" - MOVW x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)" - MOVL x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)" - MOVQ x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)" - MOVW x_cap+8(FP), AX // ERROR "invalid MOVW of x_cap\+8\(FP\); slice cap is 4-byte value" - MOVL x_cap+8(FP), AX - MOVQ x_cap+8(FP), AX // ERROR "invalid MOVQ of x_cap\+8\(FP\); slice cap is 4-byte value" - MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+12\(FP\)" - MOVQ y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)" - MOVQ y_cap+8(FP), AX // ERROR "invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)" - RET - -TEXT ·argiface(SB),0,$0-16 - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); interface type is 4-byte value" - MOVL x+0(FP), AX - MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); interface type is 4-byte value" - MOVW x_type+0(FP), AX // ERROR "invalid MOVW of x_type\+0\(FP\); interface type is 4-byte value" - MOVL x_type+0(FP), AX - MOVQ x_type+0(FP), AX // ERROR "invalid MOVQ of x_type\+0\(FP\); interface type is 4-byte value" - MOVQ x_itable+0(FP), AX // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)" - MOVQ x_itable+1(FP), AX // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)" - MOVW x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)" - MOVL x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)" - MOVQ x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)" - MOVW x_data+4(FP), AX // ERROR "invalid MOVW of x_data\+4\(FP\); interface data is 4-byte value" - MOVL x_data+4(FP), AX - MOVQ x_data+4(FP), AX // ERROR "invalid MOVQ of x_data\+4\(FP\); interface data is 4-byte value" - MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); interface itable is 4-byte value" - MOVL y+8(FP), AX - MOVQ y+8(FP), AX // ERROR "invalid MOVQ of y\+8\(FP\); interface itable is 4-byte value" - MOVW y_itable+8(FP), AX // ERROR "invalid MOVW of y_itable\+8\(FP\); interface itable is 4-byte value" - MOVL y_itable+8(FP), AX - MOVQ y_itable+8(FP), AX // ERROR "invalid MOVQ of y_itable\+8\(FP\); interface itable is 4-byte value" - MOVQ y_type+8(FP), AX // ERROR "unknown variable y_type; offset 8 is y_itable\+8\(FP\)" - MOVW y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)" - MOVL y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)" - MOVQ y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)" - MOVW y_data+12(FP), AX // ERROR "invalid MOVW of y_data\+12\(FP\); interface data is 4-byte value" - MOVL y_data+12(FP), AX - MOVQ y_data+12(FP), AX // ERROR "invalid MOVQ of y_data\+12\(FP\); interface data is 4-byte value" - RET - -TEXT ·returnint(SB),0,$0-4 - MOVB AX, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 4-byte value" - MOVW AX, ret+0(FP) // ERROR "invalid MOVW of ret\+0\(FP\); int is 4-byte value" - MOVL AX, ret+0(FP) - MOVQ AX, ret+0(FP) // ERROR "invalid MOVQ of ret\+0\(FP\); int is 4-byte value" - MOVQ AX, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)" - MOVQ AX, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)" - RET - -TEXT ·returnbyte(SB),0,$0-5 - MOVL x+0(FP), AX - MOVB AX, ret+4(FP) - MOVW AX, ret+4(FP) // ERROR "invalid MOVW of ret\+4\(FP\); byte is 1-byte value" - MOVL AX, ret+4(FP) // ERROR "invalid MOVL of ret\+4\(FP\); byte is 1-byte value" - MOVQ AX, ret+4(FP) // ERROR "invalid MOVQ of ret\+4\(FP\); byte is 1-byte value" - MOVB AX, ret+3(FP) // ERROR "invalid offset ret\+3\(FP\); expected ret\+4\(FP\)" - RET - -TEXT ·returnnamed(SB),0,$0-21 - MOVB x+0(FP), AX - MOVL AX, r1+4(FP) - MOVW AX, r2+8(FP) - MOVL AX, r3+12(FP) - MOVL AX, r3_base+12(FP) - MOVL AX, r3_len+16(FP) - MOVB AX, r4+20(FP) - MOVQ AX, r1+4(FP) // ERROR "invalid MOVQ of r1\+4\(FP\); int is 4-byte value" - RET diff --git a/src/cmd/vet/test_asm3.s b/src/cmd/vet/test_asm3.s deleted file mode 100644 index bf98805a2..000000000 --- a/src/cmd/vet/test_asm3.s +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build arm -// +build vet_test - -TEXT ·arg1(SB),0,$0-2 - MOVB x+0(FP), AX - MOVB y+1(FP), BX - MOVH x+0(FP), AX // ERROR "\[arm\] invalid MOVH of x\+0\(FP\); int8 is 1-byte value" - MOVH y+1(FP), AX // ERROR "invalid MOVH of y\+1\(FP\); uint8 is 1-byte value" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int8 is 1-byte value" - MOVW y+1(FP), AX // ERROR "invalid MOVW of y\+1\(FP\); uint8 is 1-byte value" - MOVB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)" - MOVB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)" - RET - -TEXT ·arg2(SB),0,$0-4 - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int16 is 2-byte value" - MOVB y+2(FP), AX // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value" - MOVH x+0(FP), AX - MOVH y+2(FP), BX - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int16 is 2-byte value" - MOVW y+2(FP), AX // ERROR "invalid MOVW of y\+2\(FP\); uint16 is 2-byte value" - MOVH x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)" - MOVH y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)" - RET - -TEXT ·arg4(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value" - MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value" - MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); int32 is 4-byte value" - MOVH y+4(FP), AX // ERROR "invalid MOVH of y\+4\(FP\); uint32 is 4-byte value" - MOVW x+0(FP), AX - MOVW y+4(FP), AX - MOVW x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" - MOVW y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" - RET - -TEXT ·arg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value" - MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value" - MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); int64 is 8-byte value" - MOVH y+8(FP), AX // ERROR "invalid MOVH of y\+8\(FP\); uint64 is 8-byte value" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)" - MOVW x_lo+0(FP), AX - MOVW x_hi+4(FP), AX - MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)" - MOVW y_lo+8(FP), AX - MOVW y_hi+12(FP), AX - MOVQ x+0(FP), AX - MOVQ y+8(FP), AX - MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" - MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" - RET - -TEXT ·argint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int is 4-byte value" - MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint is 4-byte value" - MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); int is 4-byte value" - MOVH y+4(FP), AX // ERROR "invalid MOVH of y\+4\(FP\); uint is 4-byte value" - MOVW x+0(FP), AX - MOVW y+4(FP), AX - MOVQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" - MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" - RET - -TEXT ·argptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-20" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 4-byte value" - MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); \*byte is 4-byte value" - MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); \*byte is 4-byte value" - MOVH y+4(FP), AX // ERROR "invalid MOVH of y\+4\(FP\); \*byte is 4-byte value" - MOVW x+0(FP), AX - MOVW y+4(FP), AX - MOVQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" - MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" - MOVH c+8(FP), AX // ERROR "invalid MOVH of c\+8\(FP\); chan int is 4-byte value" - MOVH m+12(FP), AX // ERROR "invalid MOVH of m\+12\(FP\); map\[int\]int is 4-byte value" - MOVH f+16(FP), AX // ERROR "invalid MOVH of f\+16\(FP\); func\(\) is 4-byte value" - RET - -TEXT ·argstring(SB),0,$16 // ERROR "wrong argument size 0; expected \$\.\.\.-16" - MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); string base is 4-byte value" - MOVW x+0(FP), AX - MOVH x_base+0(FP), AX // ERROR "invalid MOVH of x_base\+0\(FP\); string base is 4-byte value" - MOVW x_base+0(FP), AX - MOVH x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVH x_len+4(FP), AX // ERROR "invalid MOVH of x_len\+4\(FP\); string len is 4-byte value" - MOVW x_len+4(FP), AX - MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+8\(FP\)" - MOVQ y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+12\(FP\)" - RET - -TEXT ·argslice(SB),0,$24 // ERROR "wrong argument size 0; expected \$\.\.\.-24" - MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); slice base is 4-byte value" - MOVW x+0(FP), AX - MOVH x_base+0(FP), AX // ERROR "invalid MOVH of x_base\+0\(FP\); slice base is 4-byte value" - MOVW x_base+0(FP), AX - MOVH x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVH x_len+4(FP), AX // ERROR "invalid MOVH of x_len\+4\(FP\); slice len is 4-byte value" - MOVW x_len+4(FP), AX - MOVH x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)" - MOVW x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)" - MOVQ x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)" - MOVH x_cap+8(FP), AX // ERROR "invalid MOVH of x_cap\+8\(FP\); slice cap is 4-byte value" - MOVW x_cap+8(FP), AX - MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+12\(FP\)" - MOVQ y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)" - MOVQ y_cap+8(FP), AX // ERROR "invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)" - RET - -TEXT ·argiface(SB),0,$0-16 - MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); interface type is 4-byte value" - MOVW x+0(FP), AX - MOVH x_type+0(FP), AX // ERROR "invalid MOVH of x_type\+0\(FP\); interface type is 4-byte value" - MOVW x_type+0(FP), AX - MOVQ x_itable+0(FP), AX // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)" - MOVQ x_itable+1(FP), AX // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)" - MOVH x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)" - MOVW x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)" - MOVQ x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)" - MOVH x_data+4(FP), AX // ERROR "invalid MOVH of x_data\+4\(FP\); interface data is 4-byte value" - MOVW x_data+4(FP), AX - MOVH y+8(FP), AX // ERROR "invalid MOVH of y\+8\(FP\); interface itable is 4-byte value" - MOVW y+8(FP), AX - MOVH y_itable+8(FP), AX // ERROR "invalid MOVH of y_itable\+8\(FP\); interface itable is 4-byte value" - MOVW y_itable+8(FP), AX - MOVQ y_type+8(FP), AX // ERROR "unknown variable y_type; offset 8 is y_itable\+8\(FP\)" - MOVH y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)" - MOVW y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)" - MOVQ y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)" - MOVH y_data+12(FP), AX // ERROR "invalid MOVH of y_data\+12\(FP\); interface data is 4-byte value" - MOVW y_data+12(FP), AX - RET - -TEXT ·returnint(SB),0,$0-4 - MOVB AX, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 4-byte value" - MOVH AX, ret+0(FP) // ERROR "invalid MOVH of ret\+0\(FP\); int is 4-byte value" - MOVW AX, ret+0(FP) - MOVQ AX, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)" - MOVQ AX, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)" - RET - -TEXT ·returnbyte(SB),0,$0-5 - MOVW x+0(FP), AX - MOVB AX, ret+4(FP) - MOVH AX, ret+4(FP) // ERROR "invalid MOVH of ret\+4\(FP\); byte is 1-byte value" - MOVW AX, ret+4(FP) // ERROR "invalid MOVW of ret\+4\(FP\); byte is 1-byte value" - MOVB AX, ret+3(FP) // ERROR "invalid offset ret\+3\(FP\); expected ret\+4\(FP\)" - RET - -TEXT ·returnnamed(SB),0,$0-21 - MOVB x+0(FP), AX - MOVW AX, r1+4(FP) - MOVH AX, r2+8(FP) - MOVW AX, r3+12(FP) - MOVW AX, r3_base+12(FP) - MOVW AX, r3_len+16(FP) - MOVB AX, r4+20(FP) - MOVB AX, r1+4(FP) // ERROR "invalid MOVB of r1\+4\(FP\); int is 4-byte value" - RET diff --git a/src/cmd/vet/test_assign.go b/src/cmd/vet/test_assign.go deleted file mode 100644 index 8e0f45e53..000000000 --- a/src/cmd/vet/test_assign.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains tests for the useless-assignment checker. - -// +build vet_test - -package main - -type ST struct { - x int -} - -func (s *ST) SetX(x int) { - // Accidental self-assignment; it should be "s.x = x" - x = x // ERROR "self-assignment of x to x" - // Another mistake - s.x = s.x // ERROR "self-assignment of s.x to s.x" -} diff --git a/src/cmd/vet/test_atomic.go b/src/cmd/vet/test_atomic.go deleted file mode 100644 index 9231e9dc0..000000000 --- a/src/cmd/vet/test_atomic.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build vet_test - -// This file contains tests for the atomic checker. - -package main - -import ( - "sync/atomic" -) - -type Counter uint64 - -func AtomicTests() { - x := uint64(1) - x = atomic.AddUint64(&x, 1) // ERROR "direct assignment to atomic value" - _, x = 10, atomic.AddUint64(&x, 1) // ERROR "direct assignment to atomic value" - x, _ = atomic.AddUint64(&x, 1), 10 // ERROR "direct assignment to atomic value" - - y := &x - *y = atomic.AddUint64(y, 1) // ERROR "direct assignment to atomic value" - - var su struct{ Counter uint64 } - su.Counter = atomic.AddUint64(&su.Counter, 1) // ERROR "direct assignment to atomic value" - z1 := atomic.AddUint64(&su.Counter, 1) - _ = z1 // Avoid err "z declared and not used" - - var sp struct{ Counter *uint64 } - *sp.Counter = atomic.AddUint64(sp.Counter, 1) // ERROR "direct assignment to atomic value" - z2 := atomic.AddUint64(sp.Counter, 1) - _ = z2 // Avoid err "z declared and not used" - - au := []uint64{10, 20} - au[0] = atomic.AddUint64(&au[0], 1) // ERROR "direct assignment to atomic value" - au[1] = atomic.AddUint64(&au[0], 1) - - ap := []*uint64{&au[0], &au[1]} - *ap[0] = atomic.AddUint64(ap[0], 1) // ERROR "direct assignment to atomic value" - *ap[1] = atomic.AddUint64(ap[0], 1) -} diff --git a/src/cmd/vet/test_buildtag.go b/src/cmd/vet/test_buildtag.go deleted file mode 100644 index d7174ade2..000000000 --- a/src/cmd/vet/test_buildtag.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains tests for the buildtag checker. - -// +build vet_test -// +builder // ERROR "possible malformed \+build comment" -// +build !ignore - -package main - -// +build toolate // ERROR "build comment appears too late in file" - -var _ = 3 diff --git a/src/cmd/vet/test_buildtag_bad.go b/src/cmd/vet/test_buildtag_bad.go deleted file mode 100644 index 0a0a39bd1..000000000 --- a/src/cmd/vet/test_buildtag_bad.go +++ /dev/null @@ -1,15 +0,0 @@ -// This file contains misplaced or malformed build constraints. -// The Go tool will skip it, because the constraints are invalid. -// It serves only to test the tag checker during make test. - -// Mention +build // ERROR "possible malformed \+build comment" - -// +build !!bang // ERROR "invalid double negative in build constraint" -// +build @#$ // ERROR "invalid non-alphanumeric build constraint" - -// +build toolate // ERROR "build comment appears too late in file" -package bad - -// This is package 'bad' rather than 'main' so the erroneous build -// tag doesn't end up looking like a package doc for the vet command -// when examined by godoc. diff --git a/src/cmd/vet/test_deadcode.go b/src/cmd/vet/test_deadcode.go deleted file mode 100644 index d08e57782..000000000 --- a/src/cmd/vet/test_deadcode.go +++ /dev/null @@ -1,2121 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build vet_test -// +build ignore - -// This file contains tests for the dead code checker. - -package main - -type T int - -var x interface{} -var c chan int - -func external() int // ok - -func _() int { -} - -func _() int { - print(1) -} - -func _() int { - print(1) - return 2 - println() // ERROR "unreachable code" -} - -func _() int { -L: - print(1) - goto L - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - panic(2) - println() // ERROR "unreachable code" -} - -// but only builtin panic -func _() int { - var panic = func(int) {} - print(1) - panic(2) - println() // ok -} - -func _() int { - { - print(1) - return 2 - println() // ERROR "unreachable code" - } - println() // ok -} - -func _() int { - { - print(1) - return 2 - } - println() // ERROR "unreachable code" -} - -func _() int { -L: - { - print(1) - goto L - println() // ERROR "unreachable code" - } - println() // ok -} - -func _() int { -L: - { - print(1) - goto L - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - { - panic(2) - } -} - -func _() int { - print(1) - { - panic(2) - println() // ERROR "unreachable code" - } -} - -func _() int { - print(1) - { - panic(2) - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - return 2 - { // ERROR "unreachable code" - } -} - -func _() int { -L: - print(1) - goto L - { // ERROR "unreachable code" - } -} - -func _() int { - print(1) - panic(2) - { // ERROR "unreachable code" - } -} - -func _() int { - { - print(1) - return 2 - { // ERROR "unreachable code" - } - } -} - -func _() int { -L: - { - print(1) - goto L - { // ERROR "unreachable code" - } - } -} - -func _() int { - print(1) - { - panic(2) - { // ERROR "unreachable code" - } - } -} - -func _() int { - { - print(1) - return 2 - } - { // ERROR "unreachable code" - } -} - -func _() int { -L: - { - print(1) - goto L - } - { // ERROR "unreachable code" - } -} - -func _() int { - print(1) - { - panic(2) - } - { // ERROR "unreachable code" - } -} - -func _() int { - print(1) - if x == nil { - panic(2) - } else { - panic(3) - } - println() // ERROR "unreachable code" -} - -func _() int { -L: - print(1) - if x == nil { - panic(2) - } else { - goto L - } - println() // ERROR "unreachable code" -} - -func _() int { -L: - print(1) - if x == nil { - panic(2) - } else if x == 1 { - return 0 - } else if x != 2 { - panic(3) - } else { - goto L - } - println() // ERROR "unreachable code" -} - -// if-else chain missing final else is not okay, even if the -// conditions cover every possible case. - -func _() int { - print(1) - if x == nil { - panic(2) - } else if x != nil { - panic(3) - } - println() // ok -} - -func _() int { - print(1) - if x == nil { - panic(2) - } - println() // ok -} - -func _() int { -L: - print(1) - if x == nil { - panic(2) - } else if x == 1 { - return 0 - } else if x != 1 { - panic(3) - } - println() // ok -} - -func _() int { - print(1) - for { - } - println() // ERROR "unreachable code" -} - -func _() int { - for { - for { - break - } - } - println() // ERROR "unreachable code" -} - -func _() int { - for { - for { - break - println() // ERROR "unreachable code" - } - } -} - -func _() int { - for { - for { - continue - println() // ERROR "unreachable code" - } - } -} - -func _() int { - for { - L: - for { - break L - } - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - for { - break - } - println() // ok -} - -func _() int { - for { - for { - } - break // ERROR "unreachable code" - } - println() // ok -} - -func _() int { -L: - for { - for { - break L - } - } - println() // ok -} - -func _() int { - print(1) - for x == nil { - } - println() // ok -} - -func _() int { - for x == nil { - for { - break - } - } - println() // ok -} - -func _() int { - for x == nil { - L: - for { - break L - } - } - println() // ok -} - -func _() int { - print(1) - for true { - } - println() // ok -} - -func _() int { - for true { - for { - break - } - } - println() // ok -} - -func _() int { - for true { - L: - for { - break L - } - } - println() // ok -} - -func _() int { - print(1) - select {} - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - println() // ERROR "unreachable code" - } -} - -func _() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - select { - case <-c: - print(2) - for { - } - println() // ERROR "unreachable code" - } -} - -func _() int { - print(1) - select { - case <-c: - print(2) - for { - } - } - println() // ERROR "unreachable code" -} - -func _() int { -L: - print(1) - select { - case <-c: - print(2) - panic("abc") - println() // ERROR "unreachable code" - case c <- 1: - print(2) - goto L - println() // ERROR "unreachable code" - } -} - -func _() int { -L: - print(1) - select { - case <-c: - print(2) - panic("abc") - case c <- 1: - print(2) - goto L - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - println() // ERROR "unreachable code" - default: - select {} - println() // ERROR "unreachable code" - } -} - -func _() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - default: - select {} - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - select { - case <-c: - print(2) - } - println() // ok -} - -func _() int { -L: - print(1) - select { - case <-c: - print(2) - panic("abc") - goto L // ERROR "unreachable code" - case c <- 1: - print(2) - } - println() // ok -} - -func _() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - default: - print(2) - } - println() // ok -} - -func _() int { - print(1) - select { - default: - break - } - println() // ok -} - -func _() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - break // ERROR "unreachable code" - } - println() // ok -} - -func _() int { - print(1) -L: - select { - case <-c: - print(2) - for { - break L - } - } - println() // ok -} - -func _() int { - print(1) -L: - select { - case <-c: - print(2) - panic("abc") - case c <- 1: - print(2) - break L - } - println() // ok -} - -func _() int { - print(1) - select { - case <-c: - print(1) - panic("abc") - default: - select {} - break // ERROR "unreachable code" - } - println() // ok -} - -func _() int { - print(1) - switch x { - case 1: - print(2) - panic(3) - println() // ERROR "unreachable code" - default: - return 4 - println() // ERROR "unreachable code" - } -} - -func _() int { - print(1) - switch x { - case 1: - print(2) - panic(3) - default: - return 4 - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - switch x { - default: - return 4 - println() // ERROR "unreachable code" - case 1: - print(2) - panic(3) - println() // ERROR "unreachable code" - } -} - -func _() int { - print(1) - switch x { - default: - return 4 - case 1: - print(2) - panic(3) - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - switch x { - case 1: - print(2) - fallthrough - default: - return 4 - println() // ERROR "unreachable code" - } -} - -func _() int { - print(1) - switch x { - case 1: - print(2) - fallthrough - default: - return 4 - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - switch { - } - println() // ok -} - -func _() int { - print(1) - switch x { - case 1: - print(2) - panic(3) - case 2: - return 4 - } - println() // ok -} - -func _() int { - print(1) - switch x { - case 2: - return 4 - case 1: - print(2) - panic(3) - } - println() // ok -} - -func _() int { - print(1) - switch x { - case 1: - print(2) - fallthrough - case 2: - return 4 - } - println() // ok -} - -func _() int { - print(1) - switch x { - case 1: - print(2) - panic(3) - } - println() // ok -} - -func _() int { - print(1) -L: - switch x { - case 1: - print(2) - panic(3) - break L // ERROR "unreachable code" - default: - return 4 - } - println() // ok -} - -func _() int { - print(1) - switch x { - default: - return 4 - break // ERROR "unreachable code" - case 1: - print(2) - panic(3) - } - println() // ok -} - -func _() int { - print(1) -L: - switch x { - case 1: - print(2) - for { - break L - } - default: - return 4 - } - println() // ok -} - -func _() int { - print(1) - switch x.(type) { - case int: - print(2) - panic(3) - println() // ERROR "unreachable code" - default: - return 4 - println() // ERROR "unreachable code" - } -} - -func _() int { - print(1) - switch x.(type) { - case int: - print(2) - panic(3) - default: - return 4 - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - switch x.(type) { - default: - return 4 - println() // ERROR "unreachable code" - case int: - print(2) - panic(3) - println() // ERROR "unreachable code" - } -} - -func _() int { - print(1) - switch x.(type) { - default: - return 4 - case int: - print(2) - panic(3) - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - switch x.(type) { - case int: - print(2) - fallthrough - default: - return 4 - println() // ERROR "unreachable code" - } -} - -func _() int { - print(1) - switch x.(type) { - case int: - print(2) - fallthrough - default: - return 4 - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - switch { - } - println() // ok -} - -func _() int { - print(1) - switch x.(type) { - case int: - print(2) - panic(3) - case float64: - return 4 - } - println() // ok -} - -func _() int { - print(1) - switch x.(type) { - case float64: - return 4 - case int: - print(2) - panic(3) - } - println() // ok -} - -func _() int { - print(1) - switch x.(type) { - case int: - print(2) - fallthrough - case float64: - return 4 - } - println() // ok -} - -func _() int { - print(1) - switch x.(type) { - case int: - print(2) - panic(3) - } - println() // ok -} - -func _() int { - print(1) -L: - switch x.(type) { - case int: - print(2) - panic(3) - break L // ERROR "unreachable code" - default: - return 4 - } - println() // ok -} - -func _() int { - print(1) - switch x.(type) { - default: - return 4 - break // ERROR "unreachable code" - case int: - print(2) - panic(3) - } - println() // ok -} - -func _() int { - print(1) -L: - switch x.(type) { - case int: - print(2) - for { - break L - } - default: - return 4 - } - println() // ok -} - -// again, but without the leading print(1). -// testing that everything works when the terminating statement is first. - -func _() int { - println() // ok -} - -func _() int { - return 2 - println() // ERROR "unreachable code" -} - -func _() int { -L: - goto L - println() // ERROR "unreachable code" -} - -func _() int { - panic(2) - println() // ERROR "unreachable code" -} - -// but only builtin panic -func _() int { - var panic = func(int) {} - panic(2) - println() // ok -} - -func _() int { - { - return 2 - println() // ERROR "unreachable code" - } -} - -func _() int { - { - return 2 - } - println() // ERROR "unreachable code" -} - -func _() int { -L: - { - goto L - println() // ERROR "unreachable code" - } -} - -func _() int { -L: - { - goto L - } - println() // ERROR "unreachable code" -} - -func _() int { - { - panic(2) - println() // ERROR "unreachable code" - } -} - -func _() int { - { - panic(2) - } - println() // ERROR "unreachable code" -} - -func _() int { - return 2 - { // ERROR "unreachable code" - } - println() // ok -} - -func _() int { -L: - goto L - { // ERROR "unreachable code" - } - println() // ok -} - -func _() int { - panic(2) - { // ERROR "unreachable code" - } - println() // ok -} - -func _() int { - { - return 2 - { // ERROR "unreachable code" - } - } - println() // ok -} - -func _() int { -L: - { - goto L - { // ERROR "unreachable code" - } - } - println() // ok -} - -func _() int { - { - panic(2) - { // ERROR "unreachable code" - } - } - println() // ok -} - -func _() int { - { - return 2 - } - { // ERROR "unreachable code" - } - println() // ok -} - -func _() int { -L: - { - goto L - } - { // ERROR "unreachable code" - } - println() // ok -} - -func _() int { - { - panic(2) - } - { // ERROR "unreachable code" - } - println() // ok -} - -// again, with func literals - -var _ = func() int { -} - -var _ = func() int { - print(1) -} - -var _ = func() int { - print(1) - return 2 - println() // ERROR "unreachable code" -} - -var _ = func() int { -L: - print(1) - goto L - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - panic(2) - println() // ERROR "unreachable code" -} - -// but only builtin panic -var _ = func() int { - var panic = func(int) {} - print(1) - panic(2) - println() // ok -} - -var _ = func() int { - { - print(1) - return 2 - println() // ERROR "unreachable code" - } - println() // ok -} - -var _ = func() int { - { - print(1) - return 2 - } - println() // ERROR "unreachable code" -} - -var _ = func() int { -L: - { - print(1) - goto L - println() // ERROR "unreachable code" - } - println() // ok -} - -var _ = func() int { -L: - { - print(1) - goto L - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - { - panic(2) - } -} - -var _ = func() int { - print(1) - { - panic(2) - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - { - panic(2) - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - return 2 - { // ERROR "unreachable code" - } -} - -var _ = func() int { -L: - print(1) - goto L - { // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - panic(2) - { // ERROR "unreachable code" - } -} - -var _ = func() int { - { - print(1) - return 2 - { // ERROR "unreachable code" - } - } -} - -var _ = func() int { -L: - { - print(1) - goto L - { // ERROR "unreachable code" - } - } -} - -var _ = func() int { - print(1) - { - panic(2) - { // ERROR "unreachable code" - } - } -} - -var _ = func() int { - { - print(1) - return 2 - } - { // ERROR "unreachable code" - } -} - -var _ = func() int { -L: - { - print(1) - goto L - } - { // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - { - panic(2) - } - { // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - if x == nil { - panic(2) - } else { - panic(3) - } - println() // ERROR "unreachable code" -} - -var _ = func() int { -L: - print(1) - if x == nil { - panic(2) - } else { - goto L - } - println() // ERROR "unreachable code" -} - -var _ = func() int { -L: - print(1) - if x == nil { - panic(2) - } else if x == 1 { - return 0 - } else if x != 2 { - panic(3) - } else { - goto L - } - println() // ERROR "unreachable code" -} - -// if-else chain missing final else is not okay, even if the -// conditions cover every possible case. - -var _ = func() int { - print(1) - if x == nil { - panic(2) - } else if x != nil { - panic(3) - } - println() // ok -} - -var _ = func() int { - print(1) - if x == nil { - panic(2) - } - println() // ok -} - -var _ = func() int { -L: - print(1) - if x == nil { - panic(2) - } else if x == 1 { - return 0 - } else if x != 1 { - panic(3) - } - println() // ok -} - -var _ = func() int { - print(1) - for { - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - for { - for { - break - } - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - for { - for { - break - println() // ERROR "unreachable code" - } - } -} - -var _ = func() int { - for { - for { - continue - println() // ERROR "unreachable code" - } - } -} - -var _ = func() int { - for { - L: - for { - break L - } - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - for { - break - } - println() // ok -} - -var _ = func() int { - for { - for { - } - break // ERROR "unreachable code" - } - println() // ok -} - -var _ = func() int { -L: - for { - for { - break L - } - } - println() // ok -} - -var _ = func() int { - print(1) - for x == nil { - } - println() // ok -} - -var _ = func() int { - for x == nil { - for { - break - } - } - println() // ok -} - -var _ = func() int { - for x == nil { - L: - for { - break L - } - } - println() // ok -} - -var _ = func() int { - print(1) - for true { - } - println() // ok -} - -var _ = func() int { - for true { - for { - break - } - } - println() // ok -} - -var _ = func() int { - for true { - L: - for { - break L - } - } - println() // ok -} - -var _ = func() int { - print(1) - select {} - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - select { - case <-c: - print(2) - for { - } - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - select { - case <-c: - print(2) - for { - } - } - println() // ERROR "unreachable code" -} - -var _ = func() int { -L: - print(1) - select { - case <-c: - print(2) - panic("abc") - println() // ERROR "unreachable code" - case c <- 1: - print(2) - goto L - println() // ERROR "unreachable code" - } -} - -var _ = func() int { -L: - print(1) - select { - case <-c: - print(2) - panic("abc") - case c <- 1: - print(2) - goto L - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - println() // ERROR "unreachable code" - default: - select {} - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - default: - select {} - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - select { - case <-c: - print(2) - } - println() // ok -} - -var _ = func() int { -L: - print(1) - select { - case <-c: - print(2) - panic("abc") - goto L // ERROR "unreachable code" - case c <- 1: - print(2) - } - println() // ok -} - -var _ = func() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - default: - print(2) - } - println() // ok -} - -var _ = func() int { - print(1) - select { - default: - break - } - println() // ok -} - -var _ = func() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - break // ERROR "unreachable code" - } - println() // ok -} - -var _ = func() int { - print(1) -L: - select { - case <-c: - print(2) - for { - break L - } - } - println() // ok -} - -var _ = func() int { - print(1) -L: - select { - case <-c: - print(2) - panic("abc") - case c <- 1: - print(2) - break L - } - println() // ok -} - -var _ = func() int { - print(1) - select { - case <-c: - print(1) - panic("abc") - default: - select {} - break // ERROR "unreachable code" - } - println() // ok -} - -var _ = func() int { - print(1) - switch x { - case 1: - print(2) - panic(3) - println() // ERROR "unreachable code" - default: - return 4 - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - switch x { - case 1: - print(2) - panic(3) - default: - return 4 - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - switch x { - default: - return 4 - println() // ERROR "unreachable code" - case 1: - print(2) - panic(3) - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - switch x { - default: - return 4 - case 1: - print(2) - panic(3) - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - switch x { - case 1: - print(2) - fallthrough - default: - return 4 - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - switch x { - case 1: - print(2) - fallthrough - default: - return 4 - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - switch { - } - println() // ok -} - -var _ = func() int { - print(1) - switch x { - case 1: - print(2) - panic(3) - case 2: - return 4 - } - println() // ok -} - -var _ = func() int { - print(1) - switch x { - case 2: - return 4 - case 1: - print(2) - panic(3) - } - println() // ok -} - -var _ = func() int { - print(1) - switch x { - case 1: - print(2) - fallthrough - case 2: - return 4 - } - println() // ok -} - -var _ = func() int { - print(1) - switch x { - case 1: - print(2) - panic(3) - } - println() // ok -} - -var _ = func() int { - print(1) -L: - switch x { - case 1: - print(2) - panic(3) - break L // ERROR "unreachable code" - default: - return 4 - } - println() // ok -} - -var _ = func() int { - print(1) - switch x { - default: - return 4 - break // ERROR "unreachable code" - case 1: - print(2) - panic(3) - } - println() // ok -} - -var _ = func() int { - print(1) -L: - switch x { - case 1: - print(2) - for { - break L - } - default: - return 4 - } - println() // ok -} - -var _ = func() int { - print(1) - switch x.(type) { - case int: - print(2) - panic(3) - println() // ERROR "unreachable code" - default: - return 4 - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - switch x.(type) { - case int: - print(2) - panic(3) - default: - return 4 - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - switch x.(type) { - default: - return 4 - println() // ERROR "unreachable code" - case int: - print(2) - panic(3) - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - switch x.(type) { - default: - return 4 - case int: - print(2) - panic(3) - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - switch x.(type) { - case int: - print(2) - fallthrough - default: - return 4 - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - switch x.(type) { - case int: - print(2) - fallthrough - default: - return 4 - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - switch { - } - println() // ok -} - -var _ = func() int { - print(1) - switch x.(type) { - case int: - print(2) - panic(3) - case float64: - return 4 - } - println() // ok -} - -var _ = func() int { - print(1) - switch x.(type) { - case float64: - return 4 - case int: - print(2) - panic(3) - } - println() // ok -} - -var _ = func() int { - print(1) - switch x.(type) { - case int: - print(2) - fallthrough - case float64: - return 4 - } - println() // ok -} - -var _ = func() int { - print(1) - switch x.(type) { - case int: - print(2) - panic(3) - } - println() // ok -} - -var _ = func() int { - print(1) -L: - switch x.(type) { - case int: - print(2) - panic(3) - break L // ERROR "unreachable code" - default: - return 4 - } - println() // ok -} - -var _ = func() int { - print(1) - switch x.(type) { - default: - return 4 - break // ERROR "unreachable code" - case int: - print(2) - panic(3) - } - println() // ok -} - -var _ = func() int { - print(1) -L: - switch x.(type) { - case int: - print(2) - for { - break L - } - default: - return 4 - } - println() // ok -} - -// again, but without the leading print(1). -// testing that everything works when the terminating statement is first. - -var _ = func() int { - println() // ok -} - -var _ = func() int { - return 2 - println() // ERROR "unreachable code" -} - -var _ = func() int { -L: - goto L - println() // ERROR "unreachable code" -} - -var _ = func() int { - panic(2) - println() // ERROR "unreachable code" -} - -// but only builtin panic -var _ = func() int { - var panic = func(int) {} - panic(2) - println() // ok -} - -var _ = func() int { - { - return 2 - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - { - return 2 - } - println() // ERROR "unreachable code" -} - -var _ = func() int { -L: - { - goto L - println() // ERROR "unreachable code" - } -} - -var _ = func() int { -L: - { - goto L - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - { - panic(2) - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - { - panic(2) - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - return 2 - { // ERROR "unreachable code" - } - println() // ok -} - -var _ = func() int { -L: - goto L - { // ERROR "unreachable code" - } - println() // ok -} - -var _ = func() int { - panic(2) - { // ERROR "unreachable code" - } - println() // ok -} - -var _ = func() int { - { - return 2 - { // ERROR "unreachable code" - } - } - println() // ok -} - -var _ = func() int { -L: - { - goto L - { // ERROR "unreachable code" - } - } - println() // ok -} - -var _ = func() int { - { - panic(2) - { // ERROR "unreachable code" - } - } - println() // ok -} - -var _ = func() int { - { - return 2 - } - { // ERROR "unreachable code" - } - println() // ok -} - -var _ = func() int { -L: - { - goto L - } - { // ERROR "unreachable code" - } - println() // ok -} - -var _ = func() int { - { - panic(2) - } - { // ERROR "unreachable code" - } - println() // ok -} diff --git a/src/cmd/vet/test_method.go b/src/cmd/vet/test_method.go deleted file mode 100644 index 41de62bb1..000000000 --- a/src/cmd/vet/test_method.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains tests for the canonical method checker. - -// +build vet_test - -// This file contains the code to check canonical methods. - -package main - -import ( - "fmt" -) - -type MethodTest int - -func (t *MethodTest) Scan(x fmt.ScanState, c byte) { // ERROR "should have signature Scan" -} - -type MethodTestInterface interface { - ReadByte() byte // ERROR "should have signature ReadByte" -} diff --git a/src/cmd/vet/test_print.go b/src/cmd/vet/test_print.go deleted file mode 100644 index 8b41e6c69..000000000 --- a/src/cmd/vet/test_print.go +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build vet_test - -// This file contains tests for the printf checker. - -package main - -import ( - "fmt" - "unsafe" // just for test case printing unsafe.Pointer -) - -func UnsafePointerPrintfTest() { - var up unsafe.Pointer - fmt.Printf("%p, %x %X", up, up, up) -} - -// Error methods that do not satisfy the Error interface and should be checked. -type errorTest1 int - -func (errorTest1) Error(...interface{}) string { - return "hi" -} - -type errorTest2 int // Analogous to testing's *T type. -func (errorTest2) Error(...interface{}) { -} - -type errorTest3 int - -func (errorTest3) Error() { // No return value. -} - -type errorTest4 int - -func (errorTest4) Error() int { // Different return type. - return 3 -} - -type errorTest5 int - -func (errorTest5) error() { // niladic; don't complain if no args (was bug) -} - -// This function never executes, but it serves as a simple test for the program. -// Test with make test. -func PrintfTests() { - var b bool - var i int - var r rune - var s string - var x float64 - var p *int - // Some good format/argtypes - fmt.Printf("") - fmt.Printf("%b %b", 3, i) - fmt.Printf("%c %c %c %c", 3, i, 'x', r) - fmt.Printf("%d %d", 3, i) - fmt.Printf("%e %e", 3e9, x) - fmt.Printf("%E %E", 3e9, x) - fmt.Printf("%f %f", 3e9, x) - fmt.Printf("%F %F", 3e9, x) - fmt.Printf("%g %g", 3e9, x) - fmt.Printf("%G %G", 3e9, x) - fmt.Printf("%o %o", 3, i) - fmt.Printf("%p %p", p, nil) - fmt.Printf("%q %q %q %q", 3, i, 'x', r) - fmt.Printf("%s %s", "hi", s) - fmt.Printf("%t %t", true, b) - fmt.Printf("%T %T", 3, i) - fmt.Printf("%U %U", 3, i) - fmt.Printf("%v %v", 3, i) - fmt.Printf("%x %x %x %x", 3, i, "hi", s) - fmt.Printf("%X %X %X %X", 3, i, "hi", s) - fmt.Printf("%.*s %d %g", 3, "hi", 23, 2.3) - // Some bad format/argTypes - fmt.Printf("%b", "hi") // ERROR "arg .hi. for printf verb %b of wrong type" - fmt.Printf("%c", 2.3) // ERROR "arg 2.3 for printf verb %c of wrong type" - fmt.Printf("%d", 2.3) // ERROR "arg 2.3 for printf verb %d of wrong type" - fmt.Printf("%e", "hi") // ERROR "arg .hi. for printf verb %e of wrong type" - fmt.Printf("%E", true) // ERROR "arg true for printf verb %E of wrong type" - fmt.Printf("%f", "hi") // ERROR "arg .hi. for printf verb %f of wrong type" - fmt.Printf("%F", 'x') // ERROR "arg 'x' for printf verb %F of wrong type" - fmt.Printf("%g", "hi") // ERROR "arg .hi. for printf verb %g of wrong type" - fmt.Printf("%G", i) // ERROR "arg i for printf verb %G of wrong type" - fmt.Printf("%o", x) // ERROR "arg x for printf verb %o of wrong type" - fmt.Printf("%p", 23) // ERROR "arg 23 for printf verb %p of wrong type" - fmt.Printf("%q", x) // ERROR "arg x for printf verb %q of wrong type" - fmt.Printf("%s", b) // ERROR "arg b for printf verb %s of wrong type" - fmt.Printf("%t", 23) // ERROR "arg 23 for printf verb %t of wrong type" - fmt.Printf("%U", x) // ERROR "arg x for printf verb %U of wrong type" - fmt.Printf("%x", nil) // ERROR "arg nil for printf verb %x of wrong type" - fmt.Printf("%X", 2.3) // ERROR "arg 2.3 for printf verb %X of wrong type" - fmt.Printf("%.*s %d %g", 3, "hi", 23, 'x') // ERROR "arg 'x' for printf verb %g of wrong type" - // TODO - fmt.Println() // not an error - fmt.Println("%s", "hi") // ERROR "possible formatting directive in Println call" - fmt.Printf("%s", "hi", 3) // ERROR "wrong number of args for format in Printf call" - fmt.Printf("%"+("s"), "hi", 3) // ERROR "wrong number of args for format in Printf call" - fmt.Printf("%s%%%d", "hi", 3) // correct - fmt.Printf("%08s", "woo") // correct - fmt.Printf("% 8s", "woo") // correct - fmt.Printf("%.*d", 3, 3) // correct - fmt.Printf("%.*d", 3, 3, 3) // ERROR "wrong number of args for format in Printf call" - fmt.Printf("%.*d", "hi", 3) // ERROR "arg .hi. for \* in printf format not of type int" - fmt.Printf("%.*d", i, 3) // correct - fmt.Printf("%.*d", s, 3) // ERROR "arg s for \* in printf format not of type int" - fmt.Printf("%q %q", multi()...) // ok - fmt.Printf("%#q", `blah`) // ok - printf("now is the time", "buddy") // ERROR "no formatting directive" - Printf("now is the time", "buddy") // ERROR "no formatting directive" - Printf("hi") // ok - const format = "%s %s\n" - Printf(format, "hi", "there") - Printf(format, "hi") // ERROR "wrong number of args for format in Printf call" - f := new(File) - f.Warn(0, "%s", "hello", 3) // ERROR "possible formatting directive in Warn call" - f.Warnf(0, "%s", "hello", 3) // ERROR "wrong number of args for format in Warnf call" - f.Warnf(0, "%r", "hello") // ERROR "unrecognized printf verb" - f.Warnf(0, "%#s", "hello") // ERROR "unrecognized printf flag" - // Something that satisfies the error interface. - var e error - fmt.Println(e.Error()) // ok - // Something that looks like an error interface but isn't, such as the (*T).Error method - // in the testing package. - var et1 errorTest1 - fmt.Println(et1.Error()) // ERROR "no args in Error call" - fmt.Println(et1.Error("hi")) // ok - fmt.Println(et1.Error("%d", 3)) // ERROR "possible formatting directive in Error call" - var et2 errorTest2 - et2.Error() // ERROR "no args in Error call" - et2.Error("hi") // ok, not an error method. - et2.Error("%d", 3) // ERROR "possible formatting directive in Error call" - var et3 errorTest3 - et3.Error() // ok, not an error method. - var et4 errorTest4 - et4.Error() // ok, not an error method. - var et5 errorTest5 - et5.error() // ok, not an error method. -} - -// printf is used by the test. -func printf(format string, args ...interface{}) { - panic("don't call - testing only") -} - -// multi is used by the test. -func multi() []interface{} { - panic("don't call - testing only") -} diff --git a/src/cmd/vet/test_rangeloop.go b/src/cmd/vet/test_rangeloop.go deleted file mode 100644 index 941fd72aa..000000000 --- a/src/cmd/vet/test_rangeloop.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains tests for the rangeloop checker. - -// +build vet_test - -package main - -func RangeLoopTests() { - var s []int - for i, v := range s { - go func() { - println(i) // ERROR "range variable i enclosed by function" - println(v) // ERROR "range variable v enclosed by function" - }() - } - for i, v := range s { - defer func() { - println(i) // ERROR "range variable i enclosed by function" - println(v) // ERROR "range variable v enclosed by function" - }() - } - for i := range s { - go func() { - println(i) // ERROR "range variable i enclosed by function" - }() - } - for _, v := range s { - go func() { - println(v) // ERROR "range variable v enclosed by function" - }() - } - for i, v := range s { - go func() { - println(i, v) - }() - println("unfortunately, we don't catch the error above because of this statement") - } - for i, v := range s { - go func(i, v int) { - println(i, v) - }(i, v) - } - for i, v := range s { - i, v := i, v - go func() { - println(i, v) - }() - } - // If the key of the range statement is not an identifier - // the code should not panic (it used to). - var x [2]int - var f int - for x[0], f = range s { - go func() { - _ = f // ERROR "range variable f enclosed by function" - }() - } -} diff --git a/src/cmd/vet/test_structtag.go b/src/cmd/vet/test_structtag.go deleted file mode 100644 index 08cf737fd..000000000 --- a/src/cmd/vet/test_structtag.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains tests for the structtag checker. - -// +build vet_test - -// This file contains the test for canonical struct tags. - -package main - -type StructTagTest struct { - X int "hello" // ERROR "not compatible with reflect.StructTag.Get" -} diff --git a/src/cmd/vet/test_taglit.go b/src/cmd/vet/test_taglit.go deleted file mode 100644 index f34062f18..000000000 --- a/src/cmd/vet/test_taglit.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains tests for the untagged struct literal checker. - -// +build vet_test - -// This file contains the test for untagged struct literals. - -package main - -import ( - "flag" - "go/scanner" -) - -var Okay1 = []string{ - "Name", - "Usage", - "DefValue", -} - -var Okay2 = map[string]bool{ - "Name": true, - "Usage": true, - "DefValue": true, -} - -var Okay3 = struct { - X string - Y string - Z string -}{ - "Name", - "Usage", - "DefValue", -} - -type MyStruct struct { - X string - Y string - Z string -} - -var Okay4 = MyStruct{ - "Name", - "Usage", - "DefValue", -} - -// Testing is awkward because we need to reference things from a separate package -// to trigger the warnings. - -var BadStructLiteralUsedInTests = flag.Flag{ // ERROR "untagged fields" - "Name", - "Usage", - nil, // Value - "DefValue", -} - -// Used to test the check for slices and arrays: If that test is disabled and -// vet is run with --compositewhitelist=false, this line triggers an error. -// Clumsy but sufficient. -var scannerErrorListTest = scanner.ErrorList{nil, nil} diff --git a/src/cmd/vet/types.go b/src/cmd/vet/types.go deleted file mode 100644 index 75f195b0f..000000000 --- a/src/cmd/vet/types.go +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build gotypes - -// This file contains the pieces of the tool that require the go/types package. -// To compile this file, you must first run -// $ go get code.google.com/p/go.exp/go/types - -package main - -import ( - "go/ast" - "go/token" - - "code.google.com/p/go.exp/go/types" -) - -// Type is equivalent to go/types.Type. Repeating it here allows us to avoid -// depending on the go/types package. -type Type interface { - String() string -} - -func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error { - pkg.types = make(map[ast.Expr]Type) - pkg.values = make(map[ast.Expr]interface{}) - exprFn := func(x ast.Expr, typ types.Type, val interface{}) { - pkg.types[x] = typ - if val != nil { - pkg.values[x] = val - } - } - // By providing the Context with our own error function, it will continue - // past the first error. There is no need for that function to do anything. - context := types.Context{ - Expr: exprFn, - Error: func(error) {}, - } - _, err := context.Check(fs, astFiles) - return err -} - -// isStruct reports whether the composite literal c is a struct. -// If it is not (probably a struct), it returns a printable form of the type. -func (pkg *Package) isStruct(c *ast.CompositeLit) (bool, string) { - // Check that the CompositeLit's type is a slice or array (which needs no tag), if possible. - typ := pkg.types[c] - // If it's a named type, pull out the underlying type. - actual := typ - if namedType, ok := typ.(*types.NamedType); ok { - actual = namedType.Underlying - } - if actual == nil { - // No type information available. Assume true, so we do the check. - return true, "" - } - switch actual.(type) { - case *types.Struct: - return true, typ.String() - default: - return false, "" - } -} - -func (f *File) matchArgType(t printfArgType, arg ast.Expr) bool { - // TODO: for now, we can only test builtin types and untyped constants. - typ := f.pkg.types[arg] - if typ == nil { - return true - } - basic, ok := typ.(*types.Basic) - if !ok { - return true - } - switch basic.Kind { - case types.Bool: - return t&argBool != 0 - case types.Int, types.Int8, types.Int16, types.Int32, types.Int64: - fallthrough - case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.Uintptr: - return t&argInt != 0 - case types.Float32, types.Float64, types.Complex64, types.Complex128: - return t&argFloat != 0 - case types.String: - return t&argString != 0 - case types.UnsafePointer: - return t&(argPointer|argInt) != 0 - case types.UntypedBool: - return t&argBool != 0 - case types.UntypedComplex: - return t&argFloat != 0 - case types.UntypedFloat: - // If it's integral, we can use an int format. - switch f.pkg.values[arg].(type) { - case int, int8, int16, int32, int64: - return t&(argInt|argFloat) != 0 - case uint, uint8, uint16, uint32, uint64: - return t&(argInt|argFloat) != 0 - } - return t&argFloat != 0 - case types.UntypedInt: - return t&argInt != 0 - case types.UntypedRune: - return t&(argInt|argRune) != 0 - case types.UntypedString: - return t&argString != 0 - case types.UntypedNil: - return t&argPointer != 0 // TODO? - case types.Invalid: - if *verbose { - f.Warnf(arg.Pos(), "printf argument %v has invalid or unknown type", arg) - } - return true // Probably a type check problem. - } - return false -} - -// numArgsInSignature tells how many formal arguments the function type -// being called has. -func (f *File) numArgsInSignature(call *ast.CallExpr) int { - // Check the type of the function or method declaration - typ := f.pkg.types[call.Fun] - if typ == nil { - return 0 - } - // The type must be a signature, but be sure for safety. - sig, ok := typ.(*types.Signature) - if !ok { - return 0 - } - return len(sig.Params) -} - -// isErrorMethodCall reports whether the call is of a method with signature -// func Error() string -// where "string" is the universe's string type. We know the method is called "Error". -func (f *File) isErrorMethodCall(call *ast.CallExpr) bool { - // Is it a selector expression? Otherwise it's a function call, not a method call. - sel, ok := call.Fun.(*ast.SelectorExpr) - if !ok { - return false - } - // The package is type-checked, so if there are no arguments, we're done. - if len(call.Args) > 0 { - return false - } - // Check the type of the method declaration - typ := f.pkg.types[sel] - if typ == nil { - return false - } - // The type must be a signature, but be sure for safety. - sig, ok := typ.(*types.Signature) - if !ok { - return false - } - // There must be a receiver for it to be a method call. Otherwise it is - // a function, not something that satisfies the error interface. - if sig.Recv == nil { - return false - } - // There must be no arguments. Already verified by type checking, but be thorough. - if len(sig.Params) > 0 { - return false - } - // Finally the real questions. - // There must be one result. - if len(sig.Results) != 1 { - return false - } - // It must have return type "string" from the universe. - result := sig.Results[0].Type - if types.IsIdentical(result, types.Typ[types.String]) { - return true - } - return false -} diff --git a/src/cmd/vet/typestub.go b/src/cmd/vet/typestub.go deleted file mode 100644 index fabbbe19d..000000000 --- a/src/cmd/vet/typestub.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !gotypes - -// This file contains stubs for the pieces of the tool that require the go/types package, -// to be used if go/types is not available. - -package main - -import ( - "go/ast" - "go/token" -) - -// Type is equivalent to go/types.Type. Repeating it here allows us to avoid -// depending on the go/types package. -type Type interface { - String() string -} - -func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error { - return nil -} - -func (pkg *Package) isStruct(c *ast.CompositeLit) (bool, string) { - return true, "" // Assume true, so we do the check. -} - -func (f *File) matchArgType(t printfArgType, arg ast.Expr) bool { - return true // We can't tell without types. -} - -func (f *File) numArgsInSignature(call *ast.CallExpr) int { - return 0 // We don't know. -} - -func (f *File) isErrorMethodCall(call *ast.CallExpr) bool { - // Is it a selector expression? Otherwise it's a function call, not a method call. - if _, ok := call.Fun.(*ast.SelectorExpr); !ok { - return false - } - return true // Best guess we can make without types. -} diff --git a/src/cmd/yacc/Makefile b/src/cmd/yacc/Makefile index 56e954289..480844805 100644 --- a/src/cmd/yacc/Makefile +++ b/src/cmd/yacc/Makefile @@ -2,9 +2,9 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -units: yacc.go units.y - go run yacc.go -p units_ units.y - go build -o units y.go +expr: yacc.go expr.y + go run yacc.go -p expr expr.y + go build -o expr y.go clean: - rm -f y.go y.output units + rm -f y.go y.output expr diff --git a/src/cmd/yacc/doc.go b/src/cmd/yacc/doc.go index 792c104e3..ceaaf2448 100644 --- a/src/cmd/yacc/doc.go +++ b/src/cmd/yacc/doc.go @@ -20,10 +20,8 @@ written in C and documented at Adepts of the original yacc will have no trouble adapting to this form of the tool. -The file units.y in this directory is a yacc grammar for a version of -the Unix tool units, also written in Go and largely transliterated -from the Plan 9 C version. It needs the flag "-p units_" (see -below). +The file expr.y in this directory is a yacc grammar for a very simple +expression parser. It needs the flag "-p expr" (see below). The generated parser is reentrant. Parse expects to be given an argument that conforms to the following interface: diff --git a/src/cmd/yacc/expr.y b/src/cmd/yacc/expr.y new file mode 100644 index 000000000..3afffe7ee --- /dev/null +++ b/src/cmd/yacc/expr.y @@ -0,0 +1,205 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This is an example of a goyacc program. +// To build it: +// go tool yacc -p "expr" expr.y (produces y.go) +// go build -o expr y.go +// expr +// > <type an expression> + +%{ + +// This tag will be copied to the generated file to prevent that file +// confusing a future build. + +// +build ignore + +package main + +import ( + "bufio" + "bytes" + "fmt" + "io" + "log" + "math/big" + "os" + "unicode/utf8" +) + +%} + +%union { + num *big.Rat +} + +%type <num> expr expr1 expr2 expr3 + +%token <num> NUM + +%% + +top: + expr + { + if $1.IsInt() { + fmt.Println($1.Num().String()) + } else { + fmt.Println($1.String()) + } + } + +expr: + expr1 +| '+' expr + { + $$ = $2 + } +| '-' expr + { + $$.Neg($2) + } + +expr1: + expr2 +| expr1 '+' expr2 + { + $$.Add($1, $3) + } +| expr1 '-' expr2 + { + $$.Sub($1, $3) + } + +expr2: + expr3 +| expr2 '*' expr3 + { + $$.Mul($1, $3) + } +| expr2 '/' expr3 + { + $$.Quo($1, $3) + } + +expr3: + NUM +| '(' expr ')' + { + $$ = $2 + } + + +%% + +// The parser expects the lexer to return 0 on EOF. Give it a name +// for clarity. +const eof = 0 + +// The parser uses the type <prefix>Lex as a lexer. It must provide +// the methods Lex(*<prefix>SymType) int and Error(string). +type exprLex struct { + line []byte + peek rune +} + +// The parser calls this method to get each new token. This +// implementation returns operators and NUM. +func (x *exprLex) Lex(yylval *exprSymType) int { + for { + c := x.next() + switch c { + case eof: + return eof + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + return x.num(c, yylval) + case '+', '-', '*', '/', '(', ')': + return int(c) + + // Recognize Unicode multiplication and division + // symbols, returning what the parser expects. + case '×': + return '*' + case '÷': + return '/' + + case ' ', '\t', '\n': + default: + log.Printf("unrecognized character %q", c) + } + } +} + +// Lex a number. +func (x *exprLex) num(c rune, yylval *exprSymType) int { + add := func(b *bytes.Buffer, c rune) { + if _, err := b.WriteRune(c); err != nil { + log.Fatalf("WriteRune: %s", err) + } + } + var b bytes.Buffer + add(&b, c) + L: for { + c = x.next() + switch c { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', 'e', 'E': + add(&b, c) + default: + break L + } + } + if c != eof { + x.peek = c + } + yylval.num = &big.Rat{} + _, ok := yylval.num.SetString(b.String()) + if !ok { + log.Printf("bad number %q", b.String()) + return eof + } + return NUM +} + +// Return the next rune for the lexer. +func (x *exprLex) next() rune { + if x.peek != eof { + r := x.peek + x.peek = eof + return r + } + if len(x.line) == 0 { + return eof + } + c, size := utf8.DecodeRune(x.line) + x.line = x.line[size:] + if c == utf8.RuneError && size == 1 { + log.Print("invalid utf8") + return x.next() + } + return c +} + +// The parser calls this method on a parse error. +func (x *exprLex) Error(s string) { + log.Printf("parse error: %s", s) +} + +func main() { + in := bufio.NewReader(os.Stdin) + for { + if _, err := os.Stdout.WriteString("> "); err != nil { + log.Fatalf("WriteString: %s", err) + } + line, err := in.ReadBytes('\n') + if err == io.EOF { + return + } + if err != nil { + log.Fatalf("ReadBytes: %s", err) + } + + exprParse(&exprLex{line: line}) + } +} diff --git a/src/cmd/yacc/units.txt b/src/cmd/yacc/units.txt deleted file mode 100644 index df8f567d9..000000000 --- a/src/cmd/yacc/units.txt +++ /dev/null @@ -1,576 +0,0 @@ -/ Plan 9's /lib/units -/ http://plan9.bell-labs.com/sources/plan9/lib/units -/ -/ Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights Reserved. -/ Distributed under the terms of the Lucent Public License Version 1.02 -/ See http://plan9.bell-labs.com/plan9/license.html -/ -/order of evaluation -/ + - -/ * / -/ juxtaposition (meaning *) -/ ¹ ² ³ ^ -/ | (meaning /) -/ name number () - -/dimensions -m # -kg # -sec # -coul # -candela # -$ # -radian # -bit # -erlang # -°K # -°C # -°F # - -/constants - -π 3.14159265358979323846 -pi π -c 2.997925e+8 m/sec -g 9.80665 m/sec² -au 1.49597871e+11 m -mole 6.022169e+23 -e 1.6021917e-19 coul -energy c² -force g -mercury 1.33322e+5 kg/m²sec² -hg mercury -h 6.62620e-34 m²kg/sec -ℏ h/2 π -hbar ℏ -nonillion 1e30 -octillion 1e27 -septillion 1e24 -sextillion 1e21 -pentillion 1e18 -quadrillion 1e15 -trillion 1e12 -billion 1e9 -million 1e6 -thousand 1e3 -hundred 1e2 - -/dimensionless - -° 1|180 π radian -degree ° -circle 2 π radian -turn 2 π radian -grad .9 ° -arcdeg 1 ° -arcmin 1|60 ° -arcsec 1|3600 ° -ccs 1|36 erlang - -steradian radian² -sphere 4 π steradian -sr steradian -giga 1024 1024 1024 - -/Time - -second sec -s sec -minute 60 sec -min minute -hour 60 min -hr hour -day 24 hr -da day -week 7 day -year 365.24219879 day -yr year -month 1|12 year -ms millisec -us microsec - -/Mass - -gram millikg -gm gram -mg milligram -metricton kilokg - -/Avoirdupois - -lb .45359237 kg -lbf lb g -pound lb -ounce 1|16 lb -oz ounce -dram 1|16 oz -dr dram -grain 1|7000 lb -gr grain -shortton 2000 lb -ton shortton -longton 2240 lb - -/Apothecary - -scruple 20 grain -apdram 60 grain -apounce 480 grain -troyounce apounce -appound 5760 grain -troypound appound - -/Length - -meter m -cm centimeter -mm millimeter -km kilometer -nm nanometer -micron micrometer -µ micrometer -Å decinanometer -angstrom Å - -inch 2.54 cm -" inch -in inch -inches inch -' 12" -foot 12 in -feet foot -ft foot -yard 3 ft -yd yard -rod 5.5 yd -rd rod -mile 5280 ft -mi mile - -british 1200|3937 m/ft -nmile 1852 m - -acre 4840 yd² - -cc cm³ -liter kilocc -ml milliliter - -/US Liquid - -gallon 231 in³ -imperial 1.20095 -epa 0.8 -gal gallon -quart 1|4 gal -qt quart -pint 1|2 qt -pt pint - -floz 1|16 pt -fldr 1|8 floz - -/US Dry - -dry 268.8025 in³/gallon -peck 8 dry quart -pk peck -bushel 4 peck -bu bushel - -/British - -brgallon 277.420 in³ -brquart 1|4 brgallon -brpint 1|2 brquart -brfloz 1|20 brpint -brpeck 554.84 in³ -brbushel 4 brpeck - -/Energy Work - -newton kg m/sec² -nt newton -joule nt m -cal 4.1868 joule - -/Electrical - -coulomb coul -ampere coul/sec -amp ampere -watt joule/sec -volt watt/amp -Ω volt/amp -ohm Ω -mho 1/Ω -farad coul/volt -henry sec²/farad -weber volt sec - -/Light - -cd candela -lumen cd sr -lux cd sr/m² - -/ MONEY DATE -/ Wed Aug 29, 2012 - -argentinapeso $ 0.2160 -australiadollar $ 1.0372 -boliviaboliviano $ 0.1427 -brazilreal $ 0.4872 -britainpound $ 1.5843 -canadadollar $ 1.0117 -chilepeso $ 1 | 480.6 -chinayuan $ 0.1574 -colombiapeso $ 1 | 1834 -czechkoruna $ 0.0506 -denmarkkrone $ 0.1681 -dominicanpeso $ 0.0256 -egyptpound $ 0.1640 -elsalvadorcolon $ 1 | 8.75 -europeuro $ 1.2528 -guatemalaquetzal $ 0.1290 -honduraslempira $ 0.0511 -hongkongdollar $ 0.1289 -hungaryforint $ 1 | 226.5 -indiarupee $ 0.0180 -indonesiarupiah $ 1 | 9540 -israelshekel $ 0.2479 -japanyen $ 0.0127 -kenyashilling $ 0.0119 -kuwaitdinar $ 3.5456 -lebanonpound $ 1 | 1505.5 -malaysiaringgit $ 0.3204 -mexicopeso $ 0.0754 -newzealanddollar $ 0.8035 -nicaraguacordoba $ 0.0421 -norwaykrone $ 0.1717 -pakistanrupee $ 0.0106 -paraguayguarani $ 1 | 4415 -perunewsol $ 0.3832 -philippinespeso $ 0.0236 -polandzloty $ 0.3001 -russiaruble $ 0.0311 -saudiarabiariyal $ 1 | 3.75 -singaporedollar $ 0.7976 -slovakkoruna 1 | 30.126 europeuro -southafricarand $ 0.1188 -southkoreawon $ 1 | 1135 -swedenkrona $ 0.1502 -switzerlandfranc $ 1.0431 -taiwandollar $ 0.0334 -thailandbaht $ 0.0319 -turkeynewlira $ 0.5504 -uaedirham $ 0.2723 -uruguaynewpeso $ 0.0465 -vietnamdong $ 1 | 20865 - -/ END MONEY - -€ europeuro -£ britainpound -¥ japanyen -dollar $ - -baht thailandbaht -brpound britainpound -dirham uaedirham -euro europeuro -forint hungaryforint -krona swedenkrona -peso mexicopeso -rand southafricarand -real brazilreal -yuan chinayuan -ringgit malaysiaringgit -riyal saudiarabiariyal -ruble russiaruble -rupee indiarupee -rupiah indonesiarupiah -shekel israelshekel -sol perunewsol -won southkoreawon -yen japanyen -zloty polandzloty - -usdollar dollar -sterling britainpound | pound -poundsterling britainpound - -/bits - -baud bit/sec -byte 8 bit -short 2 byte -long 4 byte -vlong 8 bytes -frame 2352 byte - -/Australian liquid measure - -pony 7 brfloz -midie 10 brfloz -pot midie -handle midie -schooner 15 brfloz -jug 40 brfloz -resch midie -alf midie -tinny 13 brfloz -stubby tinny -twisty 250 ml -longneck 2 tinny -slab 24 tinny -sixpack 6 tinny -nip brfloz - -/wine -winebottle 750 ml -balthazar 16 winebottle -jeroboam 4 winebottle -magnum 2 winebottle -mathusalem 8 winebottle -methuselah 8 winebottle -nebuchadnezzar 20 winebottle -rehoboam 6 winebottle -salmanazar 12 winebottle -split 0.25 winebottle -jigger 1.5 floz - -/Trivia - -% 1|100 -admiraltyknot 6080 ft/hr -ε₀ (1e-9/36π) farad/m -α (1/4π ε₀) e²/ℏ c -alpha α -apostilb cd/π m² -are 1e+2 m² -arpentcan 27.52 mi -arpentlin 191.835 ft -astronomicalunit au -atmosphere 1.01325e+5 nt/m² -atm atmosphere -atomicmassunit 1.66044e-27 kg -amu atomicmassunit -bag 94 lb -bakersdozen 13 -bar 1e+5 nt/m² -barie 1e-1 nt/m² -barleycorn 1|3 in -barn 1e-28 m² -barrel 42 gal -barye 1e-1 nt/m² -bev 1e+9 e volt -biot 10 amp -blondel cd/π m² -boardfoot 144 in³ -bolt 40 yd -bottommeasure 1|40 in -britishthermalunit 1.05506e+3 joule -btu britishthermalunit -quad 1.0e+15 btu -refrigeration 12000 btu/ton hour -buck dollar -cable 720 ft -caliber 1e-2 in -calorie cal -carat 205 mg -cent centidollar -cental 100 lb -centesimalminute 1e-2 grad -centesimalsecond 1e-4 grad -century 100 year -cfs ft³/sec -chain 66 ft -circularinch 1|4 π in² -circularmil 1e-6|4 π in² -clusec 1e-8 mm hg m³/s -coomb 4 bu -cord 128 ft³ -cordfoot cord -crith 9.06e-2 gm -cubit 18 in -cup 1|2 pt -curie 3.7e+10/sec -cusec ft³/sec -dalton amu -decade 10 yr -degK °K -degC °C -degF °F -dipotre 1/m -displacementton 35 ft³ -doppelzentner 100 kg -dozen 12 -drop .03 cm³ -dyne cm gm/sec² -electronvolt e volt -ell 45 in -engineerschain 100 ft -engineerslink 100|100 ft -equivalentfootcandle lumen/π ft² -equivalentlux lumen/π m² -equivalentphot cd/π cm² -erg cm²gm/sec² -ev e volt -faraday 9.652e+4 coul -fathom 6 ft -fermi 1e-15 m -fifth 4|5 qt -fin 5 dollar -finger 7|8 in -firkin 9 gal -footcandle lumen/ft² -footlambert cd/π ft² -fortnight 14 da -franklin 3.33564e-10 coul -frigorie kilocal -furlong 220 yd -galileo 1e-2 m/sec² -gamma 1e-9 weber/m² -gauss 1e-4 weber/m² -geodeticfoot british ft -geographicalmile 1852 m -gilbert 7.95775e-1 amp -gill 1|4 pt -gross 144 -gunterschain 22 yd -hand 4 in -hectare 1e+4 m² -hefnercandle .92 cd -hertz 1/sec -hogshead 2 barrel -hd hogshead -homestead 1|4 mi² -horsepower 550 ft lb g/sec -hp horsepower -hyl gm force sec²/m -hz 1/sec -imaginarycubicfoot 1.4 ft³ -karat 1|24 -kcal kilocal -kcalorie kilocal -kev 1e+3 e volt -key kg -khz 1e+3/sec -kilderkin 18 gal -knot nmile/hr -kwh kilowatt hour -lambert cd/π cm² -langley cal/cm² -last 80 bu -league 3 mi -lightyear c yr -ly lightyear -lightsecond c sec -line 1|12 in -link 66|100 ft -longhundredweight 112 lb -longquarter 28 lb -lusec 1e-6 mm hg m³/s -mach 331.46 m/sec -marineleague 3 nmile -maxwell 1e-8 weber -metriccarat 200 mg -mev 1e+6 e volt -mgd megagal/day -mh millihenry -mhz 1e+6/sec -mil 1e-3 in -millenium 1000 year -minersinch 1.5 ft³/min -minim 1|60 fldr -mo month -mpg mile/gal -mph mile/hr -nail 1|16 yd -nauticalmile nmile -nit cd/m² -noggin 1|8 qt -nox 1e-3 lux -ns nanosec -oersted 2.5e+2 amp/m π -oe oersted -pace 36 in -palm 3 in -parasang 3.5 mi -parsec au radian/arcsec -pascal nt/m² -pc parsec -pennyweight 1|20 oz -percent % -perch rd -pf picofarad -phot lumen/cm² -pica 1|6 in -pieze 1e+3 nt/m² -pipe 4 barrel -point 1|72 in -poise gm/cm sec -pole rd -poundal ft lb/sec² -pdl poundal -proof 1/200 -psi lb g/in² -quarter 9 in -quartersection 1|4 mi² -quintal 100 kg -quire 25 -rad 100 erg/gm -ream 500 -registerton 100 ft³ -rhe 10 m²/nt sec -rontgen 2.58e-4 curie/kg -rood 1.21e+3 yd -rope 20 ft -rutherford 1e+6/sec -rydberg 1.36054e+1 ev -sabin 1 ft² -sack 3 bu -seam 8 bu -section mi² -shippington 40 ft³ -shorthundredweight 100 lb -shortquarter 25 lb -siemens 1/Ω -σ 5.66956e-5 erg/cm² °K^4 sec -sigma σ -skein 120 yd -skot 1e-3 apostilb -slug lb g sec²/ft -span 9 in -spat 4 π sr -spindle 14400 yd -square 100 ft² -squidge 1|972 inch -catsquidge 1|432 inch -stere m³ -sthene 1e+3 nt -stilb cd/cm² -stoke 1e-4 m²/sec -stone 14 lb -strike 2 bu -surveyfoot british ft -surveyorschain 66 ft -surveyorslink 66|100 ft -tablespoon 4 fldr -teaspoon 4|3 fldr -tesla weber/m² -therm 1e+5 btu -thermie 1e+6 cal -timberfoot ft³ -tnt 4.6e+6 m²/sec² -tonne 1e+6 gm -torr mm hg -township 36 mi² -tun 8 barrel -water .22491|2.54 kg/m²sec² -wey 40 bu -weymass 252 lb -Xunit 1.00202e-13 m -k 1.38047e-16 erg/°K -foal 9223372036854775807 diff --git a/src/cmd/yacc/units.y b/src/cmd/yacc/units.y deleted file mode 100644 index 9c1b0b336..000000000 --- a/src/cmd/yacc/units.y +++ /dev/null @@ -1,768 +0,0 @@ -// Derived from Plan 9's /sys/src/cmd/units.y -// http://plan9.bell-labs.com/sources/plan9/sys/src/cmd/units.y -// -// Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights Reserved. -// Portions Copyright 2009 The Go Authors. All Rights Reserved. -// Distributed under the terms of the Lucent Public License Version 1.02 -// See http://plan9.bell-labs.com/plan9/license.html - -// Generate parser with prefix "units_": -// go tool yacc -p "units_" - -%{ - -// This tag will end up in the generated y.go, so that forgetting -// 'make clean' does not fail the next build. - -// +build ignore - -// units.y -// example of a Go yacc program -// usage is -// go tool yacc -p "units_" units.y (produces y.go) -// go build -o units y.go -// ./units $GOROOT/src/cmd/yacc/units.txt -// you have: c -// you want: furlongs/fortnight -// * 1.8026178e+12 -// / 5.5474878e-13 -// you have: - -package main - -import ( - "bufio" - "flag" - "fmt" - "math" - "runtime" - "os" - "path/filepath" - "strconv" - "unicode/utf8" -) - -const ( - Ndim = 15 // number of dimensions - Maxe = 695 // log of largest number -) - -type Node struct { - vval float64 - dim [Ndim]int8 -} - -type Var struct { - name string - node Node -} - -var fi *bufio.Reader // input -var fund [Ndim]*Var // names of fundamental units -var line string // current input line -var lineno int // current input line number -var linep int // index to next rune in unput -var nerrors int // error count -var one Node // constant one -var peekrune rune // backup runt from input -var retnode1 Node -var retnode2 Node -var retnode Node -var sym string -var vflag bool -%} - -%union { - node Node - vvar *Var - numb int - vval float64 -} - -%type <node> prog expr expr0 expr1 expr2 expr3 expr4 - -%token <vval> VÄL // dieresis to test UTF-8 -%token <vvar> VAR -%token <numb> _SUP // tests leading underscore in token name -%% -prog: - ':' VAR expr - { - var f int - f = int($2.node.dim[0]) - $2.node = $3 - $2.node.dim[0] = 1 - if f != 0 { - Errorf("redefinition of %v", $2.name) - } else if vflag { - fmt.Printf("%v\t%v\n", $2.name, &$2.node) - } - } -| ':' VAR '#' - { - var f, i int - for i = 1; i < Ndim; i++ { - if fund[i] == nil { - break - } - } - if i >= Ndim { - Error("too many dimensions") - i = Ndim - 1 - } - fund[i] = $2 - f = int($2.node.dim[0]) - $2.node = one - $2.node.dim[0] = 1 - $2.node.dim[i] = 1 - if f != 0 { - Errorf("redefinition of %v", $2.name) - } else if vflag { - fmt.Printf("%v\t#\n", $2.name) - } - } -| ':' - { - } -| '?' expr - { - retnode1 = $2 - } -| '?' - { - retnode1 = one - } - -expr: - expr4 -| expr '+' expr4 - { - add(&$$, &$1, &$3) - } -| expr '-' expr4 - { - sub(&$$, &$1, &$3) - } - -expr4: - expr3 -| expr4 '*' expr3 - { - mul(&$$, &$1, &$3) - } -| expr4 '/' expr3 - { - div(&$$, &$1, &$3) - } - -expr3: - expr2 -| expr3 expr2 - { - mul(&$$, &$1, &$2) - } - -expr2: - expr1 -| expr2 _SUP - { - xpn(&$$, &$1, $2) - } -| expr2 '^' expr1 - { - var i int - for i = 1; i < Ndim; i++ { - if $3.dim[i] != 0 { - Error("exponent has units") - $$ = $1 - break - } - } - if i >= Ndim { - i = int($3.vval) - if float64(i) != $3.vval { - Error("exponent not integral") - } - xpn(&$$, &$1, i) - } - } - -expr1: - expr0 -| expr1 '|' expr0 - { - div(&$$, &$1, &$3) - } - -expr0: - VAR - { - if $1.node.dim[0] == 0 { - Errorf("undefined %v", $1.name) - $$ = one - } else { - $$ = $1.node - } - } -| VÄL - { - $$ = one - $$.vval = $1 - } -| '(' expr ')' - { - $$ = $2 - } -%% - -type UnitsLex int - -func (UnitsLex) Lex(yylval *units_SymType) int { - var c rune - var i int - - c = peekrune - peekrune = ' ' - -loop: - if (c >= '0' && c <= '9') || c == '.' { - goto numb - } - if ralpha(c) { - goto alpha - } - switch c { - case ' ', '\t': - c = getrune() - goto loop - case '×': - return '*' - case '÷': - return '/' - case '¹', 'ⁱ': - yylval.numb = 1 - return _SUP - case '²', '': - yylval.numb = 2 - return _SUP - case '³', '': - yylval.numb = 3 - return _SUP - } - return int(c) - -alpha: - sym = "" - for i = 0; ; i++ { - sym += string(c) - c = getrune() - if !ralpha(c) { - break - } - } - peekrune = c - yylval.vvar = lookup(0) - return VAR - -numb: - sym = "" - for i = 0; ; i++ { - sym += string(c) - c = getrune() - if !rdigit(c) { - break - } - } - peekrune = c - f, err := strconv.ParseFloat(sym, 64) - if err != nil { - fmt.Printf("error converting %v\n", sym) - f = 0 - } - yylval.vval = f - return VÄL -} - -func (UnitsLex) Error(s string) { - Errorf("syntax error, last name: %v", sym) -} - -func main() { - var file string - - flag.BoolVar(&vflag, "v", false, "verbose") - - flag.Parse() - - file = filepath.Join(runtime.GOROOT(), "src/cmd/yacc/units.txt") - if flag.NArg() > 0 { - file = flag.Arg(0) - } else if file == "" { - fmt.Fprintf(os.Stderr, "cannot find data file units.txt; provide it as argument or set $GOROOT\n") - os.Exit(1) - } - - f, err := os.Open(file) - if err != nil { - fmt.Fprintf(os.Stderr, "error opening %v: %v\n", file, err) - os.Exit(1) - } - fi = bufio.NewReader(f) - - one.vval = 1 - - /* - * read the 'units' file to - * develop a database - */ - lineno = 0 - for { - lineno++ - if readline() { - break - } - if len(line) == 0 || line[0] == '/' { - continue - } - peekrune = ':' - units_Parse(UnitsLex(0)) - } - - /* - * read the console to - * print ratio of pairs - */ - fi = bufio.NewReader(os.NewFile(0, "stdin")) - - lineno = 0 - for { - if (lineno & 1) != 0 { - fmt.Printf("you want: ") - } else { - fmt.Printf("you have: ") - } - if readline() { - break - } - peekrune = '?' - nerrors = 0 - units_Parse(UnitsLex(0)) - if nerrors != 0 { - continue - } - if (lineno & 1) != 0 { - if specialcase(&retnode, &retnode2, &retnode1) { - fmt.Printf("\tis %v\n", &retnode) - } else { - div(&retnode, &retnode2, &retnode1) - fmt.Printf("\t* %v\n", &retnode) - div(&retnode, &retnode1, &retnode2) - fmt.Printf("\t/ %v\n", &retnode) - } - } else { - retnode2 = retnode1 - } - lineno++ - } - fmt.Printf("\n") - os.Exit(0) -} - -/* - * all characters that have some - * meaning. rest are usable as names - */ -func ralpha(c rune) bool { - switch c { - case 0, '+', '-', '*', '/', '[', ']', '(', ')', - '^', ':', '?', ' ', '\t', '.', '|', '#', - '×', '÷', '¹', 'ⁱ', '²', '', '³', '': - return false - } - return true -} - -/* - * number forming character - */ -func rdigit(c rune) bool { - switch c { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '.', 'e', '+', '-': - return true - } - return false -} - -func Errorf(s string, v ...interface{}) { - fmt.Printf("%v: %v\n\t", lineno, line) - fmt.Printf(s, v...) - fmt.Printf("\n") - - nerrors++ - if nerrors > 5 { - fmt.Printf("too many errors\n") - os.Exit(1) - } -} - -func Error(s string) { - Errorf("%s", s) -} - -func add(c, a, b *Node) { - var i int - var d int8 - - for i = 0; i < Ndim; i++ { - d = a.dim[i] - c.dim[i] = d - if d != b.dim[i] { - Error("add must be like units") - } - } - c.vval = fadd(a.vval, b.vval) -} - -func sub(c, a, b *Node) { - var i int - var d int8 - - for i = 0; i < Ndim; i++ { - d = a.dim[i] - c.dim[i] = d - if d != b.dim[i] { - Error("sub must be like units") - } - } - c.vval = fadd(a.vval, -b.vval) -} - -func mul(c, a, b *Node) { - var i int - - for i = 0; i < Ndim; i++ { - c.dim[i] = a.dim[i] + b.dim[i] - } - c.vval = fmul(a.vval, b.vval) -} - -func div(c, a, b *Node) { - var i int - - for i = 0; i < Ndim; i++ { - c.dim[i] = a.dim[i] - b.dim[i] - } - c.vval = fdiv(a.vval, b.vval) -} - -func xpn(c, a *Node, b int) { - var i int - - *c = one - if b < 0 { - b = -b - for i = 0; i < b; i++ { - div(c, c, a) - } - } else { - for i = 0; i < b; i++ { - mul(c, c, a) - } - } -} - -func specialcase(c, a, b *Node) bool { - var i int - var d, d1, d2 int8 - - d1 = 0 - d2 = 0 - for i = 1; i < Ndim; i++ { - d = a.dim[i] - if d != 0 { - if d != 1 || d1 != 0 { - return false - } - d1 = int8(i) - } - d = b.dim[i] - if d != 0 { - if d != 1 || d2 != 0 { - return false - } - d2 = int8(i) - } - } - if d1 == 0 || d2 == 0 { - return false - } - - if fund[d1].name == "°C" && fund[d2].name == "°F" && - b.vval == 1 { - for ll := 0; ll < len(c.dim); ll++ { - c.dim[ll] = b.dim[ll] - } - c.vval = a.vval*9./5. + 32. - return true - } - - if fund[d1].name == "°F" && fund[d2].name == "°C" && - b.vval == 1 { - for ll := 0; ll < len(c.dim); ll++ { - c.dim[ll] = b.dim[ll] - } - c.vval = (a.vval - 32.) * 5. / 9. - return true - } - return false -} - -func printdim(str string, d, n int) string { - var v *Var - - if n != 0 { - v = fund[d] - if v != nil { - str += fmt.Sprintf("%v", v.name) - } else { - str += fmt.Sprintf("[%d]", d) - } - switch n { - case 1: - break - case 2: - str += "²" - case 3: - str += "³" - default: - str += fmt.Sprintf("^%d", n) - } - } - return str -} - -func (n Node) String() string { - var str string - var f, i, d int - - str = fmt.Sprintf("%.7e ", n.vval) - - f = 0 - for i = 1; i < Ndim; i++ { - d = int(n.dim[i]) - if d > 0 { - str = printdim(str, i, d) - } else if d < 0 { - f = 1 - } - } - - if f != 0 { - str += " /" - for i = 1; i < Ndim; i++ { - d = int(n.dim[i]) - if d < 0 { - str = printdim(str, i, -d) - } - } - } - - return str -} - -func (v *Var) String() string { - var str string - str = fmt.Sprintf("%v %v", v.name, v.node) - return str -} - -func readline() bool { - s, err := fi.ReadString('\n') - if err != nil { - return true - } - line = s - linep = 0 - return false -} - -func getrune() rune { - var c rune - var n int - - if linep >= len(line) { - return 0 - } - c, n = utf8.DecodeRuneInString(line[linep:len(line)]) - linep += n - if c == '\n' { - c = 0 - } - return c -} - -var symmap = make(map[string]*Var) // symbol table - -func lookup(f int) *Var { - var p float64 - var w *Var - - v, ok := symmap[sym] - if ok { - return v - } - if f != 0 { - return nil - } - v = new(Var) - v.name = sym - symmap[sym] = v - - p = 1 - for { - p = fmul(p, pname()) - if p == 0 { - break - } - w = lookup(1) - if w != nil { - v.node = w.node - v.node.vval = fmul(v.node.vval, p) - break - } - } - return v -} - -type Prefix struct { - vval float64 - name string -} - -var prefix = []Prefix{ // prefix table - {1e-24, "yocto"}, - {1e-21, "zepto"}, - {1e-18, "atto"}, - {1e-15, "femto"}, - {1e-12, "pico"}, - {1e-9, "nano"}, - {1e-6, "micro"}, - {1e-6, "μ"}, - {1e-3, "milli"}, - {1e-2, "centi"}, - {1e-1, "deci"}, - {1e1, "deka"}, - {1e2, "hecta"}, - {1e2, "hecto"}, - {1e3, "kilo"}, - {1e6, "mega"}, - {1e6, "meg"}, - {1e9, "giga"}, - {1e12, "tera"}, - {1e15, "peta"}, - {1e18, "exa"}, - {1e21, "zetta"}, - {1e24, "yotta"}, -} - -func pname() float64 { - var i, j, n int - var s string - - /* - * rip off normal prefixs - */ - n = len(sym) - for i = 0; i < len(prefix); i++ { - s = prefix[i].name - j = len(s) - if j < n && sym[0:j] == s { - sym = sym[j:n] - return prefix[i].vval - } - } - - /* - * rip off 's' suffixes - */ - if n > 2 && sym[n-1] == 's' { - sym = sym[0 : n-1] - return 1 - } - - return 0 -} - -// careful multiplication -// exponents (log) are checked before multiply -func fmul(a, b float64) float64 { - var l float64 - - if b <= 0 { - if b == 0 { - return 0 - } - l = math.Log(-b) - } else { - l = math.Log(b) - } - - if a <= 0 { - if a == 0 { - return 0 - } - l += math.Log(-a) - } else { - l += math.Log(a) - } - - if l > Maxe { - Error("overflow in multiply") - return 1 - } - if l < -Maxe { - Error("underflow in multiply") - return 0 - } - return a * b -} - -// careful division -// exponents (log) are checked before divide -func fdiv(a, b float64) float64 { - var l float64 - - if b <= 0 { - if b == 0 { - Errorf("division by zero: %v %v", a, b) - return 1 - } - l = math.Log(-b) - } else { - l = math.Log(b) - } - - if a <= 0 { - if a == 0 { - return 0 - } - l -= math.Log(-a) - } else { - l -= math.Log(a) - } - - if l < -Maxe { - Error("overflow in divide") - return 1 - } - if l > Maxe { - Error("underflow in divide") - return 0 - } - return a / b -} - -func fadd(a, b float64) float64 { - return a + b -} diff --git a/src/cmd/yacc/yacc.go b/src/cmd/yacc/yacc.go index 76b3aeac5..c53dc3b74 100644 --- a/src/cmd/yacc/yacc.go +++ b/src/cmd/yacc/yacc.go @@ -357,7 +357,7 @@ func main() { func setup() { var j, ty int - stderr = bufio.NewWriter(os.NewFile(2, "stderr")) + stderr = bufio.NewWriter(os.Stderr) foutput = nil flag.Parse() @@ -555,17 +555,18 @@ outer: // process a rule rlines[nprod] = lineno + ruleline := lineno if t == '|' { curprod[mem] = prdptr[nprod-1][0] mem++ } else if t == IDENTCOLON { curprod[mem] = chfind(1, tokname) if curprod[mem] < NTBASE { - errorf("token illegal on LHS of grammar rule") + lerrorf(ruleline, "token illegal on LHS of grammar rule") } mem++ } else { - errorf("illegal rule: missing semicolon or | ?") + lerrorf(ruleline, "illegal rule: missing semicolon or | ?") } // read rule body @@ -586,11 +587,11 @@ outer: } if t == PREC { if gettok() != IDENTIFIER { - errorf("illegal %%prec syntax") + lerrorf(ruleline, "illegal %%prec syntax") } j = chfind(2, tokname) if j >= NTBASE { - errorf("nonterminal " + nontrst[j-NTBASE].name + " illegal after %%prec") + lerrorf(ruleline, "nonterminal "+nontrst[j-NTBASE].name+" illegal after %%prec") } levprd[nprod] = toklev[j] t = gettok() @@ -646,7 +647,7 @@ outer: // no explicit action, LHS has value tempty := curprod[1] if tempty < 0 { - errorf("must return a value, since LHS has a type") + lerrorf(ruleline, "must return a value, since LHS has a type") } if tempty >= NTBASE { tempty = nontrst[tempty-NTBASE].value @@ -654,7 +655,7 @@ outer: tempty = TYPE(toklev[tempty]) } if tempty != nontrst[curprod[0]-NTBASE].value { - errorf("default action causes potential type clash") + lerrorf(ruleline, "default action causes potential type clash") } fmt.Fprintf(fcode, "\n\tcase %v:", nprod) fmt.Fprintf(fcode, "\n\t\t%sVAL.%v = %sS[%spt-0].%v", @@ -1130,7 +1131,9 @@ func emitcode(code []rune, lineno int) { writecode(line) if !fmtImported && isPackageClause(line) { fmt.Fprintln(ftable, `import __yyfmt__ "fmt"`) - fmt.Fprintf(ftable, "//line %v:%v\n\t\t", infile, lineno+i) + if !lflag { + fmt.Fprintf(ftable, "//line %v:%v\n\t\t", infile, lineno+i) + } fmtImported = true } } @@ -2193,8 +2196,10 @@ nextk: func output() { var c, u, v int - fmt.Fprintf(ftable, "\n//line yacctab:1\n") - fmt.Fprintf(ftable, "var %sExca = []int{\n", prefix) + if !lflag { + fmt.Fprintf(ftable, "\n//line yacctab:1") + } + fmt.Fprintf(ftable, "\nvar %sExca = []int{\n", prefix) noset := mkset() @@ -2963,7 +2968,9 @@ func others() { } // copy yaccpar - fmt.Fprintf(ftable, "\n//line yaccpar:1\n") + if !lflag { + fmt.Fprintf(ftable, "\n//line yaccpar:1\n") + } parts := strings.SplitN(yaccpar, prefix+"run()", 2) fmt.Fprintf(ftable, "%v", parts[0]) @@ -3187,7 +3194,7 @@ func create(s string) *bufio.Writer { // // write out error comment // -func errorf(s string, v ...interface{}) { +func lerrorf(lineno int, s string, v ...interface{}) { nerrors++ fmt.Fprintf(stderr, s, v...) fmt.Fprintf(stderr, ": %v:%v\n", infile, lineno) @@ -3197,6 +3204,10 @@ func errorf(s string, v ...interface{}) { } } +func errorf(s string, v ...interface{}) { + lerrorf(lineno, s, v...) +} + func exit(status int) { if ftable != nil { ftable.Flush() @@ -3275,7 +3286,7 @@ out: c = $$Tok2[1] /* unknown char */ } if $$Debug >= 3 { - __yyfmt__.Printf("lex %U %s\n", uint(char), $$Tokname(c)) + __yyfmt__.Printf("lex %s(%d)\n", $$Tokname(c), uint(char)) } return c } @@ -3372,7 +3383,7 @@ $$default: Nerrs++ if $$Debug >= 1 { __yyfmt__.Printf("%s", $$Statname($$state)) - __yyfmt__.Printf("saw %s\n", $$Tokname($$char)) + __yyfmt__.Printf(" saw %s\n", $$Tokname($$char)) } fallthrough |